text:
"""
Ready made and base classes for regularization.

All classes are derived from :class:`~fatiando.inversion.base.Objective` and
should be used by summing them to a :class:`~fatiando.inversion.base.Misfit`
derivative.

The regularization parameter is set by multiplying the regularization instance
by a scalar, e.g., ``solver = misfit + 0.1*regularization``.

See :class:`fatiando.gravmag.eqlayer.EQLGravity` for an example.

**List of classes**

* :class:`~fatiando.inversion.regularization.Damping`: Damping regularization
  (or 0th order Tikhonov regularization)
* :class:`~fatiando.inversion.regularization.Smoothness`: Generic smoothness
  regularization (or 1st order Tikhonov regularization). Requires a finite
  difference matrix to specify the parameter derivatives to minimize.
* :class:`~fatiando.inversion.regularization.Smoothness1D`: Smoothness for 1D
  problems. Automatically builds a finite difference matrix based on the number
  of parameters
* :class:`~fatiando.inversion.regularization.Smoothness2D`: Smoothness for 2D
  grid based problems. Automatically builds a finite difference matrix of
  derivatives in the two spacial dimensions based on the shape of the parameter
  grid
* :class:`~fatiando.inversion.regularization.TotalVariation`: Generic total
  variation regularization (enforces sharpness of the solution). Requires a
  finite difference matrix to specify the parameter derivatives.
* :class:`~fatiando.inversion.regularization.TotalVariation1D`: Total variation
  for 1D problems. Similar to Smoothness1D
* :class:`~fatiando.inversion.regularization.TotalVariation2D`: Total variation
  for 2D grid based problems. Similar to Smoothness2D

**Regularization parameter estimation**

Bellow are classes that estimate an optimal value for the regularization
parameter. They work exactly like an Objective function, i.e., run the
inversion by calling their `fit()` method and accessing
the estimates by `p_`, `estimate_`, `residuals()` and `predicted()`.

* :class:`~fatiando.inversion.regularization.LCurve`: Use an L-curve criterion.
  Runs the inversion using several regularization parameters. The best value
  is the one that falls on the corner of the log-log plot of the data
  misfit vs regularizing function. Only works for a single regularization.

----

"""
from __future__ import division
import multiprocessing

import numpy
import scipy.sparse

from .base import Objective
from ..utils import safe_dot
from ..vis import mpl

class Damping(Objective):

    r"""
    Damping (0th order Tikhonov) regularization.

    Imposes the minimum norm of the parameter vector.

    The regularizing function if of the form

    .. math::

        \theta^{NM}(\bar{p}) = \bar{p}^T\bar{p}

    Its gradient and Hessian matrices are, respectively,

    .. math::

        \bar{\nabla}\theta^{NM}(\bar{p}) = 2\bar{\bar{I}}\bar{p}

    .. math::

        \bar{\bar{\nabla}}\theta^{NM}(\bar{p}) = 2\bar{\bar{I}}

    Parameters:

    * nparams : int
        The number of parameter

    Examples:

    >>> import numpy
    >>> damp = Damping(3)
    >>> p = numpy.array([0, 0, 0])
    >>> damp.value(p)
    0.0
    >>> damp.hessian(p).todense()
    matrix([[ 2.,  0.,  0.],
            [ 0.,  2.,  0.],
            [ 0.,  0.,  2.]])
    >>> damp.gradient(p)
    array([ 0.,  0.,  0.])
    >>> p = numpy.array([1, 0, 0])
    >>> damp.value(p)
    1.0
    >>> damp.hessian(p).todense()
    matrix([[ 2.,  0.,  0.],
            [ 0.,  2.,  0.],
            [ 0.,  0.,  2.]])
    >>> damp.gradient(p)
    array([ 2.,  0.,  0.])

    """

    def __init__(self, nparams):
        super(Damping, self).__init__(nparams, islinear=True)

    def _get_hessian(self, p):
        """
        Calculate the Hessian matrix.

        Parameters:

        * p : 1d-array
            The parameter vector

        Returns:

        * hessian : 2d-array
            The Hessian

        """
        # This is cheap so there is no need to cache it
        return 2 * scipy.sparse.identity(self.nparams).tocsr()

    def _get_gradient(self, p):
        """
        Calculate the gradient vector.

        Parameters:

        * p : 1d-array or ``'null'``
            The parameter vector. If ``'null'``, will return 0.

        Returns:

        * gradient : 1d-array
            The gradient

        """
        if p is 'null':
            grad = 0
        else:
            grad = 2. * p
        return grad

    def _get_value(self, p):
        """
        Calculate the value of this function.

        Parameters:

        * p : 1d-array
            The parameter vector

        Returns:

        * value : float
            The value of this function evaluated at *p*

        """
        return numpy.linalg.norm(p) ** 2

class Smoothness(Objective):

    r"""
    Smoothness (1st order Tikhonov) regularization.

    Imposes that adjacent parameters have values close to each other.

    The regularizing function if of the form

    .. math::

        \theta^{SV}(\bar{p}) =
        \bar{p}^T \bar{\bar{R}}^T \bar{\bar{R}}\bar{p}

    Its gradient and Hessian matrices are, respectively,

    .. math::

        \bar{\nabla}\theta^{SV}(\bar{p}) =
        2\bar{\bar{R}}^T \bar{\bar{R}}\bar{p}

    .. math::

        \bar{\bar{\nabla}}\theta^{SV}(\bar{p}) =
        2\bar{\bar{R}}^T \bar{\bar{R}}

    in which matrix :math:`\bar{\bar{R}}` is a finite difference matrix. It
    represents the differences between one parameter and another and is what
    indicates what *adjacent* means.

    Parameters:

    * fdmat : 2d-array or sparse matrix
        The finite difference matrix

    Examples:

    >>> import numpy as np
    >>> fd = np.array([[1, -1, 0],
    ...                [0, 1, -1]])
    >>> s = Smoothness(fd)
    >>> p = np.array([0, 0, 0])
    >>> s.value(p)
    0.0
    >>> s.gradient(p)
    array([0, 0, 0])
    >>> s.hessian(p)
    array([[ 2, -2,  0],
           [-2,  4, -2],
           [ 0, -2,  2]])
    >>> p = np.array([1, 0, 1])
    >>> s.value(p)
    2.0
    >>> s.gradient(p)
    array([ 2, -4,  2])
    >>> s.hessian(p)
    array([[ 2, -2,  0],
           [-2,  4, -2],
           [ 0, -2,  2]])

    """

    def __init__(self, fdmat):
        super(Smoothness, self).__init__(fdmat.shape[1], islinear=True)
        self._cache = {}
        self._cache['hessian'] = {'hash': '',
                                  'array': 2 * safe_dot(fdmat.T, fdmat)}

    def _get_hessian(self, p):
        """
        Calculate the Hessian matrix.

        Parameters:

        * p : 1d-array
            The parameter vector

        Returns:

        * hessian : 2d-array
            The Hessian

        """
        return self._cache['hessian']['array']

    def _get_gradient(self, p):
        """
        Calculate the gradient vector.

        Parameters:

        * p : 1d-array or ``'null'``
            The parameter vector. If ``'null'``, will return 0.

        Returns:

        * gradient : 1d-array
            The gradient

        """
        if p is 'null':
            grad = 0
        else:
            grad = safe_dot(self.hessian(p), p)
        return grad

    def _get_value(self, p):
        """
        Calculate the value of this function.

        Parameters:

        * p : 1d-array
            The parameter vector

        Returns:

        * value : float
            The value of this function evaluated at *p*

        """
        # Need to divide by 2 because the hessian is 2*R.T*R
        return numpy.sum(p * safe_dot(self.hessian(p), p)) / 2.

class Smoothness1D(Smoothness):

    """
    Smoothness regularization for 1D problems.

    Extends the generic :class:`~fatiando.inversion.regularization.Smoothness`
    class by automatically building the finite difference matrix.

    Parameters:

    * npoints : int
        The number of parameters

    Examples:

    >>> import numpy as np
    >>> s = Smoothness1D(3)
    >>> p = np.array([0, 0, 0])
    >>> s.value(p)
    0.0
    >>> s.gradient(p)
    array([0, 0, 0])
    >>> s.hessian(p).todense()
    matrix([[ 2, -2,  0],
            [-2,  4, -2],
            [ 0, -2,  2]])
    >>> p = np.array([1, 0, 1])
    >>> s.value(p)
    2.0
    >>> s.gradient(p)
    array([ 2, -4,  2])
    >>> s.hessian(p).todense()
    matrix([[ 2, -2,  0],
            [-2,  4, -2],
            [ 0, -2,  2]])

    """

    def __init__(self, npoints):
        super(Smoothness1D, self).__init__(fd1d(npoints))

class Smoothness2D(Smoothness):

    """
    Smoothness regularization for 2D problems.

    Extends the generic :class:`~fatiando.inversion.regularization.Smoothness`
    class by automatically building the finite difference matrix.

    Parameters:

    * shape : tuple = (ny, nx)
        The shape of the parameter grid. Number of parameters in the y and x
        (or z and x, time and offset, etc) dimensions.

    Examples:

    >>> import numpy as np
    >>> s = Smoothness2D((2, 2))
    >>> p = np.array([[0, 0],
    ...               [0, 0]]).ravel()
    >>> s.value(p)
    0.0
    >>> s.gradient(p)
    array([0, 0, 0, 0])
    >>> s.hessian(p).todense()
    matrix([[ 4, -2, -2,  0],
            [-2,  4,  0, -2],
            [-2,  0,  4, -2],
            [ 0, -2, -2,  4]])
    >>> p = np.array([[1, 0],
    ...               [2, 3]]).ravel()
    >>> s.value(p)
    12.0
    >>> s.gradient(p)
    array([ 0, -8,  0,  8])
    >>> s.hessian(p).todense()
    matrix([[ 4, -2, -2,  0],
            [-2,  4,  0, -2],
            [-2,  0,  4, -2],
            [ 0, -2, -2,  4]])

    """

    def __init__(self, shape):
        super(Smoothness2D, self).__init__(fd2d(shape))

class TotalVariation(Objective):

    r"""
    Total variation regularization.

    Imposes that adjacent parameters have a few sharp transitions.

    The regularizing function if of the form

    .. math::

        \theta^{VT}(\bar{p}) = \sum\limits_{k=1}^L |v_k|

    where vector :math:`\bar{v} = \bar{\bar{R}}\bar{p}`. See
    :class:`~fatiando.inversion.regularization.Smoothness` for the definition
    of the :math:`\bar{\bar{R}}` matrix.

    This functions is not differentiable at the null vector, so the following
    form is used to calculate the gradient and Hessian

    .. math::

        \theta^{VT}(\bar{p}) \approx \theta^{VT}_\beta(\bar{p}) =
        \sum\limits_{k=1}^L \sqrt{v_k^2 + \beta}

    Its gradient and Hessian matrices are, respectively,

    .. math::

        \bar{\nabla}\theta^{VT}_\beta(\bar{p}) =
        \bar{\bar{R}}^T \bar{q}(\bar{p})

    .. math::

        \bar{\bar{\nabla}}\theta^{VT}_\beta(\bar{p}) =
        \bar{\bar{R}}^T \bar{\bar{Q}}(\bar{p})\bar{\bar{R}}

    and

    .. math::

        \bar{q}(\bar{p}) =
        \begin{bmatrix}
            \dfrac{v_1}{\sqrt{v_1^2 + \beta}} \\
            \dfrac{v_2}{\sqrt{v_2^2 + \beta}} \\
            \vdots \\ \dfrac{v_L}{\sqrt{v_L^2 + \beta}}
        \end{bmatrix}

    .. math::

        \bar{\bar{Q}}(\bar{p}) =
        \begin{bmatrix}
            \dfrac{\beta}{(v_1^2 + \beta)^{\frac{3}{2}}} & 0 & \ldots & 0 \\
            0 & \dfrac{\beta}{(v_2^2 + \beta)^{\frac{3}{2}}} & \ldots & 0 \\
            \vdots & \vdots & \ddots & \vdots \\
            0 & 0 & \ldots & \dfrac{\beta}{(v_L^2 + \beta)^{\frac{3}{2}}}
        \end{bmatrix}

    Parameters:

    * beta : float
        The beta parameter for the differentiable approximation. The larger it
        is, the closer total variation is to
        :class:`~fatiando.inversion.regularization.Smoothness`. Should be a
        small, positive value.
    * fdmat : 2d-array or sparse matrix
        The finite difference matrix

    """

    def __init__(self, beta, fdmat):
        if beta <= 0:
            raise ValueError("Invalid beta=%g. Must be > 0" % (beta))
        super(TotalVariation, self).__init__(
            nparams=fdmat.shape[1], islinear=False)
        self.beta = beta
        self._fdmat = fdmat

    def _get_value(self, p):
        """
        Calculate the value of this function.

        Parameters:

        * p : 1d-array
            The parameter vector

        Returns:

        * value : float
            The value of this function evaluated at *p*

        """
        return numpy.linalg.norm(safe_dot(self._fdmat, p), 1)

    def _get_hessian(self, p):
        """
        Calculate the Hessian matrix.

        Parameters:

        * p : 1d-array
            The parameter vector

        Returns:

        * hessian : 2d-array
            The Hessian

        """
        derivs = safe_dot(self._fdmat, p)
        q = self.beta / ((derivs ** 2 + self.beta) ** 1.5)
        q_matrix = scipy.sparse.diags(q, 0).tocsr()
        return safe_dot(self._fdmat.T, q_matrix * self._fdmat)

    def _get_gradient(self, p):
        """
        Calculate the gradient vector.

        Parameters:

        * p : 1d-array
            The parameter vector.

        Returns:

        * gradient : 1d-array
            The gradient

        """
        derivs = safe_dot(self._fdmat, p)
        q = derivs / numpy.sqrt(derivs ** 2 + self.beta)
        grad = safe_dot(self._fdmat.T, q)
        if len(grad.shape) > 1:
            grad = numpy.array(grad.T).ravel()
        return grad

class TotalVariation1D(TotalVariation):

    """
    Total variation regularization for 1D problems.

    Extends the generic
    :class:`~fatiando.inversion.regularization.TotalVariation`
    class by automatically building the finite difference matrix.

    Parameters:

    * beta : float
        The beta parameter for the differentiable approximation. The larger it
        is, the closer total variation is to
        :class:`~fatiando.inversion.regularization.Smoothness`. Should be a
        small, positive value.
    * npoints : int
        The number of parameters

    """

    def __init__(self, beta, npoints):
        super(TotalVariation1D, self).__init__(beta, fd1d(npoints))

class TotalVariation2D(TotalVariation):

    """
    Total variation regularization for 2D problems.

    Extends the generic
    :class:`~fatiando.inversion.regularization.TotalVariation`
    class by automatically building the finite difference matrix.

    Parameters:

    * beta : float
        The beta parameter for the differentiable approximation. The larger it
        is, the closer total variation is to
        :class:`~fatiando.inversion.regularization.Smoothness`. Should be a
        small, positive value.
    * shape : tuple = (ny, nx)
        The shape of the parameter grid. Number of parameters in the y and x
        (or z and x, time and offset, etc) dimensions.

    """

    def __init__(self, beta, shape):
        super(TotalVariation2D, self).__init__(beta, fd2d(shape))

def fd1d(size):
    """
    Produce a 1D finite difference matrix.

    Parameters:

    * size : int
        The number of points

    Returns:

    * fd : sparse CSR matrix
        The finite difference matrix

    Examples:

    >>> fd1d(2).todense()
    matrix([[ 1, -1]])
    >>> fd1d(3).todense()
    matrix([[ 1, -1,  0],
            [ 0,  1, -1]])
    >>> fd1d(4).todense()
    matrix([[ 1, -1,  0,  0],
            [ 0,  1, -1,  0],
            [ 0,  0,  1, -1]])

    """
    i = range(size - 1) + range(size - 1)
    j = range(size - 1) + range(1, size)
    v = [1] * (size - 1) + [-1] * (size - 1)
    return scipy.sparse.coo_matrix((v, (i, j)), (size - 1, size)).tocsr()

def fd2d(shape):
    """
    Produce a 2D finite difference matrix.

    Parameters:

    * shape : tuple = (ny, nx)
        The shape of the parameter grid. Number of parameters in the y and x
        (or z and x, time and offset, etc) dimensions.

    Returns:

    * fd : sparse CSR matrix
        The finite difference matrix

    Examples:

    >>> fd2d((2, 2)).todense()
    matrix([[ 1, -1,  0,  0],
            [ 0,  0,  1, -1],
            [ 1,  0, -1,  0],
            [ 0,  1,  0, -1]])
    >>> fd2d((2, 3)).todense()
    matrix([[ 1, -1,  0,  0,  0,  0],
            [ 0,  1, -1,  0,  0,  0],
            [ 0,  0,  0,  1, -1,  0],
            [ 0,  0,  0,  0,  1, -1],
            [ 1,  0,  0, -1,  0,  0],
            [ 0,  1,  0,  0, -1,  0],
            [ 0,  0,  1,  0,  0, -1]])

    """
    ny, nx = shape
    nderivs = (nx - 1) * ny + (ny - 1) * nx
    I, J, V = [], [], []
    deriv = 0
    param = 0
    for i in xrange(ny):
        for j in xrange(nx - 1):
            I.extend([deriv, deriv])
            J.extend([param, param + 1])
            V.extend([1, -1])
            deriv += 1
            param += 1
        param += 1
    param = 0
    for i in xrange(ny - 1):
        for j in xrange(nx):
            I.extend([deriv, deriv])
            J.extend([param, param + nx])
            V.extend([1, -1])
            deriv += 1
            param += 1
    return scipy.sparse.coo_matrix((V, (I, J)), (nderivs, nx * ny)).tocsr()

class LCurve(object):

    """
    Use the L-curve criterion to estimate the regularization parameter.

    Runs the inversion using several specified regularization parameters.
    The best value is the one that falls on the corner of the log-log plot of
    the data misfit vs regularizing function.
    This point is automatically found using the triangle method of
    Castellanos et al. (2002).

    This class behaves as :class:`~fatiando.inversion.base.Misfit`.
    To use it, simply call ``fit`` and optionally ``config``.
    The estimate will be stored in ``estimate_`` and ``p_``.
    The estimated regularization parameter will be stored in ``regul_param_``.

    Parameters:

    * datamisfit : :class:`~fatiando.inversion.base.Misfit`
        The data misfit instance for the inverse problem. Can be a sum of other
        misfits.
    * regul : A class from :mod:`fatiando.inversion.regularization`
        The regularizing function.
    * regul_params : list
        The values of the regularization parameter that will be tested.
    * loglog : True or False
        If True, will use a log-log scale for the L-curve (recommended).
    * jobs : None or int
        If not None, will use *jobs* processes to calculate the L-curve.

    References:

    Castellanos, J. L., S. Gomez, and V. Guerra (2002), The triangle method for
    finding the corner of the L-curve, Applied Numerical Mathematics, 43(4),
    359-373, doi:10.1016/S0168-9274(01)00179-9.

    Examples:

    We'll use the L-curve to estimate the best regularization parameter for a
    smooth inversion using :mod:`fatiando.seismic.srtomo`.

    First, we'll setup some synthetic data:

    >>> import numpy
    >>> from fatiando.mesher import SquareMesh
    >>> from fatiando.seismic import ttime2d, srtomo
    >>> from fatiando.inversion.regularization import Smoothness2D, LCurve
    >>> from fatiando import utils
    >>> area = (0, 2, 0, 2)
    >>> shape = (10, 10)
    >>> model = SquareMesh(area, shape)
    >>> vp = 4*numpy.ones(shape)
    >>> vp[3:7,3:7] = 10
    >>> vp
    array([[  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  10.,  10.,  10.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  10.,  10.,  10.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  10.,  10.,  10.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  10.,  10.,  10.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.]])
    >>> model.addprop('vp', vp.ravel())
    >>> src_loc = utils.random_points(area, 30, seed=0)
    >>> rec_loc = utils.circular_points(area, 20, random=True, seed=0)
    >>> srcs, recs = utils.connect_points(src_loc, rec_loc)
    >>> tts = ttime2d.straight(model, 'vp', srcs, recs)
    >>> tts = utils.contaminate(tts, 0.01, percent=True, seed=0)

    Now we can setup a tomography by creating the necessary data misfit
    (`SRTomo`) and regularization (`Smoothness2D`) objects:

    >>> mesh = SquareMesh(area, shape)
    >>> datamisfit = srtomo.SRTomo(tts, srcs, recs, mesh)
    >>> regul = Smoothness2D(mesh.shape)

    The tomography solver will be the `LCurve` solver. It works by calling
    `fit()` and accessing `estimate_`, exactly like any other solver:

    >>> tomo = LCurve(datamisfit, regul, [10**i for i in range(-10, -2, 1)])
    >>> e = tomo.fit().estimate_
    >>> print numpy.array_repr(e.reshape(shape), precision=0)
    array([[  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  11.,   9.,  11.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  10.,  11.,  10.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  10.,  10.,  10.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  11.,  10.,  11.,   9.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.]])

    The estimated regularization parameter is stored in `regul_param_`:

    >>> tomo.regul_param_
    1e-05

    The `LCurve` object also exposes the `residuals()` and `predicted()`
    methods of the data misfit class:

    >>> residuals = tomo.residuals()
    >>> print '%.4f %.4f' % (residuals.mean(), residuals.std())
    -0.0000 0.0047

    `LCurve` also has a `config` method to configure the optimization process
    for non-linear problems, for example:

    >>> initial = 1./4.*numpy.ones(mesh.size)
    >>> tomo = LCurve(datamisfit, regul, [10**i for i in range(-10, -2, 1)])
    >>> e = tomo.config('levmarq', initial=initial).fit().estimate_
    >>> tomo.regul_param_
    1e-05
    >>> print numpy.array_repr(e.reshape(shape), precision=0)
    array([[  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  11.,   9.,  11.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  10.,  11.,  10.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  10.,  10.,  10.,  10.,   4.,   4.,   4.],
           [  4.,   4.,   4.,  11.,  10.,  11.,   9.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.],
           [  4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.,   4.]])

    """

    def __init__(self, datamisfit, regul, regul_params, loglog=True,
                 jobs=None):
        self.regul_params = regul_params
        self.datamisfit = datamisfit
        self.regul = regul
        self.regul_param_ = None
        self.objectives = None
        self.corner_ = None
        self.estimate_ = None
        self.p_ = None
        self.dnorm = None
        self.mnorm = None
        self.fit_method = None
        self.fit_args = None
        self.jobs = jobs
        self.loglog = loglog

    def fit(self):
        """
        Solve for the parameter vector and optimum regularization parameter.

        Combines the data-misfit and regularization solvers using the range of
        regularization parameters provided and calls ``fit`` and ``config`` on
        each.

        The ``p_`` and ``estimate_`` attributes correspond to the combination
        that falls in the corner of the L-curve.

        The regularization parameter for this corner point if stored in the
        ``regul_param_`` attribute.

        Returns:

        * self

        """
        if self.datamisfit.islinear:
            self.datamisfit.jacobian('null')
        solvers = [
            self.datamisfit + mu * self.regul for mu in self.regul_params]
        if self.fit_method is not None:
            for solver in solvers:
                solver.config(self.fit_method, **self.fit_args)
        if self.jobs is None:
            results = [_run_lcurve(s) for s in solvers]
        else:
            pool = multiprocessing.Pool(self.jobs)
            results = pool.map(_run_lcurve, solvers)
            pool.close()
            pool.join()
        self.objectives = results
        self.dnorm = numpy.array(
            [self.datamisfit.value(s.p_) for s in results])
        self.mnorm = numpy.array([self.regul.value(s.p_) for s in results])
        self.select_corner()
        return self

    def _scale_curve(self):
        """
        Puts the data-misfit and regularizing function values in the range
        [-10, 10].
        """
        if self.loglog:
            x, y = numpy.log(self.dnorm), numpy.log(self.mnorm)
        else:
            x, y = self.dnorm, self.mnorm

        def scale(a):
            vmin, vmax = a.min(), a.max()
            l, u = -10, 10
            return (((u - l) / (vmax - vmin)) *
                    (a - (u * vmin - l * vmax) / (u - l)))
        return scale(x), scale(y)

    def select_corner(self):
        """
        Selects the corner value of the L-curve and sets the estimate to it.

        Uses the Triangle method of Castellanos et al. (2002).

        The index of the corner value is stored in the ``corner_`` attribute.

        Returns:

        * self

        """
        x, y = self._scale_curve()
        n = len(self.regul_params)
        corner = n - 1

        def dist(p1, p2):
            "Return the geometric distance between p1 and p2"
            return numpy.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)

        cte = 7. * numpy.pi / 8.
        angmin = None
        c = [x[-1], y[-1]]
        for k in xrange(0, n - 2):
            b = [x[k], y[k]]
            for j in xrange(k + 1, n - 1):
                a = [x[j], y[j]]
                ab = dist(a, b)
                ac = dist(a, c)
                bc = dist(b, c)
                cosa = (ab ** 2 + ac ** 2 - bc ** 2) / (2. * ab * ac)
                ang = numpy.arccos(cosa)
                area = 0.5 * ((b[0] - a[0]) * (a[1] - c[1]) -
                              (a[0] - c[0]) * (b[1] - a[1]))
                # area is > 0 because in the paper C is index 0
                if area > 0 and (ang < cte and
                                 (angmin is None or ang < angmin)):
                    corner = j
                    angmin = ang
        self.corner_ = corner
        self.regul_param_ = self.regul_params[corner]
        self.p_ = self.objectives[corner].p_
        self.estimate_ = self.objectives[corner].estimate_
        return self

    def config(self, method, **kwargs):
        """
        Configure the optimization method and its parameters.

        This sets the method used by
        :meth:`~fatiando.inversion.regularization.LCurve.fit` and the keyword
        arguments that are passed to it.

        Parameters:

        * method : string
            The optimization method. One of: ``'linear'``, ``'newton'``,
            ``'levmarq'``, ``'steepest'``, ``'acor'``

        Other keyword arguments that can be passed are the ones allowed by each
        method.

        See :meth:`fatiando.inversion.base.Misfit.config`.

        Returns:

        * self

        """
        self.fit_method = method
        self.fit_args = kwargs
        return self

    def predicted(self, p=None):
        """
        Returns the predicted data for a given parameter vector.

        Uses the solver that falls on the corner of the L-curve.

        Parameters:

        * p : 1d-array or None
            The parameter vector used to calculate the predicted data. If None,
            will use the current estimate stored in ``estimate_``.

        Returns:

        * predicted : 1d-array or list of 1d-arrays
            The predicted data. If this is the sum of 1 or more Misfit
            instances, will return the predicted data from each of the summed
            misfits in the order of the sum.

        """
        return self.objectives[self.corner_].predicted(p)

    def residuals(self, p=None):
        """
        Returns the residuals vector (observed - predicted data).

        Uses the solver that falls on the corner of the L-curve.

        Parameters:

        * p : 1d-array or None
            The parameter vector used to calculate the residuals. If None, will
            use the current estimate stored in ``estimate_``.

        Returns:

        * residuals : 1d-array or list of 1d-arrays
            The residual vector. If this is the sum of 1 or more Misfit
            instances, will return the residual vector from each of the summed
            misfits in the order of the sum.

        """
        return self.objectives[self.corner_].residuals(p)

    def plot_lcurve(self, guides=True):
        """
        Make a plot of the data-misfit x regularization values.

        The estimated corner value is shown as a blue triangle.

        Parameters:

        * guides : True or False
            Plot vertical and horizontal lines across the corner value.

        """
        x, y = self.dnorm, self.mnorm
        if self.loglog:
            mpl.loglog(x, y, '.-k')
        else:
            mpl.plot(x, y, '.-k')
        if guides:
            ax = mpl.gca()
            vmin, vmax = ax.get_ybound()
            mpl.vlines(x[self.corner_], vmin, vmax)
            vmin, vmax = ax.get_xbound()
            mpl.hlines(y[self.corner_], vmin, vmax)
        mpl.plot(x[self.corner_], y[self.corner_], '^b', markersize=10)
        mpl.xlabel('Data misfit')
        mpl.ylabel('Regularization')

def _run_lcurve(solver):
    """
    Call ``fit`` on the solver. Needed for multiprocessing.
    """
    result = solver.fit()
    return result


text:
require 'webmock/rspec'
require "mico"

describe Mico::Member do
  before { ENV["MEURIO_TOKEN"] = "racecarsyayas" }

  describe ".all" do
    let(:members_page1){ File.open("spec/support/members_page1.json").read }
    let(:members_page2){ File.open("spec/support/members_page2.json").read }
    let(:members_page3){ File.open("spec/support/members_page3.json").read }
    before{ stub_request(:get, Mico::Member::URL).with(:query => {:token => ENV["MEURIO_TOKEN"]}).to_return(:body => members_page1) }
    before{ stub_request(:get, Mico::Member::URL).with(:query => {:token => ENV["MEURIO_TOKEN"], :page => 2}).to_return(:body => members_page2) }
    before{ stub_request(:get, Mico::Member::URL).with(:query => {:token => ENV["MEURIO_TOKEN"], :by_updated_at => "2011-09-20T16:18:27-03:00"}).to_return(:body => members_page3) }

    it "should return a hash with the first page of members" do
      Mico::Member.all.should be_== JSON.parse(members_page1)
    end

    it "should return a hash with the second page of members" do
      Mico::Member.all({:page => 2}).should be_== JSON.parse(members_page2)
    end

    it "should return a hash of members filtered by updated at attribute" do
      Mico::Member.all({:by_updated_at => "2011-09-20T16:18:27-03:00"}).should be_== JSON.parse(members_page3)
    end
  end
end

redpajama_set_name: RedPajamaGithub


text:
<html>
  <head>
    <title>im a title</title>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>EXPECTED_RUM_LOADER_LOCATION
    <meta name="keywords" content="testing,rum,insertion" >
    <script>
      junk
    </script>
  </head>
  <body>im some body text</body>
</html>


text:
FactoryBot.define do
  factory :flag do
    flagger { build(:user) }
    project_submission
    reason { "It's offensive" }
  end
end


text:
package org.apache.sshd.server.keyprovider;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.security.KeyPair;

import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PEMWriter;

/**
 * TODO Add javadoc
 *
 * @author Apache MINA SSHD Project
 */
public class PEMGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {

    public PEMGeneratorHostKeyProvider() {
    }

    public PEMGeneratorHostKeyProvider(String path) {
        super(path);
    }

    public PEMGeneratorHostKeyProvider(String path, String algorithm) {
        super(path, algorithm);
    }

    public PEMGeneratorHostKeyProvider(String path, String algorithm, int keySize) {
        super(path, algorithm, keySize);
    }

    protected KeyPair doReadKeyPair(InputStream is) throws Exception {
        PEMReader r = new PEMReader(new InputStreamReader(is));
        return (KeyPair) r.readObject();
    }

    protected void doWriteKeyPair(KeyPair kp, OutputStream os) throws Exception {
        PEMWriter w = new PEMWriter(new OutputStreamWriter(os));
        w.writeObject(kp);
    }

}


text:
package website.automate.jwebrobot.context;

import website.automate.jwebrobot.executor.ExecutorOptions;
import website.automate.jwebrobot.loader.ScenarioFile;
import website.automate.waml.io.model.main.Scenario;

import java.io.File;
import java.nio.file.Files;
import java.util.*;

import static java.util.Collections.unmodifiableMap;

public class GlobalExecutionContext {

    private File tempDir = createTempDir();

    private Map<String, Scenario> pathScenarioMap = new HashMap<>();

    private Map<Scenario, File> scenarioFileMap = new HashMap<>();

    private Map<String, Object> memory;

    private List<Scenario> scenarios = new ArrayList<>();

    private ExecutorOptions options;

    private Map<String, Object> exportMemory = new HashMap<>();

    public GlobalExecutionContext(Collection<ScenarioFile> scenarioFiles, ExecutorOptions options,
            Map<String, Object> memory) {
        this.options = options;
        this.memory = unmodifiableMap(memory);
        addScenarios(scenarioFiles);
    }

    private void addScenarios(Collection<ScenarioFile> scenarioFiles){
        for(ScenarioFile scenarioFile : scenarioFiles){
                addScenario(scenarioFile.getFile(), scenarioFile.getScenario());
        }
    }

    private void addScenario(File scenarioFile, Scenario scenario){
        this.scenarioFileMap.put(scenario, scenarioFile);
        this.pathScenarioMap.put(scenario.getPath(), scenario);
        this.scenarios.add(scenario);
    }

    private File createTempDir(){
        try {
            return Files.createTempDirectory("jwebrobot").toFile();
        } catch (Exception e){
            throw new RuntimeException("Can not create temp directory.", e);
        }
    }

    public Scenario getScenarioByPath(String name){
        return pathScenarioMap.get(name);
    }

    public File getFile(Scenario scenario){
        return scenarioFileMap.get(scenario);
    }

    public ExecutorOptions getOptions() {
        return options;
    }

    public List<Scenario> getScenarios() {
        return scenarios;
    }

    public Map<String, Object> getMemory() {
        return memory;
    }

    public File getTempDir() {
        return tempDir;
    }

    public Map<String, Object> getExportMemory() {
        return exportMemory;
    }
}


text:
from __future__ import absolute_import

from rest_framework.response import Response

from sentry.api.bases.project import ProjectEndpoint
from sentry.api.serializers import serialize
from sentry.models import Environment

class ProjectEnvironmentsEndpoint(ProjectEndpoint):
    def get(self, request, project):
        queryset = Environment.objects.filter(
            project_id=project.id,
        ).order_by('name')

        return Response(serialize(list(queryset), request.user))


text:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace CustomModelAnimation
{
    /// <summary>
    /// Describes the position of a single bone at a single point in time.
    /// </summary>
    public class ModelKeyframe
    {
        /// <summary>
        /// Gets the index of the target bone that is animated by this keyframe.
        /// </summary>
        [ContentSerializer]
        public int Bone { get; private set; }

        /// <summary>
        /// Gets the time offset from the start of the animation to this keyframe.
        /// </summary>
        [ContentSerializer]
        public TimeSpan Time { get; private set; }

        /// <summary>
        /// Gets the bone transform for this keyframe.
        /// </summary>
        [ContentSerializer]
        public Matrix Transform { get; private set; }

        /// <summary>
        /// Constructs a new ModelKeyframe object.
        /// </summary>
        public ModelKeyframe(int bone, TimeSpan time, Matrix transform)
        {
            Bone = bone;
            Time = time;
            Transform = transform;
        }

        /// <summary>
        /// Private constructor for use by the XNB deserializer.
        /// </summary>
        private ModelKeyframe()
        {
        }
    }
}


text:
package me.thuanle.Astronomers;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PowerManager;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private class DrawerItemClickListener implements android.widget.AdapterView.OnItemClickListener {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectItem(position);
        }
    }

    protected PowerManager.WakeLock mWakeLock;
    private String[] mMapTitles;
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    private CharSequence mTitle;

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mMapTitles = getResources().getStringArray(R.array.map_array);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        // Set the adapter for the list view
        mDrawerList.setAdapter(new ArrayAdapter<String>(this,
                R.layout.drawer_list_item, mMapTitles));
        // Set the list's click listener
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

        // Keep wake-lock of screen
        final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        this.mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
        this.mWakeLock.acquire();
        // Maximum brightness
        WindowManager.LayoutParams layout = getWindow().getAttributes();
        layout.screenBrightness = 1F;
        getWindow().setAttributes(layout);
    }

    @Override
    protected void onDestroy() {
        this.mWakeLock.release();
        super.onDestroy();
    }

    @Override
    protected void onResume() {
        super.onResume();
        App.mainActivity=this;
    }

    private void selectItem(int position) {
        // update the main content by replacing fragments
        Fragment fragment = new PlanetFragment();
        Bundle args = new Bundle();
        args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
        fragment.setArguments(args);

        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();

        // update selected item and title, then close the drawer
        mDrawerList.setItemChecked(position, true);
        setTitle(mMapTitles[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

    @Override
    public void setTitle(CharSequence title) {
        mTitle = title;
        getActionBar().setTitle(mTitle);
    }

//    private void setupForegroundDispatch(MainActivity activity, NfcAdapter adapter) {
//        final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
//        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//
//        final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
//
//        IntentFilter[] filters = new IntentFilter[1];
//        String[][] techList = new String[][]{};
//
//        // Notice that this is the same filter as in our manifest.
//        filters[0] = new IntentFilter();
//        filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
//        filters[0].addCategory(Intent.CATEGORY_DEFAULT);
//        try {
//            filters[0].addDataType(NFC_MIME);
//        } catch (IntentFilter.MalformedMimeTypeException e) {
//            throw new RuntimeException("Check your mime type.");
//        }
//
//        adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
//    }
//
//    private void stopForegroundDispatch(MainActivity activity, NfcAdapter adapter) {
//        adapter.disableForegroundDispatch(activity);
//    }
}


text:
package com.tom.recipes.handler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.stream.Collectors;

import net.minecraft.block.BlockStone;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.FurnaceRecipes;
import net.minecraft.nbt.NBTTagCompound;

import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.common.crafting.JsonContext;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;

import com.google.gson.JsonObject;

import com.tom.api.recipes.RecipeHelper;
import com.tom.api.recipes.RecipeHelper.ItemStackWCount;
import com.tom.config.Config;
import com.tom.core.CoreInit;
import com.tom.core.TMResource;
import com.tom.core.TMResource.CraftingMaterial;
import com.tom.core.TMResource.Type;
import com.tom.defense.DefenseInit;
import com.tom.factory.FactoryInit;
import com.tom.lib.utils.EmptyEntry;
import com.tom.recipes.AdvancedCraftingRecipes;
import com.tom.util.RecipeData;
import com.tom.util.TomsModUtils;

import com.tom.core.item.ItemBlueprint;

public class MachineCraftingHandler {
	public static final String ROOT = "machine_recipes/";
	public static final String ROOT_UV = ROOT + "uv_light_box";
	public static final String ROOT_LASER = ROOT + "laser_engraver";
	public static final String ROOT_ALLOY_SMELTER = ROOT + "alloy_smelter";
	public static final String ROOT_CRUSHER = ROOT + "crusher";
	public static final String ROOT_BLAST_FURNACE = ROOT + "blast_furnace";
	public static final String ROOT_PLATE_BENDER = ROOT + "plate_bending_machine";
	public static final String ROOT_COKE_OVEN = ROOT + "coke_oven";
	public static final String ROOT_WIRE_MILL = ROOT + "wire_mill";
	public static void loadRecipes() {
		addHammerRecipe(Blocks.OBSIDIAN.getDefaultState(), 2, CraftingMaterial.CRUSHED_OBSIDIAN.getStackNormal());
		addHammerRecipe(Blocks.GRAVEL.getDefaultState(), 1, new ItemStackWRng(new ItemStack(Blocks.SAND)), new ItemStackWRng(new ItemStack(Items.FLINT), 15), new ItemStackWRng(CraftingMaterial.STONE_DUST.getStackNormal(), 10));
		addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.STONE), 2, new ItemStackWRng(new ItemStack(Blocks.GRAVEL)), new ItemStackWRng(CraftingMaterial.STONE_DUST.getStackNormal(), 50));
		addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.ANDESITE), 2, new ItemStackWRng(new ItemStack(Blocks.GRAVEL)), new ItemStackWRng(CraftingMaterial.STONE_DUST.getStackNormal(), 50));
		addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.GRANITE), 2, new ItemStackWRng(new ItemStack(Blocks.GRAVEL)), new ItemStackWRng(CraftingMaterial.STONE_DUST.getStackNormal(), 50));
		addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.DIORITE), 2, new ItemStackWRng(new ItemStack(Blocks.GRAVEL)), new ItemStackWRng(CraftingMaterial.STONE_DUST.getStackNormal(), 50));
		addHammerRecipe(Blocks.COBBLESTONE.getDefaultState(), 2, new ItemStackWRng(new ItemStack(Blocks.GRAVEL)), new ItemStackWRng(CraftingMaterial.STONE_DUST.getStackNormal(), 50));
		addElectrolyzerRecipe(new FluidStack(FluidRegistry.WATER, 300), 200, new FluidStack(CoreInit.Hydrogen.get(), 200), new FluidStack(CoreInit.Oxygen.get(), 100));
		addCentrifugeRecipe(new FluidStack(CoreInit.Hydrogen.get(), 5), 5, new FluidStack(CoreInit.Deuterium.get(), 5));
		addCentrifugeRecipe(new FluidStack(CoreInit.Deuterium.get(), 5), 10, new FluidStack(CoreInit.Tritium.get(), 5));
		if(Config.enableHammerOreMining){
			addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.STONE), 2, new ItemStackWRng(TMResource.IRON.getStackNormal(Type.CRUSHED_ORE), 4));
			addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.ANDESITE), 2, new ItemStackWRng(TMResource.IRON.getStackNormal(Type.CRUSHED_ORE), 4));
			addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.GRANITE), 2, new ItemStackWRng(TMResource.IRON.getStackNormal(Type.CRUSHED_ORE), 4));
			addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.DIORITE), 2, new ItemStackWRng(TMResource.IRON.getStackNormal(Type.CRUSHED_ORE), 4));
			addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.STONE), 2, new ItemStackWRng(TMResource.GOLD.getStackNormal(Type.CRUSHED_ORE), .5f));
			addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.ANDESITE), 2, new ItemStackWRng(TMResource.GOLD.getStackNormal(Type.CRUSHED_ORE), .5f));
			addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.GRANITE), 2, new ItemStackWRng(TMResource.GOLD.getStackNormal(Type.CRUSHED_ORE), .5f));
			addHammerRecipe(Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.DIORITE), 2, new ItemStackWRng(TMResource.GOLD.getStackNormal(Type.CRUSHED_ORE), .5f));
		}
		if(RecipeHelper.genJson()){
			genCirc(AdvancedCraftingRecipes.BASIC_CIRCUIT_PLATE.get(), AdvancedCraftingRecipes.PHOTOACTIVE_BASIC_CIRCUIT_PLATE.get(), "basiccirc", AdvancedCraftingRecipes.RAW_BASIC_CIRCUIT_PANEL.get(), AdvancedCraftingRecipes.UNASSEMBLED_BASIC_CIRCUIT_PANEL.get(), 40, 200, 0);
			genCirc(AdvancedCraftingRecipes.BASIC_CIRCUIT_PLATE.get(), AdvancedCraftingRecipes.PHOTOACTIVE_BASIC_CIRCUIT_PLATE.get(), "normcirc", AdvancedCraftingRecipes.RAW_NORMAL_CIRCUIT_PANEL.get(), AdvancedCraftingRecipes.UNASSEMBLED_NORMAL_CIRCUIT_PANEL.get(), 100, 400, 0);
			genCirc(AdvancedCraftingRecipes.ADVANCED_CIRCUIT_PLATE.get(), AdvancedCraftingRecipes.PHOTOACTIVE_ADVANCED_CIRCUIT_PLATE.get(), "advcirc", AdvancedCraftingRecipes.RAW_ADV_CIRCUIT_PANEL.get(), AdvancedCraftingRecipes.UNASSEMBLED_ADVANCED_CIRCUIT_PANEL.get(), 200, 800, 1);
			genAlloySmelterRecipe(TMResource.COPPER.getStackNormal(Type.INGOT, 3), TMResource.TIN.getStackNormal(Type.INGOT), TMResource.BRONZE.getStackNormal(Type.INGOT, 4));
			genAlloySmelterRecipe(TMResource.GOLD.getStackNormal(Type.INGOT), TMResource.SILVER.getStackNormal(Type.INGOT), TMResource.ELECTRUM.getStackNormal(Type.INGOT, 2));
			genAlloySmelterRecipe(TMResource.LEAD.getStackNormal(Type.INGOT), TMResource.TIN.getStackNormal(Type.INGOT), CraftingMaterial.SOLDERING_ALLOY.getStackNormal(2));
			genAlloySmelterRecipe(new ItemStack(Blocks.REDSTONE_BLOCK), TMResource.ELECTRUM.getStackNormal(Type.INGOT, 4), TMResource.REDSTONE.getStackNormal(Type.INGOT, 4));
			genAlloySmelterRecipe(TMResource.COPPER.getStackNormal(Type.INGOT), TMResource.NICKEL.getStackNormal(Type.INGOT), CraftingMaterial.CUPRONICKEL_INGOT.getStackNormal(2));
			genAlloySmelterRecipe(TMResource.OBSIDIAN.getStackNormal(Type.DUST, 2), TMResource.LEAD.getStackNormal(Type.DUST), new ItemStack(CoreInit.hardenedGlass));
			genAlloySmelterRecipe(new ItemStack(CoreInit.hardenedGlass, 2), TMResource.ENDERIUM.getStackNormal(Type.DUST), new ItemStack(CoreInit.hardenedGlass, 2, 1));
			genAlloySmelterRecipe(TMResource.STEEL.getStackNormal(Type.INGOT, 4), CraftingMaterial.RAW_ELECTRICAL_STEEL_DUST.getStackNormal(), CraftingMaterial.ELECTRICAL_STEEL_INGOT.getStackNormal(4));
			genAlloySmelterRecipe(TMResource.COPPER.getStackNormal(Type.INGOT, 3), TMResource.ZINC.getStackNormal(Type.INGOT), TMResource.BRASS.getStackNormal(Type.INGOT, 4));
			genAlloySmelterRecipe(new ItemStack(Blocks.REDSTONE_BLOCK), new ItemStack(Items.ENDER_PEARL), CraftingMaterial.REDSTONE_CRYSTAL.getStackNormal());
			genChipset(1, new ItemStack(Items.REDSTONE), 0, "basicchip", 200, 0);
			genChipset(2, TMResource.GOLD.getStackNormal(Type.DUST), 2, "advchip", 500, 1);
			genCrusherRecipe(new ItemStack(Items.BLAZE_ROD), new ItemStack(Items.BLAZE_POWDER, 5), 0);
			genCrusherRecipe(new ItemStack(Blocks.COBBLESTONE), new ItemStack(Blocks.SAND, 1), 0);
			genCrusherRecipe(new ItemStack(Blocks.GRAVEL), new ItemStack(Items.FLINT, 1), 0);
			genCrusherRecipe(new ItemStack(Blocks.STONE), new ItemStack(Blocks.GRAVEL, 1), 0);
			genCrusherRecipe(new ItemStack(Items.REEDS), new ItemStack(Items.SUGAR, 2), 0);
			genCrusherRecipe(new ItemStack(Blocks.NETHERRACK), CraftingMaterial.NETHERRACK_DUST.getStackNormal(), 0);
			genCrusherRecipe(new ItemStack(Blocks.GLASS), CraftingMaterial.GLASS_DUST.getStackNormal(), 0);
			genCrusherRecipe(TMResource.WOLFRAM.getStackNormal(Type.CRUSHED_ORE), CraftingMaterial.TUNGSTATE_DUST.getStackNormal(), 1);
			genCrusherRecipe(new ItemStack(DefenseInit.oreMonazit, 1, 0), new ItemStack(DefenseInit.crushedMonazit, 2, 0), 1);
			genCrusherRecipe(new ItemStack(DefenseInit.oreMonazit, 1, 1), new ItemStack(DefenseInit.crushedMonazit, 2, 1), 1);
			genCrusherRecipe(TMResource.ALUMINUM.getStackNormal(Type.CRUSHED_ORE), CraftingMaterial.BAUXITE_DUST.getStackNormal(), 0);
			genCrusherRecipe(CraftingMaterial.IMPURE_FLUIX.getStackNormal(), CraftingMaterial.IMPURE_FLUIX_DUST.getStackNormal(), 1);
			genCrusherRecipe(CraftingMaterial.CRUSHED_OBSIDIAN.getStackNormal(), TMResource.OBSIDIAN.getStackNormal(Type.DUST, 2), 0);
			genCrusherRecipe(new ItemStack(Blocks.OBSIDIAN), CraftingMaterial.CRUSHED_OBSIDIAN.getStackNormal(2), 1);
			genBlastFurnaceRecipe(new ItemStack(Items.IRON_INGOT), ItemStack.EMPTY, TMResource.STEEL.getStackNormal(Type.INGOT), 2000, 0);
			genBlastFurnaceRecipe(TMResource.IRON.getBlockStackNormal(1), ItemStack.EMPTY, TMResource.STEEL.getBlockStackNormal(1), 16000, 0);
			genBlastFurnaceRecipe(CraftingMaterial.TUNGSTATE_DUST.getStackNormal(), ItemStack.EMPTY, CraftingMaterial.HOT_WOLFRAM_INGOT.getStackNormal(), 6400, 0);
			genBlastFurnaceRecipe(CraftingMaterial.TUNGSTATE_DUST.getStackNormal(), TMResource.COAL.getStackNormal(Type.DUST_TINY, 3), CraftingMaterial.HOT_WOLFRAM_INGOT.getStackNormal(), 6000, 2200);
			genBlastFurnaceRecipe(new ItemStack(Items.IRON_INGOT), TMResource.COAL.getStackNormal(Type.DUST, 2), TMResource.STEEL.getStackNormal(Type.INGOT), 2800, 1020);
			genBlastFurnaceRecipe(TMResource.IRON.getBlockStackNormal(1), TMResource.COAL.getStackNormal(Type.DUST, 18), TMResource.STEEL.getBlockStackNormal(1), 25000, 1020);
			genBlastFurnaceRecipe(CraftingMaterial.ENDERIUM_BASE.getStackNormal(), new ItemStack(Items.ENDER_PEARL, 4), TMResource.ENDERIUM.getStackNormal(Type.INGOT), 5000, 2000);
			genBlastFurnaceRecipe(TMResource.WOLFRAM.getStackNormal(Type.DUST), TMResource.STEEL.getStackNormal(Type.INGOT), CraftingMaterial.HOT_TUNGSTENSTEEL_INGOT.getStackNormal(2), 15000, 3000);
			genBlastFurnaceRecipe(TMResource.TITANIUM.getStackNormal(Type.DUST), ItemStack.EMPTY, TMResource.TITANIUM.getStackNormal(Type.INGOT), 4000, 1500);
			genBlastFurnaceRecipe(TMResource.CHROME.getStackNormal(Type.DUST), ItemStack.EMPTY, TMResource.CHROME.getStackNormal(Type.INGOT), 4000, 1700);
			genBlastFurnaceRecipe(TMResource.WOLFRAM.getStackNormal(Type.DUST), ItemStack.EMPTY, CraftingMaterial.HOT_WOLFRAM_INGOT.getStackNormal(), 4000, 2000);
			genBlastFurnaceRecipe(TMResource.TUNGSTENSTEEL.getStackNormal(Type.DUST), ItemStack.EMPTY, CraftingMaterial.HOT_TUNGSTENSTEEL_INGOT.getStackNormal(), 4000, 3000);
			genBlastFurnaceRecipe(CraftingMaterial.BAUXITE_DUST.getStackNormal(), TMResource.MERCURY.getStackNormal(Type.GEM), TMResource.ALUMINUM.getStackNormal(Type.INGOT), 5000, 1700);
			genBlastFurnaceRecipe(TMResource.MERCURY.getStackNormal(Type.CRUSHED_ORE), TMResource.COAL.getStackNormal(Type.DUST, 4), TMResource.MERCURY.getStackNormal(Type.GEM), 5000, 1700);
			genBlastFurnaceRecipe(TMResource.ALUMINUM.getStackNormal(Type.DUST, 2), TMResource.COAL.getStackNormal(Type.DUST), TMResource.ALUMINUM.getStackNormal(Type.INGOT, 2), 5000, 1700);
			genBlastFurnaceRecipe(TMResource.ALUMINUM.getStackNormal(Type.DUST), ItemStack.EMPTY, TMResource.ALUMINUM.getStackNormal(Type.INGOT), 5000, 0);
			genCokeOvenRecipe(new ItemStack(Items.COAL), new ItemStack(FactoryInit.coalCoke), 500, 1800);
			genCokeOvenRecipe(new ItemStack(Blocks.COAL_BLOCK), new ItemStack(FactoryInit.blockCoalCoke), 5000, 15500);
			genCokeOvenRecipe(new ItemStack(Blocks.LOG), new ItemStack(Items.COAL, 1, 1), 250, 1600);
			genPlateBenderRecipe(CraftingMaterial.SILICON.getStackNormal(), CraftingMaterial.SILICON_PLATE.getStackNormal(), 1);
			genPlateBenderRecipe(TMResource.TIN.getStackNormal(Type.PLATE), CraftingMaterial.TIN_CAN.getStackNormal(), 1);
			genPlateBenderRecipe(CraftingMaterial.ELECTRICAL_STEEL_INGOT.getStackNormal(), CraftingMaterial.ELECTRICAL_STEEL_PLATE.getStackNormal(), 1);
			genPlateBenderRecipe(CraftingMaterial.TIN_CAN.getStackNormal(), CraftingMaterial.TIN_CASING.getStackNormal(), 1);
			genPlateBenderRecipe(TMResource.STEEL.getStackNormal(Type.PLATE), CraftingMaterial.STEEL_CASING.getStackNormal(), 2);
			genPlateBenderRecipe(TMResource.TITANIUM.getStackNormal(Type.PLATE), CraftingMaterial.TITANIUM_CASING.getStackNormal(), 3);
			genWireMillRecipe(CraftingMaterial.CUPRONICKEL_INGOT.getStackNormal(4), CraftingMaterial.CUPRONICKEL_HEATING_COIL.getStackNormal(), 1);
			genWireMillRecipe(TMResource.STEEL.getStackNormal(Type.PLATE, 3), new ItemStack(CoreInit.steelFence, 2), 2);
			genWireMillRecipe(new ItemStack(Blocks.OBSIDIAN), CraftingMaterial.OBSIDIAN_ROD.getStackNormal(2), 5);
		}
		CoreInit.addReloadableTask(MachineCraftingHandler::loadJsonRecipes);
	}
	public static void genWireMillRecipeCh(ItemStack in, ItemStack out, int tier) {
		if(RecipeHelper.genJson()){
			genWireMillRecipe(in, out, tier);
		}
	}
	private static void genWireMillRecipe(ItemStack in1, ItemStack out, int tier) {
		CoreInit.initRunnables.add(() -> {
			Map<String, Object> map = new HashMap<>();
			map.put("in", new ItemStackWCount(in1));
			map.put("out", new ItemStackWCount(out));
			map.put("tier", tier);
			RecipeHelper.writeRecipe(map, out.getUnlocalizedName().substring(5), ROOT_WIRE_MILL, "");
		});
	}
	public static void genPlateBenderRecipeCh(ItemStack in, ItemStack out, int tier) {
		if(RecipeHelper.genJson()){
			genPlateBenderRecipe(in, out, tier);
		}
	}
	private static void genPlateBenderRecipe(ItemStack in1, ItemStack out, int tier) {
		CoreInit.initRunnables.add(() -> {
			Map<String, Object> map = new HashMap<>();
			map.put("in", new ItemStackWCount(in1));
			map.put("out", new ItemStackWCount(out));
			map.put("tier", tier);
			RecipeHelper.writeRecipe(map, out.getUnlocalizedName().substring(5), ROOT_PLATE_BENDER, "");
		});
	}
	private static void genCokeOvenRecipe(ItemStack in1, ItemStack out, int creosote, int time) {
		CoreInit.initRunnables.add(() -> {
			Map<String, Object> map = new HashMap<>();
			map.put("in", new ItemStackWCount(in1));
			map.put("out", new ItemStackWCount(out));
			map.put("time", time);
			map.put("creosote", creosote);
			RecipeHelper.writeRecipe(map, out.getUnlocalizedName().substring(5), ROOT_COKE_OVEN, "");
		});
	}
	private static void genBlastFurnaceRecipe(ItemStack in1, ItemStack in2, ItemStack out, int time, int heat) {
		CoreInit.initRunnables.add(() -> {
			Map<String, Object> map = new HashMap<>();
			map.put("in1", new ItemStackWCount(in1));
			map.put("in2", new ItemStackWCount(in2));
			map.put("out", new ItemStackWCount(out));
			map.put("time", time);
			map.put("heat", heat);
			RecipeHelper.writeRecipe(map, out.getUnlocalizedName().substring(5), ROOT_BLAST_FURNACE, "");
		});
	}
	public static void genCrusherRecipeCh(ItemStack in, ItemStack out, int level) {
		if(RecipeHelper.genJson()){
			genCrusherRecipe(in, out, level);
		}
	}
	private static void genCrusherRecipe(ItemStack in1, ItemStack out, int level) {
		CoreInit.initRunnables.add(() -> {
			Map<String, Object> map = new HashMap<>();
			map.put("in", new ItemStackWCount(in1));
			map.put("out", new ItemStackWCount(out));
			map.put("level", level);
			RecipeHelper.writeRecipe(map, out.getUnlocalizedName().substring(5), ROOT_CRUSHER, "");
		});
	}
	private static void genAlloySmelterRecipe(ItemStack in1, ItemStack in2, ItemStack out) {
		CoreInit.initRunnables.add(() -> {
			Map<String, Object> map = new HashMap<>();
			map.put("in1", new ItemStackWCount(in1));
			map.put("in2", new ItemStackWCount(in2));
			map.put("out", new ItemStackWCount(out));
			RecipeHelper.writeRecipe(map, out.getUnlocalizedName().substring(5), ROOT_ALLOY_SMELTER, "");
		});
	}
	private static void genChipset(int plate, ItemStack catalist, int meta, String blueprint, int laserTime, int minLvl){
		genAlloySmelterRecipe(CraftingMaterial.SILICON_PLATE.getStackNormal(plate), catalist, new ItemStack(CoreInit.chipsetBase, 1, meta));
		CoreInit.initRunnables.add(() -> {
			Map<String, Object> map = new HashMap<>();
			map.put("blueprint", blueprint);
			map.put("in", new ItemStackWCount(new ItemStack(CoreInit.chipsetBase, 1, meta)));
			map.put("out", new ItemStackWCount(new ItemStack(CoreInit.chipset, 1, meta)));
			map.put("time", laserTime);
			map.put("level", minLvl);
			RecipeHelper.writeRecipe(map, "chipset_" + meta, ROOT_LASER, "_laser");
		});
	}
	private static void genCirc(ItemStack in, ItemStack pIn, String blueprint, ItemStack outputP, ItemStack out, int uvtime, int laserTime, int minLvl) {
		CoreInit.initRunnables.add(() -> {
			Map<String, Object> map = new HashMap<>();
			map.put("blueprint", blueprint);
			map.put("in", new ItemStackWCount(pIn));
			map.put("out", new ItemStackWCount(outputP));
			map.put("time", uvtime);
			RecipeHelper.writeRecipe(map, pIn.getUnlocalizedName(), ROOT_UV, "_uv");
			map = new HashMap<>();
			map.put("blueprint", blueprint);
			map.put("in", new ItemStackWCount(in));
			map.put("out", new ItemStackWCount(out));
			map.put("time", laserTime);
			map.put("level", minLvl);
			RecipeHelper.writeRecipe(map, pIn.getUnlocalizedName(), ROOT_LASER, "_laser");
		});
	}
	private static void loadJsonRecipes(){
		uvBoxRecipes.clear();
		laserEngraverRecipes.clear();
		alloySmelterRecipes.clear();
		crusherRecipes.clear();
		blastFurnaceRecipes.clear();
		cokeOvenRecipes.clear();
		plateBenderRecipes.clear();
		wireMillRecipes.clear();
		JsonContext ctx = new JsonContext("tomsmodcore");
		TomsModUtils.walkResources(ROOT_UV, TomsModUtils.gson, ctx, (o, s) -> parseRecipeJsonUV(o, ctx, s));
		TomsModUtils.walkResources(ROOT_LASER, TomsModUtils.gson, ctx, (o, s) -> parseRecipeJsonLaser(o, ctx, s));
		TomsModUtils.walkResources(ROOT_ALLOY_SMELTER, TomsModUtils.gson, ctx, (o, s) -> parseRecipeJsonAlloySm(o, ctx, s));
		TomsModUtils.walkResources(ROOT_CRUSHER, TomsModUtils.gson, ctx, (o, s) -> parseRecipeJsonCrusher(o, ctx, s));
		TomsModUtils.walkResources(ROOT_BLAST_FURNACE, TomsModUtils.gson, ctx, (o, s) -> parseRecipeJsonBlastFurnace(o, ctx, s));
		TomsModUtils.walkResources(ROOT_COKE_OVEN, TomsModUtils.gson, ctx, (o, s) -> parseRecipeJsonCokeOven(o, ctx, s));
		TomsModUtils.walkResources(ROOT_PLATE_BENDER, TomsModUtils.gson, ctx, (o, s) -> parseRecipeJsonPlateBender(o, ctx, s));
		TomsModUtils.walkResources(ROOT_WIRE_MILL, TomsModUtils.gson, ctx, (o, s) -> parseRecipeJsonWireMill(o, ctx, s));
	}

	private static void parseRecipeJsonWireMill(JsonObject obj, JsonContext ctx, String rid) {
		if(obj.size() == 0)return;
		boolean c = true;
		if(obj.has("condition")){
			JsonObject cond = obj.get("condition").getAsJsonObject();
			c = CraftingHelper.getCondition(cond, ctx).getAsBoolean();
		}
		if(c){
			ItemStack in = CraftingHelper.getItemStack(obj.get("in").getAsJsonObject(), ctx);
			ItemStack out = CraftingHelper.getItemStack(obj.get("out").getAsJsonObject(), ctx);
			int tier = obj.get("tier").getAsInt();
			addWireMillRecipe(in, out, tier, rid);
		}
	}

	private static void parseRecipeJsonPlateBender(JsonObject obj, JsonContext ctx, String rid) {
		if(obj.size() == 0)return;
		boolean c = true;
		if(obj.has("condition")){
			JsonObject cond = obj.get("condition").getAsJsonObject();
			c = CraftingHelper.getCondition(cond, ctx).getAsBoolean();
		}
		if(c){
			ItemStack in = CraftingHelper.getItemStack(obj.get("in").getAsJsonObject(), ctx);
			ItemStack out = CraftingHelper.getItemStack(obj.get("out").getAsJsonObject(), ctx);
			int tier = obj.get("tier").getAsInt();
			addPlateBenderRecipe(in, out, tier, rid);
		}
	}

	private static void parseRecipeJsonCokeOven(JsonObject obj, JsonContext ctx, String rid) {
		if(obj.size() == 0)return;
		boolean c = true;
		if(obj.has("condition")){
			JsonObject cond = obj.get("condition").getAsJsonObject();
			c = CraftingHelper.getCondition(cond, ctx).getAsBoolean();
		}
		if(c){
			ItemStack in = CraftingHelper.getItemStack(obj.get("in").getAsJsonObject(), ctx);
			ItemStack out = CraftingHelper.getItemStack(obj.get("out").getAsJsonObject(), ctx);
			int time = obj.get("time").getAsInt();
			int creosote = obj.get("creosote").getAsInt();
			addCokeOvenRecipe(in, out, creosote, time, rid);
		}
	}

	private static void parseRecipeJsonBlastFurnace(JsonObject obj, JsonContext ctx, String rid) {
		if(obj.size() == 0)return;
		boolean c = true;
		if(obj.has("condition")){
			JsonObject cond = obj.get("condition").getAsJsonObject();
			c = CraftingHelper.getCondition(cond, ctx).getAsBoolean();
		}
		if(c){
			ItemStack in = CraftingHelper.getItemStack(obj.get("in1").getAsJsonObject(), ctx);
			ItemStack in2 = CraftingHelper.getItemStack(obj.get("in2").getAsJsonObject(), ctx);
			ItemStack out = CraftingHelper.getItemStack(obj.get("out").getAsJsonObject(), ctx);
			int time = obj.get("time").getAsInt();
			int heat = obj.get("heat").getAsInt();
			addBlastFurnaceRecipe(in, in2, out, time, heat, rid);
		}
	}
	private static void parseRecipeJsonCrusher(JsonObject obj, JsonContext ctx, String rid) {
		if(obj.size() == 0)return;
		boolean c = true;
		if(obj.has("condition")){
			JsonObject cond = obj.get("condition").getAsJsonObject();
			c = CraftingHelper.getCondition(cond, ctx).getAsBoolean();
		}
		if(c){
			ItemStack in = CraftingHelper.getItemStack(obj.get("in").getAsJsonObject(), ctx);
			ItemStack out = CraftingHelper.getItemStack(obj.get("out").getAsJsonObject(), ctx);
			int level = obj.get("level").getAsInt();
			addCrusherRecipeInt(in, out, rid, level);
		}
	}
	private static void parseRecipeJsonUV(JsonObject obj, JsonContext ctx, String rid){
		if(obj.size() == 0)return;
		boolean c = true;
		if(obj.has("condition")){
			JsonObject cond = obj.get("condition").getAsJsonObject();
			c = CraftingHelper.getCondition(cond, ctx).getAsBoolean();
		}
		if(c){
			String blueprint = obj.get("blueprint").getAsString();
			ItemStack in = CraftingHelper.getItemStack(obj.get("in").getAsJsonObject(), ctx);
			ItemStack out = CraftingHelper.getItemStack(obj.get("out").getAsJsonObject(), ctx);
			int time = obj.get("time").getAsInt();
			addUVBoxRecipe(in, ItemBlueprint.getBlueprintFor(blueprint), out, time, rid);
		}
	}
	private static void parseRecipeJsonLaser(JsonObject obj, JsonContext ctx, String rid){
		if(obj.size() == 0)return;
		boolean c = true;
		if(obj.has("condition")){
			JsonObject cond = obj.get("condition").getAsJsonObject();
			c = CraftingHelper.getCondition(cond, ctx).getAsBoolean();
		}
		if(c){
			String blueprint = obj.get("blueprint").getAsString();
			ItemStack in = CraftingHelper.getItemStack(obj.get("in").getAsJsonObject(), ctx);
			ItemStack out = CraftingHelper.getItemStack(obj.get("out").getAsJsonObject(), ctx);
			int time = obj.get("time").getAsInt();
			int lvl = obj.get("level").getAsInt();
			addLaserEngraverRecipe(in, ItemBlueprint.getBlueprintFor(blueprint), out, time, lvl, rid);
		}
	}
	private static void parseRecipeJsonAlloySm(JsonObject obj, JsonContext ctx, String rid){
		if(obj.size() == 0)return;
		boolean c = true;
		if(obj.has("condition")){
			JsonObject cond = obj.get("condition").getAsJsonObject();
			c = CraftingHelper.getCondition(cond, ctx).getAsBoolean();
		}
		if(c){
			ItemStack in1 = CraftingHelper.getItemStack(obj.get("in1").getAsJsonObject(), ctx);
			ItemStack in2 = CraftingHelper.getItemStack(obj.get("in2").getAsJsonObject(), ctx);
			ItemStack out = CraftingHelper.getItemStack(obj.get("out").getAsJsonObject(), ctx);
			addAlloySmelterRecipe(in1, in2, out, rid);
		}
	}

	private static final ItemStack COIL_EMPTY = new ItemStack(CoreInit.emptyWireCoil);
	private static final Map<ItemStackChecker, ItemStackChecker> crusherRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> plateBenderRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> wireMillRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> coilerPlantRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> alloySmelterRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> cokeOvenRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> blastFurnaceRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> fluidTransposerRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> laserEngraverRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> uvBoxRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> electrolyzerRecipes = new HashMap<>();
	private static final Map<ItemStackChecker, ItemStackChecker> centrifugeRecipes = new HashMap<>();
	private static final Map<IBlockState, Entry<Integer, List<ItemStackWRng>>> hammerRecipes = new HashMap<>();

	private static void addCrusherRecipeInt(ItemStack input, ItemStack output, String loc, int level) {
		ItemStackChecker c = new ItemStackChecker(input);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : crusherRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			crusherRecipes.put(new ItemStackChecker(input).setName(loc), new ItemStackChecker(output).setExtra(input.getCount()).setExtra2(level));
	}

	private static void addPlateBenderRecipe(ItemStack input, ItemStack output, int tier, String loc) {
		ItemStackChecker c = new ItemStackChecker(input);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : plateBenderRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			plateBenderRecipes.put(new ItemStackChecker(input).setName(loc), new ItemStackChecker(output).setExtra(input.getCount()).setExtra2(tier));
	}

	private static void addWireMillRecipe(ItemStack input, ItemStack output, int tier, String loc) {
		ItemStackChecker c = new ItemStackChecker(input);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : wireMillRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			wireMillRecipes.put(new ItemStackChecker(input).setName(loc), new ItemStackChecker(output).setExtra(input.getCount()).setExtra2(tier));
	}

	public static void addCoilerPlantRecipe(ItemStack input, ItemStack output) {
		ItemStackChecker c = new ItemStackChecker(input);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : coilerPlantRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			coilerPlantRecipes.put(new ItemStackChecker(input), new ItemStackChecker(output).setExtra(input.getCount()).setExtra2(output.getCount()));
	}

	public static ItemStackChecker getCrusherOutput(ItemStack stack, int level) {
		if (stack.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(stack);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : crusherRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				if(recipe.getValue().extra2 <= level)
					return recipe.getValue();
				else return null;
		}
		return null;
	}

	public static ItemStackChecker getPlateBenderOutput(ItemStack stack, int lvl) {
		if (stack.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(stack);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : plateBenderRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) {
				if (lvl != -1 && recipe.getValue().extra2 > lvl)
					return null;
				else
					return recipe.getValue();
			}
		}
		return null;
	}

	public static ItemStackChecker getWireMillOutput(ItemStack stack, int lvl) {
		if (stack.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(stack);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : wireMillRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) {
				if (lvl != -1 && recipe.getValue().extra2 > lvl)
					return null;
				else
					return recipe.getValue();
			}
		}
		return null;
	}

	public static ItemStackChecker getCoilerOutput(ItemStack stack) {
		if (stack.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(stack);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : coilerPlantRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) { return recipe.getValue(); }
		}
		return null;
	}

	public static class ItemStackChecker {
		private final ItemStack stack, extraStack, extraStack2;
		private int extra, extra2, extra3, extra4, heat;
		private FluidStack fluid = null, fluid2 = null;
		private boolean mode = false;
		private String name;

		public ItemStackChecker() {
			this.stack = ItemStack.EMPTY;
			this.extraStack = ItemStack.EMPTY;
			this.extraStack2 = ItemStack.EMPTY;
		}

		public ItemStackChecker(ItemStack stack) {
			this.stack = stack;
			this.extraStack = ItemStack.EMPTY;
			this.extraStack2 = ItemStack.EMPTY;
		}

		public ItemStackChecker(ItemStack stack, ItemStack extra) {
			this.stack = stack;
			this.extraStack = extra;
			this.extraStack2 = ItemStack.EMPTY;
		}

		public ItemStackChecker(ItemStack stack, ItemStack extra, ItemStack extra2) {
			this.stack = stack;
			this.extraStack = extra;
			this.extraStack2 = extra2;
		}

		public ItemStack getStack() {
			return stack.copy();
		}

		public ItemStackChecker setExtra(int extra) {
			this.extra = extra;
			return this;
		}

		public ItemStackChecker setExtra2(int extra) {
			this.extra2 = extra;
			return this;
		}

		public ItemStackChecker setExtra3(int extra) {
			this.extra3 = extra;
			return this;
		}

		public ItemStackChecker setHeat(int extra) {
			this.heat = extra;
			return this;
		}

		public ItemStack getExtraStack() {
			return extraStack;
		}

		public ItemStack getExtraStack2() {
			return extraStack2;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (!(obj instanceof ItemStackChecker))
				return false;
			ItemStackChecker other = (ItemStackChecker) obj;
			return TomsModUtils.areItemStacksEqualOreDict(stack, other.stack, true, true, false, true) && mode == other.mode && heat <= other.heat && TomsModUtils.areFluidStacksEqual2(fluid, other.fluid) && other.stack.getCount() >= stack.getCount() && ((extraStack.isEmpty() && other.extraStack.isEmpty()) || TomsModUtils.areItemStacksEqualOreDict(extraStack, other.extraStack, true, true, false, true) && other.extraStack.getCount() >= extraStack.getCount()) && TomsModUtils.areFluidStacksEqual2(fluid2, other.fluid2) && ((extraStack2.isEmpty() && other.extraStack2.isEmpty()) || TomsModUtils.areItemStacksEqualOreDict(extraStack2, other.extraStack2, true, true, false, true) && other.extraStack2.getCount() >= extraStack2.getCount());
		}

		public int getExtra() {
			return extra;
		}

		public int getExtra2() {
			return extra2;
		}

		public int getExtra3() {
			return extra3;
		}

		public int getExtra4() {
			return extra4;
		}

		public FluidStack getExtraF() {
			return fluid;
		}

		public ItemStackChecker setExtraF(FluidStack extra) {
			this.fluid = extra;
			return this;
		}

		public FluidStack getExtraF2() {
			return fluid2;
		}

		public ItemStackChecker setExtraF2(FluidStack extra) {
			this.fluid2 = extra;
			return this;
		}

		public boolean getMode() {
			return mode;
		}

		public ItemStackChecker setMode(boolean mode) {
			this.mode = mode;
			return this;
		}

		public ItemStackChecker createSwappedExtra() {
			ItemStackChecker n = new ItemStackChecker(stack, extraStack, extraStack2);
			n.setExtra2(extra);
			n.setExtra(extra2);
			return n.setExtra3(extra3).setExtra4(extra4).setExtraF(fluid).setExtraF2(fluid2).setMode(mode).setHeat(heat);
		}

		public int getHeat() {
			return heat;
		}

		public static ItemStackChecker load(NBTTagCompound tag) {
			ItemStackChecker c = new ItemStackChecker(new ItemStack(tag.getCompoundTag("main")), new ItemStack(tag.getCompoundTag("extraS")), new ItemStack(tag.getCompoundTag("extraS2")));
			c.extra = tag.getInteger("extra");
			c.extra2 = tag.getInteger("extra2");
			c.extra3 = tag.getInteger("extra3");
			c.heat = tag.getInteger("heat");
			c.mode = tag.getBoolean("mode");
			c.extra4 = tag.getInteger("extra4");
			c.fluid = FluidStack.loadFluidStackFromNBT(tag.getCompoundTag("fluid"));
			c.fluid2 = FluidStack.loadFluidStackFromNBT(tag.getCompoundTag("fluid2"));
			String name = tag.getString("rname");
			c.name = name == null || name.isEmpty() ? null : name;
			return c;
		}

		public NBTTagCompound writeTo(NBTTagCompound tag) {
			tag.setTag("main", stack.writeToNBT(new NBTTagCompound()));
			if (!extraStack.isEmpty())
				tag.setTag("extraS", extraStack.writeToNBT(new NBTTagCompound()));
			if (!extraStack2.isEmpty())
				tag.setTag("extraS2", extraStack2.writeToNBT(new NBTTagCompound()));
			if (extra != 0)
				tag.setInteger("extra", extra);
			if (extra2 != 0)
				tag.setInteger("extra2", extra2);
			if (extra3 != 0)
				tag.setInteger("extra3", extra3);
			if (extra4 != 0)
				tag.setInteger("extra4", extra4);
			if (heat != 0)
				tag.setInteger("heat", heat);
			if (mode)
				tag.setBoolean("mode", mode);
			if (name != null)
				tag.setString("rname", name);
			if (fluid != null)
				tag.setTag("fluid", fluid.writeToNBT(new NBTTagCompound()));
			if (fluid2 != null)
				tag.setTag("fluid2", fluid2.writeToNBT(new NBTTagCompound()));
			return tag;
		}

		public NBTTagCompound writeToNew() {
			return writeTo(new NBTTagCompound());
		}

		public ItemStackChecker setExtra4(int extra4) {
			this.extra4 = extra4;
			return this;
		}

		public ItemStackChecker setName(String name) {
			this.name = name;
			return this;
		}
	}

	public static List<RecipeData> getCrusherRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : crusherRecipes.entrySet()) {
			ret.add(new RecipeData(recipe.getValue().extra2, recipe.getKey().stack, recipe.getValue().stack).setId(recipe.getKey().name));
		}
		return ret;
	}

	public static List<RecipeData> getPlateBenderRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : plateBenderRecipes.entrySet()) {
			ret.add(new RecipeData(recipe.getValue().extra2, recipe.getKey().stack, recipe.getValue().stack).setId(recipe.getKey().name));
		}
		return ret;
	}

	public static List<RecipeData> getWireMillRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : wireMillRecipes.entrySet()) {
			ret.add(new RecipeData(recipe.getValue().extra2, recipe.getKey().stack, recipe.getValue().stack).setId(recipe.getKey().name));
		}
		return ret;
	}

	public static List<RecipeData> getCoilerRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : coilerPlantRecipes.entrySet()) {
			ret.add(new RecipeData(recipe.getKey().stack, COIL_EMPTY, recipe.getValue().stack));
		}
		return ret;
	}

	public static ItemStack getFurnaceRecipe(ItemStack in) {
		if (in == null)
			return ItemStack.EMPTY;
		ItemStack stack = in.copy();
		stack.setCount(1);
		ItemStack s = FurnaceRecipes.instance().getSmeltingResult(stack);
		if (!s.isEmpty())
			s = s.copy();
		return s;
	}

	public static List<RecipeData> getAlloySmelterRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : alloySmelterRecipes.entrySet()) {
			ret.add(new RecipeData(recipe.getKey().stack, recipe.getKey().extraStack, recipe.getValue().stack).setId(recipe.getKey().name));
		}
		return ret;
	}

	public static ItemStackChecker getAlloySmelterOutput(ItemStack stack1, ItemStack stack2) {
		if (stack1.isEmpty() || stack2.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(stack1, stack2);
		ItemStackChecker cs = new ItemStackChecker(stack2, stack1);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : alloySmelterRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) {
				return recipe.getValue();
			} else if (recipe.getKey().equals(cs)) { return recipe.getValue().createSwappedExtra(); }
		}
		return null;
	}

	private static void addAlloySmelterRecipe(ItemStack input1, ItemStack input2, ItemStack output, String loc) {
		ItemStackChecker c = new ItemStackChecker(input1, input2);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : alloySmelterRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			alloySmelterRecipes.put(new ItemStackChecker(input1, input2).setName(loc), new ItemStackChecker(output).setExtra(input1.getCount()).setExtra2(input2.getCount()));
	}

	private static void addCokeOvenRecipe(ItemStack input1, ItemStack output, int creosote, int time, String loc) {
		ItemStackChecker c = new ItemStackChecker(input1).setExtra2(creosote);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : cokeOvenRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			cokeOvenRecipes.put(new ItemStackChecker(input1).setName(loc), new ItemStackChecker(output).setExtra(input1.getCount()).setExtra2(creosote).setExtra3(time));
	}

	public static ItemStackChecker getCokeOvenOutput(ItemStack stack) {
		if (stack.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(stack);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : cokeOvenRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) { return recipe.getValue(); }
		}
		return null;
	}

	private static void addBlastFurnaceRecipe(ItemStack input1, ItemStack input2, ItemStack output, int time, int heat, String loc) {
		ItemStackChecker c = new ItemStackChecker(input1, input2).setHeat(heat);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : blastFurnaceRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			blastFurnaceRecipes.put(new ItemStackChecker(input1, input2).setHeat(heat).setName(loc), new ItemStackChecker(output).setExtra(input1 != null ? input1.getCount() : 0).setExtra2(input2 != null ? input2.getCount() : 0).setExtra3(time));
	}

	public static ItemStackChecker getBlastFurnaceOutput(ItemStack stack1, ItemStack stack2, int heat) {
		if (heat == 0 && stack1 == null)
			return null;
		if (heat > 0 && (stack1 == null && stack2 == null))
			return null;
		ItemStackChecker c = new ItemStackChecker(stack1, stack2).setHeat(heat);
		ItemStackChecker cs = new ItemStackChecker(stack2, stack1).setHeat(heat);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : blastFurnaceRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) {
				return recipe.getValue();
			} else if (recipe.getKey().equals(cs)) { return recipe.getValue().createSwappedExtra(); }
		}
		return null;
	}

	public static void addFluidTransposerRecipe(ItemStack input1, ItemStack output, FluidStack fluid, boolean isExtract) {
		ItemStackChecker c = new ItemStackChecker(input1);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : fluidTransposerRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			fluidTransposerRecipes.put(new ItemStackChecker(input1).setExtraF(fluid).setMode(isExtract), new ItemStackChecker(output).setExtra(fluid.amount));
	}

	public static ItemStackChecker getFluidTransposerOutput(ItemStack stack, FluidTank tank, boolean isExtract) {
		if (stack.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(stack).setExtraF(tank.getFluid()).setMode(isExtract);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : fluidTransposerRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) { return recipe.getValue(); }
		}
		if (FluidUtil.getFluidHandler(stack.copy()) != null) {
			ItemStack container = stack.copy();
			IFluidHandlerItem h = FluidUtil.getFluidHandler(container);
			if (isExtract) {
				if (tank.getFluidAmount() != 10000) {
					if (tank.getFluid() != null) {
						FluidStack drained = h.drain(10000 - tank.getFluid().amount, false);
						if (drained != null && drained.amount > 0) {
							FluidStack stackOut = h.drain(10000 - tank.getFluid().amount, true);
							c = new ItemStackChecker(h.getContainer());
							c.setExtra(1);
							c.setExtraF(stackOut);
							return c;
						}
					} else {
						FluidStack drained = h.drain(10000, false);
						if (drained != null && drained.amount > 0) {
							FluidStack stackOut = h.drain(10000, true);
							c = new ItemStackChecker(h.getContainer());
							c.setExtraF(stackOut);
							c.setExtra(1);
							return c;
						}
					}
				}
			} else {
				int filled = h.fill(tank.getFluid(), false);
				if (filled > 0) {
					int stackOut = h.fill(tank.getFluid(), true);
					c = new ItemStackChecker(h.getContainer());
					c.setExtra(stackOut);
					return c;
				}
			}
		}
		return null;
	}

	public static List<RecipeData> getBlastFurnaceRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : blastFurnaceRecipes.entrySet()) {
			RecipeData d = new RecipeData(recipe.getKey().stack, recipe.getKey().extraStack, recipe.getValue().stack, recipe.getKey().getMode());
			d.processTime = recipe.getValue().getExtra3();
			d.energy = recipe.getKey().getHeat();
			d.setId(recipe.getKey().name);
			ret.add(d);
		}
		return ret;
	}

	private static void addLaserEngraverRecipe(ItemStack silicon, ItemStack blueprint, ItemStack output, int time, int minlvl, String name) {
		ItemStackChecker c = new ItemStackChecker(silicon, blueprint);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : laserEngraverRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found) {
			laserEngraverRecipes.put(new ItemStackChecker(silicon, blueprint), new ItemStackChecker(output).setExtra(silicon.getCount()).setExtra3(time).setExtra4(minlvl).setName(name));
			/*if (!blueprints.stream().anyMatch(blueprint::isItemEqual)) {
				blueprints.add(blueprint);
			}*/
		}
	}

	public static ItemStackChecker getLaserEngraverOutput(ItemStack silicon, ItemStack blueprint) {
		if (silicon.isEmpty() && blueprint.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(silicon, blueprint);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : laserEngraverRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) { return recipe.getValue(); }
		}
		return null;
	}

	/*public static void addLaserEngraverRecipe(int silicon, ItemStack blueprint, ItemStack output, int time, int minlvl) {
		addLaserEngraverRecipe(CraftingMaterial.SILICON_PLATE.getStackNormal(silicon), blueprint, output, time, minlvl);
	}*/

	private static void addUVBoxRecipe(ItemStack in, ItemStack blueprint, ItemStack output, int time, String name) {
		ItemStackChecker c = new ItemStackChecker(in, blueprint);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : uvBoxRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found) {
			uvBoxRecipes.put(new ItemStackChecker(in, blueprint), new ItemStackChecker(output).setExtra(in.getCount()).setExtra3(time).setName(name));
			/*if (!blueprints.stream().anyMatch(blueprint::isItemEqual)) {
				blueprints.add(blueprint);
			}*/
		}
	}

	public static ItemStackChecker getUVBoxOutput(ItemStack in, ItemStack blueprint) {
		if (in.isEmpty() && blueprint.isEmpty())
			return null;
		ItemStackChecker c = new ItemStackChecker(in, blueprint);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : uvBoxRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) { return recipe.getValue(); }
		}
		return null;
	}

	public static void addHammerRecipe(IBlockState state, int lvl, ItemStack... stack) {
		addHammerRecipe(state, lvl, Arrays.stream(stack).filter(s -> s != null && !s.isEmpty()).map(ItemStackWRng::new).collect(Collectors.toList()));
	}

	public static void addHammerRecipe(IBlockState state, int lvl, ItemStackWRng... stack) {
		addHammerRecipe(state, lvl, Arrays.asList(stack));
	}

	public static void addHammerRecipe(IBlockState state, int lvl, List<ItemStackWRng> stack) {
		if (state != null && stack.size() > 0) {
			if(!hammerRecipes.containsKey(state))
				hammerRecipes.put(state, new EmptyEntry<>(lvl, stack.stream().filter(d -> d != null && !d.stack.isEmpty()).collect(Collectors.toList())));
			else{
				Entry<Integer, List<ItemStackWRng>> v = hammerRecipes.get(state);
				stack.stream().filter(d -> d != null && !d.stack.isEmpty()).forEach(v.getValue()::add);
			}
		}
	}

	public static List<ItemStack> getHammerResult(int lvl, IBlockState state, Random rng) {
		Entry<Integer, List<ItemStackWRng>> v = hammerRecipes.get(state);
		return v != null ? v.getKey() <= lvl ? v.getValue().stream().filter(r -> r.check(rng)).map(ItemStackWRng::getStack).collect(Collectors.toList()) : null : null;
	}

	public static class ItemStackWRng {
		public ItemStack stack;
		public float chance;

		public boolean check(Random r) {
			return (r.nextInt(100) + r.nextFloat()) <= chance;
		}

		public ItemStack getStack() {
			return stack.copy();
		}

		public ItemStackWRng(ItemStack stack, float chance) {
			this.stack = stack;
			this.chance = chance;
		}

		public ItemStackWRng(ItemStack stack) {
			this.stack = stack;
			this.chance = 100;
		}
	}

	public static void addElectrolyzerRecipe(FluidStack in, int energy, FluidStack out1, FluidStack out2) {
		ItemStackChecker c = new ItemStackChecker().setExtraF(in);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : electrolyzerRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			electrolyzerRecipes.put(new ItemStackChecker().setExtraF(in), new ItemStackChecker().setExtraF(out1).setExtraF2(out2).setExtra(energy).setExtra2(in.amount));
	}

	public static ItemStackChecker getElectrolyzerOutput(FluidStack in) {
		if (in != null)
			return null;
		ItemStackChecker c = new ItemStackChecker().setExtraF(in);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : electrolyzerRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) { return recipe.getValue(); }
		}
		return null;
	}

	public static void addCentrifugeRecipe(FluidStack in, int time, FluidStack out1) {
		ItemStackChecker c = new ItemStackChecker().setExtraF(in);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : centrifugeRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			centrifugeRecipes.put(new ItemStackChecker().setExtraF(in), new ItemStackChecker().setExtraF(out1).setExtra(time).setExtra2(in.amount).setMode(true));
	}

	public static void addCentrifugeRecipe(ItemStack in, int time, FluidStack out1) {
		ItemStackChecker c = new ItemStackChecker(in);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : centrifugeRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			centrifugeRecipes.put(new ItemStackChecker(in), new ItemStackChecker().setExtraF(out1).setExtra(time).setExtra2(in.getCount()));
	}

	public static void addCentrifugeRecipe(ItemStack in, int time, ItemStack out1, ItemStack out2, ItemStack out3, FluidStack out) {
		ItemStackChecker c = new ItemStackChecker(in);
		boolean found = false;
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : centrifugeRecipes.entrySet()) {
			if (recipe.getKey().equals(c))
				found = true;
		}
		if (!found)
			centrifugeRecipes.put(new ItemStackChecker(in), new ItemStackChecker(out1, out2, out3).setExtraF(out).setExtra(time).setExtra2(in.getCount()));
	}

	public static ItemStackChecker getCentrifugeOutput(ItemStack stack, FluidStack in) {
		if (in != null)
			return null;
		ItemStackChecker c = new ItemStackChecker().setExtraF(in);
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : centrifugeRecipes.entrySet()) {
			if (recipe.getKey().equals(c)) { return recipe.getValue(); }
		}
		return null;
	}
	public static List<RecipeData> getLaserEngraverRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : laserEngraverRecipes.entrySet()) {
			RecipeData d = new RecipeData(recipe.getKey().stack, recipe.getKey().extraStack, recipe.getValue().stack);
			d.energy = recipe.getValue().extra4;
			d.setId(recipe.getValue().name);
			ret.add(d);
		}
		return ret;
	}
	public static List<RecipeData> getUVBoxRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : uvBoxRecipes.entrySet()) {
			ret.add(new RecipeData(recipe.getKey().stack, recipe.getKey().extraStack, recipe.getValue().stack).setId(recipe.getValue().name));
		}
		return ret;
	}
	public static List<Entry<IBlockState, Entry<Integer, List<ItemStackWRng>>>> getHammerRecipes() {
		return new ArrayList<>(hammerRecipes.entrySet());
	}
	public static List<RecipeData> getCokeOvenRecipes() {
		List<RecipeData> ret = new ArrayList<>();
		for (Entry<ItemStackChecker, ItemStackChecker> recipe : cokeOvenRecipes.entrySet()) {
			RecipeData d = new RecipeData(recipe.getValue().extra2, recipe.getKey().stack, recipe.getValue().stack);
			d.setId(recipe.getKey().name);
			d.processTime = recipe.getValue().extra3;
			ret.add(d);
		}
		return ret;
	}
}


text:
namespace v8 {
namespace base {

// Convenience function that returns true if the supplied value is in range
// for the destination type.
template <typename Dst, typename Src>
inline bool IsValueInRangeForNumericType(Src value) {
  return internal::DstRangeRelationToSrcRange<Dst>(value) ==
         internal::RANGE_VALID;
}

// checked_cast<> is analogous to static_cast<> for numeric types,
// except that it CHECKs that the specified numeric conversion will not
// overflow or underflow. NaN source will always trigger a CHECK.
template <typename Dst, typename Src>
inline Dst checked_cast(Src value) {
  CHECK(IsValueInRangeForNumericType<Dst>(value));
  return static_cast<Dst>(value);
}

// saturated_cast<> is analogous to static_cast<> for numeric types, except
// that the specified numeric conversion will saturate rather than overflow or
// underflow. NaN assignment to an integral will trigger a CHECK condition.
template <typename Dst, typename Src>
inline Dst saturated_cast(Src value) {
  // Optimization for floating point values, which already saturate.
  if (std::numeric_limits<Dst>::is_iec559)
    return static_cast<Dst>(value);

  switch (internal::DstRangeRelationToSrcRange<Dst>(value)) {
    case internal::RANGE_VALID:
      return static_cast<Dst>(value);

    case internal::RANGE_UNDERFLOW:
      return std::numeric_limits<Dst>::min();

    case internal::RANGE_OVERFLOW:
      return std::numeric_limits<Dst>::max();

    // Should fail only on attempting to assign NaN to a saturated integer.
    case internal::RANGE_INVALID:
      UNREACHABLE();
  }

  UNREACHABLE();
}

}  // namespace base
}  // namespace v8

#endif  // V8_BASE_SAFE_CONVERSIONS_H_