from __future__ import division, print_function, absolute_import
from copy import deepcopy
from math import copysign
import numpy as np
import scipy.sparse as spc
from scipy.sparse.linalg import LinearOperator
from scipy.sparse import csc_matrix
from scipy.optimize import OptimizeResult

class BoxConstraint:

    def __init__(self, kind, enforce_feasibility=False):
        self.kind = kind
        self.enforce_feasibility = enforce_feasibility
        self.isinitialized = False

    def evaluate_and_initialize(self, x0, sparse_jacobian=None):
        x0 = np.atleast_1d(x0).astype(float)
        f0 = x0
        self.n = x0.size
        self.m = f0.size
        if sparse_jacobian or sparse_jacobian is None:
            J0 = spc.eye(self.n).tocsr()
            self.sparse_jacobian = True
        else:
            J0 = np.eye(self.n)
            self.sparse_jacobian = False

        self.J0 = J0
        self.kind = _check_kind(self.kind, self.m)
        self.enforce_feasibility \
            = _check_enforce_feasibility(self.enforce_feasibility, self.m)
        self.isinitialized = True
        if not _is_feasible(self.kind, self.enforce_feasibility, f0):
            warn("The initial point was changed in order "
                 "to stay inside box constraints.")
            x0_new = _reinforce_box_constraint(self.kind,
                                               self.enforce_feasibility,
                                               x0)
            self.x0 = x0_new
            self.f0 = x0_new
            return x0_new
        else:
            self.x0 = x0
            self.f0 = f0
            return x0

    def to_linear(self):
        if not self.isinitialized:
            raise RuntimeError("Trying to convert uninitialized constraint.")
        # Build Constraints
        linear = LinearConstraint(self.J0, self.kind,
                                  self.enforce_feasibility)
        linear.isinitialized = True
        linear.m = self.m
        linear.n = self.n
        linear.sparse_jacobian = self.sparse_jacobian
        linear.x0 = self.x0
        linear.f0 = self.f0
        linear.J0 = self.J0
        return linear

    def to_nonlinear(self):
        if not self.isinitialized:
            raise RuntimeError("Trying to convert uninitialized constraint.")
        return self.to_linear().to_nonlinear()

class NonlinearConstraint:

    def __init__(self, fun, kind, jac, hess='2-point', enforce_feasibility=False):
        self._fun = fun
        self.kind = kind
        self._jac = jac
        self._hess = hess
        self.enforce_feasibility = enforce_feasibility
        self.isinitialized = False

    def evaluate_and_initialize(self, x0, sparse_jacobian=None):
        x0 = np.atleast_1d(x0).astype(float)
        f0 = np.atleast_1d(self._fun(x0))
        v0 = np.zeros_like(f0)
        J0 = self._jac(x0)

        def fun_wrapped(x):
            return np.atleast_1d(self._fun(x))

        if sparse_jacobian or (sparse_jacobian is None and spc.issparse(J0)):
            def jac_wrapped(x):
                return spc.csr_matrix(self._jac(x))
            self.sparse_jacobian = True

            self.J0 = spc.csr_matrix(J0)

        else:
            def jac_wrapped(x):
                J = self._jac(x)
                if spc.issparse(J):
                    return J.toarray()
                else:
                    return np.atleast_2d(J)
            self.sparse_jacobian = False

            if spc.issparse(J0):
                self.J0 = J0.toarray()
            else:
                self.J0 = np.atleast_2d(J0)

        if callable(self._hess):
            H0 = self._hess(x0, v0)

            if spc.issparse(H0):
                H0 = spc.csr_matrix(H0)

                def hess_wrapped(x, v):
                    return spc.csr_matrix(self._hess(x, v))

            elif isinstance(H0, LinearOperator):
                def hess_wrapped(x, v):
                    return self._hess(x, v)

            else:
                H0 = np.atleast_2d(np.asarray(H0))

                def hess_wrapped(x, v):
                    return np.atleast_2d(np.asarray(self._hess(x, v)))

        elif self._hess in ('2-point', '3-point', 'cs'):
            approx_method = self._hess

            def jac_dot_v(x, v):
                J = jac_wrapped(x)
                return J.T.dot(v)

            def hess_wrapped(x, v):
                return approx_derivative(jac_dot_v, x, approx_method,
                                         as_linear_operator=True,
                                         args=(v,))

        else:
            hess_wrapped = self._hess

        self.fun = fun_wrapped
        self.jac = jac_wrapped
        self.hess = hess_wrapped
        self.x0 = x0
        self.f0 = f0
        self.n = x0.size
        self.m = f0.size
        self.kind = _check_kind(self.kind, self.m)
        self.enforce_feasibility \
            = _check_enforce_feasibility(self.enforce_feasibility, self.m)
        if not _is_feasible(self.kind, self.enforce_feasibility, f0):
            raise ValueError("Unfeasible initial point. "
                             "Either set ``enforce_feasibility=False`` or "
                             "choose a new feasible initial point ``x0``.")

        self.isinitialized = True
        return x0

class LinearConstraint:

    def __init__(self, A, kind, enforce_feasibility=False):
        self.A = A
        self.kind = kind
        self.enforce_feasibility = enforce_feasibility
        self.isinitialized = False

    def evaluate_and_initialize(self, x0, sparse_jacobian=None):
        if sparse_jacobian or (sparse_jacobian is None
                               and spc.issparse(self.A)):
            self.A = spc.csr_matrix(self.A)
            self.sparse_jacobian = True
        else:
            if spc.issparse(self.A):
                self.A = self.A.toarray()
            else:
                self.A = np.atleast_2d(self.A)
            self.sparse_jacobian = False

        x0 = np.atleast_1d(x0).astype(float)
        f0 = self.A.dot(x0)
        J0 = self.A

        self.x0 = x0
        self.f0 = f0
        self.J0 = J0
        self.n = x0.size
        self.m = f0.size
        self.kind = _check_kind(self.kind, self.m)
        self.enforce_feasibility \
            = _check_enforce_feasibility(self.enforce_feasibility, self.m)
        if not _is_feasible(self.kind, self.enforce_feasibility, f0):
            raise ValueError("Unfeasible initial point. "
                             "Either set ``enforce_feasibility=False`` or "
                             "choose a new feasible initial point ``x0``.")

        self.isinitialized = True
        return x0

    def to_nonlinear(self):
        if not self.isinitialized:
            raise RuntimeError("Trying to convert uninitialized constraint.")

        def fun(x):
            return self.A.dot(x)

        def jac(x):
            return self.A

        # Build Constraints
        nonlinear = NonlinearConstraint(fun, self.kind, jac, None,
                                        self.enforce_feasibility)
        nonlinear.isinitialized = True
        nonlinear.m = self.m
        nonlinear.n = self.n
        nonlinear.sparse_jacobian = self.sparse_jacobian
        nonlinear.fun = fun
        nonlinear.jac = jac
        nonlinear.hess = None
        nonlinear.x0 = self.x0
        nonlinear.f0 = self.f0
        nonlinear.J0 = self.J0
        return nonlinear

def _check_kind(kind, m):
    if not isinstance(kind, (tuple, list, str)):
        raise ValueError("The parameter `kind` should be a tuple, "
                         " a list, or a string.")
    if isinstance(kind, str):
        kind = (kind,)
    if len(kind) == 0:
        raise ValueError("The parameter `kind` should not be empty.")

    n_args = len(kind)
    keyword = kind[0]
    if keyword not in ("greater", "less", "equals", "interval"):
        raise ValueError("Keyword `%s` not available." % keyword)
    if n_args in (1, 2) and keyword not in ("greater", "less", "equals") \
       or n_args == 3 and keyword not in ("interval"):
        raise ValueError("Invalid `kind` format.")
    if n_args == 1:
        kind = (keyword, 0)

    if keyword in ("greater", "less", "equals"):
        c = np.asarray(kind[1], dtype=float)
        if np.size(c) not in (1, m):
            if keyword == "greater":
                raise ValueError("`lb` has the wrong dimension.")
            if keyword == "less":
                raise ValueError("`ub` has the wrong dimension.")
            if keyword == "equals":
                raise ValueError("`c` has the wrong dimension.")
        c = np.resize(c, m)
        return (keyword, c)
    elif keyword == "interval":
        lb = np.asarray(kind[1], dtype=float)
        if np.size(lb) not in (1, m):
            raise ValueError("`lb` has the wrong dimension.")
        lb = np.resize(lb, m)
        ub = np.asarray(kind[2], dtype=float)
        if np.size(ub) not in (1, m):
            raise ValueError("`ub` has the wrong dimension.")
        ub = np.resize(ub, m)
        if (lb > ub).any():
            raise ValueError("lb[i] > ub[i].")
        return (keyword, lb, ub)

def _check_enforce_feasibility(enforce_feasibility, m):
    if isinstance(enforce_feasibility, bool):
        enforce_feasibility = np.full(m,
                                      enforce_feasibility,
                                      dtype=bool)
    else:
        enforce_feasibility = np.array(enforce_feasibility,
                                       dtype=bool)

        if enforce_feasibility.size != m:
            raise ValueError("The parameter 'enforce_feasibility' "
                             "has the wrong number of elements.")
    return enforce_feasibility

def _is_feasible(kind, enforce_feasibility, f0):
    keyword = kind[0]
    if keyword == "equals":
        lb = np.asarray(kind[1], dtype=float)
        ub = np.asarray(kind[1], dtype=float)
    elif keyword == "greater":
        lb = np.asarray(kind[1], dtype=float)
        ub = np.full_like(lb, np.inf, dtype=float)
    elif keyword == "less":
        ub = np.asarray(kind[1], dtype=float)
        lb = np.full_like(ub, -np.inf, dtype=float)
    elif keyword == "interval":
        lb = np.asarray(kind[1], dtype=float)
        ub = np.asarray(kind[2], dtype=float)
    else:
        raise RuntimeError("Never be here.")

    return ((lb[enforce_feasibility] <= f0[enforce_feasibility]).all()
            and (f0[enforce_feasibility] <= ub[enforce_feasibility]).all())

def default_scaling(x):
    n, = np.shape(x)
    return spc.eye(n)

def projections(A, method=None, orth_tol=1e-12, max_refin=3, tol=1e-15):

    m, n = np.shape(A)

    # The factorization of an empty matrix
    # only works for the sparse representation.
    if m*n == 0:
        A = spc.csc_matrix(A)

    if method is None:
        method = "AugmentedSystem"
    if method not in ("NormalEquation", "AugmentedSystem"):
        raise ValueError("Method not allowed for sparse matrix.")
    if method == "NormalEquation" and not sksparse_available:
        warnings.warn(("Only accepts 'NormalEquation' option when"
                       " scikit-sparse is available. Using "
                       "'AugmentedSystem' option instead."),
                      ImportWarning)
        method = 'AugmentedSystem'

    if method == 'NormalEquation':
        null_space, least_squares, row_space \
            = normal_equation_projections(A, m, n, orth_tol, max_refin, tol)
    elif method == 'AugmentedSystem':
        null_space, least_squares, row_space \
            = augmented_system_projections(A, m, n, orth_tol, max_refin, tol)
    elif method == "QRFactorization":
        null_space, least_squares, row_space \
            = qr_factorization_projections(A, m, n, orth_tol, max_refin, tol)
    elif method == "SVDFactorization":
        null_space, least_squares, row_space \
            = svd_factorization_projections(A, m, n, orth_tol, max_refin, tol)

    Z = LinearOperator((n, n), null_space)
    LS = LinearOperator((m, n), least_squares)
    Y = LinearOperator((n, m), row_space)

    return Z, LS, Y

def orthogonality(A, g):

    # Compute vector norms
    norm_g = np.linalg.norm(g)
    # Compute Frobenius norm of the matrix A
    if spc.issparse(A):
        norm_A = spc.linalg.norm(A, ord='fro')
    else:
        norm_A = np.linalg.norm(A, ord='fro')

    # Check if norms are zero
    if norm_g == 0 or norm_A == 0:
        return 0

    norm_A_g = np.linalg.norm(A.dot(g))
    # Orthogonality measure
    orth = norm_A_g / (norm_A*norm_g)
    return orth

def augmented_system_projections(A, m, n, orth_tol, max_refin, tol):
    K = spc.csc_matrix(spc.bmat([[spc.eye(n), A.T], [A, None]]))
    solve = spc.linalg.factorized(K)
    def null_space(x):
        # v = [x]
        #     [0]
        v = np.hstack([x, np.zeros(m)])
        # lu_sol = [ z ]
        #          [aux]
        lu_sol = solve(v)
        z = lu_sol[:n]

        # Iterative refinement to improve roundoff
        # errors described in [2]_, algorithm 5.2.
        k = 0
        while orthogonality(A, z) > orth_tol:
            if k >= max_refin:
                break
            # new_v = [x] - [I A.T] * [ z ]
            #         [0]   [A  O ]   [aux]
            new_v = v - K.dot(lu_sol)
            # [I A.T] * [delta  z ] = new_v
            # [A  O ]   [delta aux]
            lu_update = solve(new_v)
            #  [ z ] += [delta  z ]
            #  [aux]    [delta aux]
            lu_sol += lu_update
            z = lu_sol[:n]
            k += 1

        return z

    def least_squares(x):
        v = np.hstack([x, np.zeros(m)])
        lu_sol = solve(v)
        return lu_sol[n:m+n]
    def row_space(x):
        v = np.hstack([np.zeros(n), x])
        lu_sol = solve(v)
        return lu_sol[:n]

    return null_space, least_squares, row_space

def to_canonical(constraints):
    if isinstance(constraints, (NonlinearConstraint,
                                LinearConstraint,
                                BoxConstraint,
                                CanonicalConstraint)):
        constraints = [constraints]
    if isinstance(constraints, (list, tuple, np.array)):
        # Converts all constraints to canonical format
        constraints_list = []
        for c in constraints:
            if isinstance(c, CanonicalConstraint):
                constraints_list += [c]
            elif isinstance(c, (NonlinearConstraint)):
                constraints_list += [_nonlinear_to_canonical(c)]
            elif isinstance(c, (LinearConstraint)):
                constraints_list += [_linear_to_canonical(c)]
            elif isinstance(c, (BoxConstraint)):
                constraints_list += [_box_to_canonical(c)]
            else:
                raise ValueError("Unknown Constraint type.")
        # Concatenate constraints
        if len(constraints_list) == 0:
            raise ValueError("Empty list.")
        elif len(constraints_list) == 1:
            constr = constraints_list[0]
        else:
            constr = _concatenate_canonical_constraints(constraints_list)
    else:
        raise ValueError("Unknown Constraint type.")

    return constr

class CanonicalConstraint:

    def __init__(self, n_vars, n_ineq, n_eq,
                 constr, jac, hess, sparse_jacobian,
                 enforce_feasibility,
                 x0, c_ineq0, c_eq0, J_ineq0, J_eq0):
        # Dimensions
        self.n_vars = n_vars
        self.n_ineq = n_ineq
        self.n_eq = n_eq
        self.constr = constr
        self.jac = jac
        self.hess = hess
        self.sparse_jacobian = sparse_jacobian
        self.enforce_feasibility = enforce_feasibility
        self.x0 = x0
        self.c_ineq0 = c_ineq0
        self.c_eq0 = c_eq0
        self.J_ineq0 = J_ineq0
        self.J_eq0 = J_eq0

def _box_to_canonical(box):
    return _linear_to_canonical(box.to_linear())

def _linear_to_canonical(linear):
    return _nonlinear_to_canonical(linear.to_nonlinear())

def _nonlinear_to_canonical(nonlinear):
    eq, ineq, val_eq, val_ineq, sign, fun_len \
        = _parse_constraint(nonlinear.kind)

    n_eq = len(eq)
    n_ineq = len(ineq)
    n_vars = nonlinear.n

    def new_constr(x):
        c = nonlinear.fun(x)
        return _convert_constr(c, n_vars, n_eq, n_ineq,
                               eq, ineq, val_eq, val_ineq,
                               sign)
    c_ineq0, c_eq0 = _convert_constr(nonlinear.f0, n_vars, n_eq, n_ineq,
                                     eq, ineq, val_eq, val_ineq,
                                     sign)

    if nonlinear.sparse_jacobian:
        def new_jac(x):
            J = nonlinear.jac(x)
            return _convert_sparse_jac(J, n_vars, n_eq, n_ineq,
                                       eq, ineq, val_eq, val_ineq,
                                       sign)
        J_ineq0, J_eq0 = _convert_sparse_jac(nonlinear.J0, n_vars, n_eq,
                                             n_ineq, eq, ineq, val_eq,
                                             val_ineq, sign)

    else:
        def new_jac(x):
            J = nonlinear.jac(x)
            return _convert_dense_jac(J, n_vars, n_eq, n_ineq,
                                      eq, ineq, val_eq, val_ineq,
                                      sign)
        J_ineq0, J_eq0 = _convert_dense_jac(nonlinear.J0, n_vars, n_eq,
                                            n_ineq, eq, ineq, val_eq,
                                            val_ineq, sign)

    if nonlinear.hess is None:
        new_hess = None
    else:
        def new_hess(x, v_eq=np.empty(0), v_ineq=np.empty(0)):
            hess = nonlinear.hess
            v = np.zeros(fun_len)
            if len(v_eq) > 0:
                v[eq] += v_eq
            if len(v_ineq) > 0:
                v[ineq[sign == 1]] += v_ineq[sign == 1]
                v[ineq[sign == -1]] -= v_ineq[sign == -1]
            return hess(x, v)

    if n_ineq == 0:
        enforce_feasibility = np.empty(0, dtype=bool)
    else:
        enforce_feasibility = nonlinear.enforce_feasibility[ineq]

    return CanonicalConstraint(n_vars, n_ineq, n_eq,
                               new_constr, new_jac, new_hess,
                               nonlinear.sparse_jacobian,
                               enforce_feasibility, nonlinear.x0,
                               c_ineq0, c_eq0, J_ineq0, J_eq0)

def _parse_constraint(kind):

    if kind[0] == "equals":
        # Read values from input structure
        c = np.asarray(kind[1], dtype=float)
        # Set returns
        eq = np.arange(len(c), dtype=int)
        ineq = np.empty(0, dtype=int)
        val_eq = np.asarray(c)
        val_ineq = np.empty(0)
        sign = np.empty(0)
        fun_len = len(c)
    elif kind[0] in ("greater", "less", "interval"):
        # Constraint type
        if kind[0] == "greater":
            lb = np.asarray(kind[1], dtype=float)
            ub = np.full_like(lb, np.inf, dtype=float)
        elif kind[0] == "less":
            ub = np.asarray(kind[1], dtype=float)
            lb = np.full_like(ub, -np.inf, dtype=float)
        elif kind[0] == "interval":
            lb = np.asarray(kind[1], dtype=float)
            ub = np.asarray(kind[2], dtype=float)
        # Set auxiliar values
        arange = np.arange(len(lb), dtype=int)
        ones = np.ones(len(lb))
        lb_isinf = np.isinf(lb)
        ub_isinf = np.isinf(ub)
        eq_list = (lb == ub) & ~lb_isinf & ~ub_isinf
        # Set returns
        eq = arange[eq_list]
        val_eq = lb[eq_list]
        ineq = np.hstack((arange[~eq_list & ~lb_isinf],
                          arange[~eq_list & ~ub_isinf]))
        val_ineq = np.hstack((lb[~eq_list & ~lb_isinf],
                              ub[~eq_list & ~ub_isinf]))
        sign = np.hstack((-ones[~eq_list & ~lb_isinf],
                          ones[~eq_list & ~ub_isinf]))
        fun_len = len(lb)
    else:
        raise RuntimeError("Never be here.")

    return eq, ineq, val_eq, val_ineq, sign, fun_len

def _convert_constr(c, n_vars, n_eq, n_ineq,
                    eq, ineq, val_eq, val_ineq,
                    sign):

    empty = np.empty((0,))

    c_eq = c[eq] - val_eq if n_eq > 0 else empty
    c_ineq = sign*(c[ineq] - val_ineq) if n_ineq > 0 else empty
    return c_ineq, c_eq

def _convert_sparse_jac(J, n_vars, n_eq, n_ineq,
                        eq, ineq, val_eq, val_ineq,
                        sign):

    empty = spc.csr_matrix(np.empty((0, n_vars)))

    J_eq = J[eq, :] if n_eq > 0 else empty
    if n_ineq > 0:
        D = spc.lil_matrix((n_ineq, n_ineq))
        D.setdiag(sign)
        J_ineq = D*J[ineq, :]
    else:
        J_ineq = empty

    return J_ineq, J_eq

def lagrangian_hessian(constraint, hess):
    def lagr_hess(x, v_eq=np.empty(0), v_ineq=np.empty(0)):
        n = len(x)
        hess_list = []
        if hess is not None:
            hess_list += [hess(x)]
        if constraint.hess is not None:
            hess_list += [constraint.hess(x, v_eq, v_ineq)]

        def matvec(p):
            result = np.zeros_like(p)
            for h in hess_list:
                result += h.dot(p)
            return result

        return spc.linalg.LinearOperator((n, n), matvec)

    return lagr_hess

def modified_dogleg(A, Y, b, trust_radius, lb, ub):

    # Compute minimum norm minimizer of 1/2*|| A x + b ||^2.
    newton_point = -Y.dot(b)
    # Check for interior point
    if inside_box_boundaries(newton_point, lb, ub)  \
       and np.linalg.norm(newton_point) <= trust_radius:
        x = newton_point
        return x

    g = A.T.dot(b)

    A_g = A.dot(g)
    cauchy_point = -np.dot(g, g) / np.dot(A_g, A_g) * g

    origin_point = np.zeros_like(cauchy_point)

    z = cauchy_point
    p = newton_point - cauchy_point
    _, alpha, intersect = box_sphere_intersections(z, p, lb, ub,
                                                   trust_radius)
    if intersect:
        x1 = z + alpha*p
    else:
        z = origin_point
        p = cauchy_point
        _, alpha, _ = box_sphere_intersections(z, p, lb, ub,
                                               trust_radius)
        x1 = z + alpha*p

    z = origin_point
    p = newton_point
    _, alpha, _ = box_sphere_intersections(z, p, lb, ub,
                                           trust_radius)
    x2 = z + alpha*p

    # Return the best solution among x1 and x2.
    if np.linalg.norm(A.dot(x1) + b) < np.linalg.norm(A.dot(x2) + b):
        return x1
    else:
        return x2

def inside_box_boundaries(x, lb, ub):
    return (lb <= x).all() and (x <= ub).all()

def box_sphere_intersections(z, d, lb, ub, trust_radius,
                             entire_line=False,
                             extra_info=False):

    ta_b, tb_b, intersect_b = box_intersections(z, d, lb, ub,
                                                entire_line)
    ta_s, tb_s, intersect_s = sphere_intersections(z, d,
                                                   trust_radius,
                                                   entire_line)
    ta = np.maximum(ta_b, ta_s)
    tb = np.minimum(tb_b, tb_s)
    if intersect_b and intersect_s and ta <= tb:
        intersect = True
    else:
        intersect = False

    if extra_info:
        sphere_info = {'ta': ta_s, 'tb': tb_s, 'intersect': intersect_s}
        box_info = {'ta': ta_b, 'tb': tb_b, 'intersect': intersect_b}
        return ta, tb, intersect, sphere_info, box_info
    else:
        return ta, tb, intersect

def box_intersections(z, d, lb, ub,
                      entire_line=False):

    z = np.asarray(z)
    d = np.asarray(d)
    lb = np.asarray(lb)
    ub = np.asarray(ub)

    if np.linalg.norm(d) == 0:
        return 0, 0, False

    zero_d = (d == 0)
    if (z[zero_d] < lb[zero_d]).any() or (z[zero_d] > ub[zero_d]).any():
        intersect = False
        return 0, 0, intersect
    not_zero_d = np.logical_not(zero_d)
    z = z[not_zero_d]
    d = d[not_zero_d]
    lb = lb[not_zero_d]
    ub = ub[not_zero_d]

    t_lb = (lb-z) / d
    t_ub = (ub-z) / d

    ta = max(np.minimum(t_lb, t_ub))
    tb = min(np.maximum(t_lb, t_ub))

    if ta <= tb:
        intersect = True
    else:
        intersect = False

    if not entire_line:
        if tb < 0 or ta > 1:
            intersect = False
            ta = 0
            tb = 0
        else:
            ta = max(0, ta)
            tb = min(1, tb)

    return ta, tb, intersect

def sphere_intersections(z, d, trust_radius,
                         entire_line=False):

    if np.linalg.norm(d) == 0:
        return 0, 0, False

    if np.isinf(trust_radius):
        if entire_line:
            ta = -np.inf
            tb = np.inf
        else:
            ta = 0
            tb = 1
        intersect = True
        return ta, tb, intersect

    a = np.dot(d, d)
    b = 2 * np.dot(z, d)
    c = np.dot(z, z) - trust_radius**2
    discriminant = b*b - 4*a*c
    if discriminant < 0:
        intersect = False
        return 0, 0, intersect
    sqrt_discriminant = np.sqrt(discriminant)

    aux = b + copysign(sqrt_discriminant, b)
    ta = -aux / (2*a)
    tb = -2*c / aux
    ta, tb = sorted([ta, tb])

    if entire_line:
        intersect = True
    else:
        if tb < 0 or ta > 1:
            intersect = False
            ta = 0
            tb = 0
        else:
            intersect = True
            ta = max(0, ta)
            tb = min(1, tb)

    return ta, tb, intersect

def tr_interior_point(fun, grad, lagr_hess, n_vars, n_ineq, n_eq,
                      constr, jac, x0, fun0, grad0,
                      constr_ineq0, jac_ineq0, constr_eq0,
                      jac_eq0, stop_criteria,
                      enforce_feasibility, xtol, state,
                      initial_barrier_parameter=0.1,
                      initial_tolerance=0.1,
                      initial_penalty=1.0,
                      initial_trust_radius=1.0,
                      return_all=False,
                      factorization_method=None):
    BOUNDARY_PARAMETER = 0.995

    BARRIER_DECAY_RATIO = 0.2

    TRUST_ENLARGEMENT = 5

    if enforce_feasibility is None:
        enforce_feasibility = np.zeros(n_ineq, bool)
    # Initial Values
    state.barrier_parameter = initial_barrier_parameter
    state.tolerance = initial_tolerance
    state.trust_radius = initial_trust_radius
    state.penalty = initial_penalty
    state.optimality = np.inf
    state.constr_violation = np.inf
    # Define initial value for the slack variables
    s0 = np.maximum(-1.5*constr_ineq0, np.ones(n_ineq))
    # Define barrier subproblem
    subprob = BarrierSubproblem(
        x0, s0, fun, grad, lagr_hess, n_vars, n_ineq, n_eq, constr, jac,
        state.barrier_parameter, state.tolerance, enforce_feasibility,
        stop_criteria, xtol, fun0, grad0, constr_ineq0, jac_ineq0,
        constr_eq0, jac_eq0)
    # Define initial parameter for the first iteration.
    z = np.hstack((x0, s0))
    fun0_subprob, constr0_subprob = subprob.fun0, subprob.constr0
    grad0_subprob, jac0_subprob = subprob.grad0, subprob.jac0
    # Define trust region bounds
    trust_lb = np.hstack((np.full(subprob.n_vars, -np.inf),
                          np.full(subprob.n_ineq, -BOUNDARY_PARAMETER)))
    trust_ub = np.full(subprob.n_vars+subprob.n_ineq, np.inf)

    # If there are inequality constraints solve a
    # sequence of barrier problems
    first_barrier_prob = True
    while True:
        if not first_barrier_prob:
            # Update parameters
            state.trust_radius = max(initial_trust_radius,
                                     TRUST_ENLARGEMENT*state.trust_radius)
            # TODO: Use more advanced strategies from [2]_
            # to update this parameters.
            state.barrier_parameter *= BARRIER_DECAY_RATIO
            state.tolerance *= BARRIER_DECAY_RATIO
        first_barrier_prob = False
        # Update Barrier Problem
        subprob.update(state.barrier_parameter, state.tolerance)
        # Solve SQP subproblem
        state = equality_constrained_sqp(
            subprob.function_and_constraints,
            subprob.gradient_and_jacobian,
            subprob.lagrangian_hessian,
            z, fun0_subprob, grad0_subprob,
            constr0_subprob, jac0_subprob, subprob.stop_criteria,
            state, trust_lb, trust_ub, initial_penalty,
            state.trust_radius, subprob.scaling, return_all,
            factorization_method)
        z = state.x
        if stop_criteria(state):
            break
        # Compute initial values for next iteration
        fun0_subprob, constr0_subprob = subprob.function_and_constraints(z)
        grad0_subprob, jac0_subprob = subprob.gradient_and_jacobian(z)

    # Get x and s
    state.x = subprob.get_variables(z)
    state.s = subprob.get_slack(z)
    # Return all
    if return_all:
        allvecs = []
        allslack = []
        for z in state.allvecs:
            allvecs += [subprob.get_variables(z)]
            allslack += [subprob.get_slack(z)]
        state.allvecs = allvecs
        state.allslack = allslack

    return state

class BarrierSubproblem:

    def __init__(self, x0, s0, fun, grad, lagr_hess, n_vars, n_ineq, n_eq,
                 constr, jac, barrier_parameter, tolerance,
                 enforce_feasibility, global_stop_criteria,
                 xtol, fun0, grad0, constr_ineq0, jac_ineq0, constr_eq0,
                 jac_eq0):
        # Store parameters
        self.n_vars = n_vars
        self.x0 = x0
        self.s0 = s0
        self.fun = fun
        self.grad = grad
        self.lagr_hess = lagr_hess
        self.constr = constr
        self.jac = jac
        self.barrier_parameter = barrier_parameter
        self.tolerance = tolerance
        self.n_eq = n_eq
        self.n_ineq = n_ineq
        self.enforce_feasibility = enforce_feasibility
        self.global_stop_criteria = global_stop_criteria
        self.xtol = xtol
        self.fun0 = self._compute_function(fun0, constr_ineq0, s0)
        self.grad0 = self._compute_gradient(grad0)
        self.constr0 = self._compute_constr(constr_ineq0, constr_eq0, s0)
        self.jac0 = self._compute_jacobian(jac_eq0, jac_ineq0, s0)

    def update(self, barrier_parameter, tolerance):
        self.barrier_parameter = barrier_parameter
        self.tolerance = tolerance

    def get_slack(self, z):
        return z[self.n_vars:self.n_vars+self.n_ineq]

    def get_variables(self, z):
        return z[:self.n_vars]

    def function_and_constraints(self, z):

        # Get variables and slack variables
        x = self.get_variables(z)
        s = self.get_slack(z)
        # Compute function and constraints
        f = self.fun(x)
        c_ineq, c_eq = self.constr(x)
        # Return objective function and constraints
        return (self._compute_function(f, c_ineq, s),
                self._compute_constr(c_ineq, c_eq, s))

    def _compute_function(self, f, c_ineq, s):
        # Use technique from Nocedal and Wright book, ref [3]_, p.576,
        # to guarantee constraints from `enforce_feasibility`
        # stay feasible along iterations.
        s[self.enforce_feasibility] = -c_ineq[self.enforce_feasibility]
        log_s = [np.log(s_i) if s_i > 0 else -np.inf for s_i in s]
        # Compute barrier objective function
        return f - self.barrier_parameter*np.sum(log_s)

    def _compute_constr(self, c_ineq, c_eq, s):
        # Compute barrier constraint
        return np.hstack((c_eq,
                          c_ineq + s))

    def scaling(self, z):

        s = self.get_slack(z)
        diag_elements = np.hstack((np.ones(self.n_vars), s))

        # Diagonal Matrix
        def matvec(vec):
            return diag_elements*vec
        return LinearOperator((self.n_vars+self.n_ineq,
                               self.n_vars+self.n_ineq),
                              matvec)

    def gradient_and_jacobian(self, z):

        # Get variables and slack variables
        x = self.get_variables(z)
        s = self.get_slack(z)
        # Compute first derivatives
        g = self.grad(x)
        J_ineq, J_eq = self.jac(x)
        # Return gradient and jacobian
        return (self._compute_gradient(g),
                self._compute_jacobian(J_eq, J_ineq, s))

    def _compute_gradient(self, g):
        return np.hstack((g, -self.barrier_parameter*np.ones(self.n_ineq)))

    def _compute_jacobian(self, J_eq, J_ineq, s):
        if self.n_ineq == 0:
            return J_eq
        else:
            if spc.issparse(J_eq) or spc.issparse(J_ineq):
                # It is expected that J_eq and J_ineq
                # are already `csr_matrix` because of
                # the way ``BoxConstraint``, ``NonlinearConstraint``
                # and ``LinearConstraint`` are defined.
                J_eq = spc.csr_matrix(J_eq)
                J_ineq = spc.csr_matrix(J_ineq)
                return self._assemble_sparse_jacobian(J_eq, J_ineq, s)
            else:
                S = np.diag(s)
                zeros = np.zeros((self.n_eq, self.n_ineq))
                # Convert to matrix
                if spc.issparse(J_ineq):
                    J_ineq = J_ineq.toarray()
                if spc.issparse(J_eq):
                    J_eq = J_eq.toarray()
                # Concatenate matrices
                return np.asarray(np.bmat([[J_eq, zeros],
                                           [J_ineq, S]]))

    def _assemble_sparse_jacobian(self, J_eq, J_ineq, s):

        n_vars, n_ineq, n_eq = self.n_vars, self.n_ineq, self.n_eq
        J_aux = spc.vstack([J_eq, J_ineq], "csr")
        indptr, indices, data = J_aux.indptr, J_aux.indices, J_aux.data
        new_indptr = indptr + np.hstack((np.zeros(n_eq, dtype=int),
                                         np.arange(n_ineq+1, dtype=int)))
        size = indices.size+n_ineq
        new_indices = np.empty(size)
        new_data = np.empty(size)
        mask = np.full(size, False, bool)
        mask[new_indptr[-n_ineq:]-1] = True
        new_indices[mask] = n_vars+np.arange(n_ineq)
        new_indices[~mask] = indices
        new_data[mask] = s
        new_data[~mask] = data
        J = spc.csr_matrix((new_data, new_indices, new_indptr),
                           (n_eq + n_ineq, n_vars + n_ineq))
        return J

    def lagrangian_hessian_x(self, z, v):
        x = self.get_variables(z)
        # Get lagrange multipliers relatated to nonlinear equality constraints
        v_eq = v[:self.n_eq]
        # Get lagrange multipliers relatated to nonlinear ineq. constraints
        v_ineq = v[self.n_eq:self.n_eq+self.n_ineq]
        lagr_hess = self.lagr_hess
        return lagr_hess(x, v_eq, v_ineq)

    def lagrangian_hessian_s(self, z, v):
        s = self.get_slack(z)
        primal = self.barrier_parameter
        primal_dual = v[-self.n_ineq:]*s
        return np.where(v[-self.n_ineq:] > 0, primal_dual, primal)

    def lagrangian_hessian(self, z, v):

        Hx = self.lagrangian_hessian_x(z, v)
        if self.n_ineq > 0:
            S_Hs_S = self.lagrangian_hessian_s(z, v)

        def matvec(vec):
            vec_x = self.get_variables(vec)
            vec_s = self.get_slack(vec)
            if self.n_ineq > 0:
                return np.hstack((Hx.dot(vec_x), S_Hs_S*vec_s))
            else:
                return Hx.dot(vec_x)
        return LinearOperator((self.n_vars+self.n_ineq,
                               self.n_vars+self.n_ineq),
                              matvec)

    def stop_criteria(self, state):

        return (state.optimality < self.tolerance
                and state.constr_violation < self.tolerance) \
            or self.global_stop_criteria(state) \
            or state.trust_radius < self.xtol

def equality_constrained_sqp(fun_and_constr, grad_and_jac, lagr_hess,
                             x0, fun0, grad0, constr0,
                             jac0, stop_criteria, state,
                             trust_lb=None,
                             trust_ub=None,
                             initial_penalty=1.0,
                             initial_trust_radius=1.0,
                             scaling=default_scaling,
                             return_all=False,
                             factorization_method=None):

    PENALTY_FACTOR = 0.3  # Rho from formula (3.51), reference [2]_, p.891.
    LARGE_REDUCTION_RATIO = 0.9
    INTERMEDIARY_REDUCTION_RATIO = 0.3
    SUFFICIENT_REDUCTION_RATIO = 1e-8  # Eta from reference [2]_, p.892.
    TRUST_ENLARGEMENT_FACTOR_L = 7.0
    TRUST_ENLARGEMENT_FACTOR_S = 2.0
    MAX_TRUST_REDUCTION = 0.5
    MIN_TRUST_REDUCTION = 0.1
    SOC_THRESHOLD = 0.1
    TR_FACTOR = 0.8  # Zeta from formula (3.21), reference [2]_, p.885.
    BOX_FACTOR = 0.5

    n, = np.shape(x0)  # Number of parameters

    # Set default lower and upper bounds.
    if trust_lb is None:
        trust_lb = np.full(n, -np.inf)
    if trust_ub is None:
        trust_ub = np.full(n, np.inf)

    # Initial values
    x = np.copy(x0)
    trust_radius = initial_trust_radius
    penalty = initial_penalty
    # Compute Values
    f = fun0
    c = grad0
    b = constr0
    A = jac0
    S = scaling(x)
    # Get projections
    Z, LS, Y = projections(A, factorization_method)
    # Compute least-square lagrange multipliers
    v = -LS.dot(c)

    # Update state parameters
    state.optimality = np.linalg.norm(c + A.T.dot(v), np.inf)
    state.constr_violation = np.linalg.norm(b, np.inf) if len(b) > 0 else 0
    state.niter += 1
    state.x = x
    state.v = v
    state.fun = f
    state.grad = c
    state.constr = b
    state.jac = A
    state.trust_radius = trust_radius
    state.penalty = penalty
    if return_all:
        state.allvecs += [np.copy(x)]
        state.allmult += [np.copy(v)]

    compute_hess = True
    while not stop_criteria(state):
        # Compute Lagrangian Hessian
        if compute_hess:
            H = lagr_hess(x, v)
            state.nhev += 1

        dn = modified_dogleg(A, Y, b,
                             TR_FACTOR*trust_radius,
                             BOX_FACTOR*trust_lb,
                             BOX_FACTOR*trust_ub)

        c_t = H.dot(dn) + c
        b_t = np.zeros_like(b)
        trust_radius_t = np.sqrt(trust_radius**2 - np.linalg.norm(dn)**2)
        lb_t = trust_lb - dn
        ub_t = trust_ub - dn
        dt, info_cg = projected_cg(H, c_t, Z, Y, b_t,
                                   trust_radius_t,
                                   lb_t, ub_t)

        # Compute update (normal + tangential steps).
        d = dn + dt

        quadratic_model = 1/2*(H.dot(d)).dot(d) + c.T.dot(d)
        linearized_constr = A.dot(d)+b
        vpred = np.linalg.norm(b) - np.linalg.norm(linearized_constr)
        vpred = max(1e-16, vpred)
        previous_penalty = penalty
        if quadratic_model > 0:
            new_penalty = quadratic_model / ((1-PENALTY_FACTOR)*vpred)
            penalty = max(penalty, new_penalty)
        predicted_reduction = -quadratic_model + penalty*vpred

        merit_function = f + penalty*np.linalg.norm(b)
        x_next = x + S.dot(d)
        f_next, b_next = fun_and_constr(x_next)
        state.nfev += 1
        state.ncev += 1
        merit_function_next = f_next + penalty*np.linalg.norm(b_next)
        actual_reduction = merit_function - merit_function_next
        reduction_ratio = actual_reduction / predicted_reduction

        if reduction_ratio < SUFFICIENT_REDUCTION_RATIO and \
           np.linalg.norm(dn) <= SOC_THRESHOLD * np.linalg.norm(dt):
            y = -Y.dot(b_next)
            _, t, intersect = box_intersections(d, y, trust_lb, trust_ub)
            x_soc = x + S.dot(d + t*y)
            f_soc, b_soc = fun_and_constr(x_soc)
            # Increment funcion evaluation counter
            state.nfev += 1
            state.ncev += 1
            merit_function_soc = f_soc + penalty*np.linalg.norm(b_soc)
            actual_reduction_soc = merit_function - merit_function_soc
            # Recompute reduction ratio
            reduction_ratio_soc = actual_reduction_soc / predicted_reduction
            if intersect and reduction_ratio_soc >= SUFFICIENT_REDUCTION_RATIO:
                x_next = x_soc
                f_next = f_soc
                b_next = b_soc
                reduction_ratio = reduction_ratio_soc

        if reduction_ratio >= LARGE_REDUCTION_RATIO:
            trust_radius = max(TRUST_ENLARGEMENT_FACTOR_L * np.linalg.norm(d),
                               trust_radius)
        elif reduction_ratio >= INTERMEDIARY_REDUCTION_RATIO:
            trust_radius = max(TRUST_ENLARGEMENT_FACTOR_S * np.linalg.norm(d),
                               trust_radius)
        elif reduction_ratio < SUFFICIENT_REDUCTION_RATIO:
                trust_reduction \
                    = (1-SUFFICIENT_REDUCTION_RATIO)/(1-reduction_ratio)
                new_trust_radius = trust_reduction * np.linalg.norm(d)
                if new_trust_radius >= MAX_TRUST_REDUCTION * trust_radius:
                    trust_radius *= MAX_TRUST_REDUCTION
                elif new_trust_radius >= MIN_TRUST_REDUCTION * trust_radius:
                    trust_radius = new_trust_radius
                else:
                    trust_radius *= MIN_TRUST_REDUCTION

        state.niter += 1
        if reduction_ratio >= SUFFICIENT_REDUCTION_RATIO:
            x = x_next
            f, b = f_next, b_next
            c, A = grad_and_jac(x)
            S = scaling(x)
            # Increment funcion evaluation counter
            state.ngev += 1
            state.njev += 1
            # Get projections
            Z, LS, Y = projections(A)
            # Compute least-square lagrange multipliers
            v = -LS.dot(c)
            # Set Flag
            compute_hess = True
            # Store state
            state.x = x
            state.v = v
            state.fun = f
            state.grad = c
            state.constr = b
            state.jac = A
            # Otimality values
            state.optimality = np.linalg.norm(c + A.T.dot(v), np.inf)
            state.constr_violation = np.linalg.norm(b, np.inf) if len(b) > 0 else 0
        else:
            penalty = previous_penalty
            compute_hess = False
        # Store values
        state.trust_radius = trust_radius
        state.penalty = penalty
        state.cg_niter += info_cg["niter"]
        state.cg_info = info_cg
        if return_all:
            state.allvecs.append(np.copy(x))
            state.allmult.append(np.copy(v))

    return state

def projected_cg(H, c, Z, Y, b, trust_radius=np.inf,
                 lb=None, ub=None, tol=None,
                 max_iter=None, max_infeasible_iter=None,
                 return_all=False):
    CLOSE_TO_ZERO = 1e-25

    n, = np.shape(c)  # Number of parameters
    m, = np.shape(b)  # Number of constraints

    # Initial Values
    x = Y.dot(-b)
    r = Z.dot(H.dot(x) + c)
    g = Z.dot(r)
    p = -g

    # Store ``x`` value
    if return_all:
        allvecs = [x]
    # Values for the first iteration
    H_p = H.dot(p)
    rt_g = np.linalg.norm(g)**2  # g.T g = r.T Z g = r.T g (ref [1]_ p.1389)

    # If x > trust-region the problem does not have a solution.
    tr_distance = trust_radius - np.linalg.norm(x)
    if tr_distance < 0:
        raise ValueError("Trust region problem does not have a solution.")
    # If x == trust_radius, then x is the solution
    # to the optimization problem, since x is the
    # minimum norm solution to Ax=b.
    elif tr_distance < CLOSE_TO_ZERO:
        info = {'niter': 0, 'stop_cond': 2, 'hits_boundary': True}
        if return_all:
            allvecs.append(x)
            info['allvecs'] = allvecs
        return x, info

    # Set default tolerance
    if tol is None:
        tol = max(min(0.01 * np.sqrt(rt_g), 0.1 * rt_g), CLOSE_TO_ZERO)
    # Set default lower and upper bounds
    if lb is None:
        lb = np.full(n, -np.inf)
    if ub is None:
        ub = np.full(n, np.inf)
    # Set maximum iterations
    if max_iter is None:
        max_iter = n-m
    max_iter = min(max_iter, n-m)
    # Set maximum infeasible iterations
    if max_infeasible_iter is None:
        max_infeasible_iter = n-m

    hits_boundary = False
    stop_cond = 1
    counter = 0
    last_feasible_x = np.empty_like(x)
    k = 0
    for i in range(max_iter):
        # Stop criteria - Tolerance : r.T g < tol
        if rt_g < tol:
            stop_cond = 4
            break
        k += 1
        # Compute curvature
        pt_H_p = H_p.dot(p)
        # Stop criteria - Negative curvature
        if pt_H_p <= 0:
            if np.isinf(trust_radius):
                    raise ValueError("Negative curvature not "
                                     "allowed for unrestrited "
                                     "problems.")
            else:
                # Find intersection with constraints
                _, alpha, intersect = box_sphere_intersections(
                    x, p, lb, ub, trust_radius, entire_line=True)
                # Update solution
                if intersect:
                    x = x + alpha*p
                # Reinforce variables are inside box constraints.
                # This is only necessary because of roundoff errors.
                x = reinforce_box_boundaries(x, lb, ub)
                # Atribute information
                stop_cond = 3
                hits_boundary = True
                break

        # Get next step
        alpha = rt_g / pt_H_p
        x_next = x + alpha*p

        # Stop criteria - Hits boundary
        if np.linalg.norm(x_next) >= trust_radius:
            # Find intersection with box constraints
            _, theta, intersect = box_sphere_intersections(x, alpha*p, lb, ub,
                                                           trust_radius)
            # Update solution
            if intersect:
                x = x + theta*alpha*p
            # Reinforce variables are inside box constraints.
            # This is only necessary because of roundoff errors.
            x = reinforce_box_boundaries(x, lb, ub)
            # Atribute information
            stop_cond = 2
            hits_boundary = True
            break

        # Check if ``x`` is inside the box and start counter if it is not.
        if inside_box_boundaries(x_next, lb, ub):
            counter = 0
        else:
            counter += 1
        # Whenever outside box constraints keep looking for intersections.
        if counter > 0:
            _, theta, intersect = box_sphere_intersections(x, alpha*p, lb, ub,
                                                           trust_radius)
            if intersect:
                last_feasible_x = x + theta*alpha*p
                # Reinforce variables are inside box constraints.
                # This is only necessary because of roundoff errors.
                last_feasible_x = reinforce_box_boundaries(last_feasible_x,
                                                           lb, ub)
                counter = 0
        # Stop after too many infeasible (regarding box constraints) iteration.
        if counter > max_infeasible_iter:
            break
        # Store ``x_next`` value
        if return_all:
            allvecs.append(x_next)

        # Update residual
        r_next = r + alpha*H_p
        # Project residual g+ = Z r+
        g_next = Z.dot(r_next)
        # Compute conjugate direction step d
        rt_g_next = np.linalg.norm(g_next)**2  # g.T g = r.T g (ref [1]_ p.1389)
        beta = rt_g_next / rt_g
        p = - g_next + beta*p
        # Prepare for next iteration
        x = x_next
        g = g_next
        r = g_next
        rt_g = np.linalg.norm(g)**2  # g.T g = r.T Z g = r.T g (ref [1]_ p.1389)
        H_p = H.dot(p)

    if not inside_box_boundaries(x, lb, ub):
        x = last_feasible_x
        hits_boundary = True
    info = {'niter': k, 'stop_cond': stop_cond,
            'hits_boundary': hits_boundary}
    if return_all:
        info['allvecs'] = allvecs
    return x, info

def reinforce_box_boundaries(x, lb, ub):
    return np.minimum(np.maximum(x, lb), ub)

def minimize_constrained(fun, x0, grad, hess='2-point', constraints=(),
                         method=None, xtol=1e-8, gtol=1e-8,
                         sparse_jacobian=None, options={},
                         callback=None, max_iter=1000,
                         verbose=0):
    # Initial value
    x0 = np.atleast_1d(x0).astype(float)
    n_vars = np.size(x0)

    # Evaluate initial point
    f0 = fun(x0)
    g0 = np.atleast_1d(grad(x0))

    def grad_wrapped(x):
        return np.atleast_1d(grad(x))

    H0 = hess(x0)
    H0 = np.atleast_2d(np.asarray(H0))
    def hess_wrapped(x):
        return np.atleast_2d(np.asarray(hess(x)))

    constraints = [constraints]
    copied_constraints = [deepcopy(constr) for constr in constraints]
    for constr in copied_constraints:
        x0 = constr.evaluate_and_initialize(x0, sparse_jacobian)
    if len(copied_constraints) == 0:
        constr = empty_canonical_constraint(x0, n_vars, sparse_jacobian)
    else:
        constr = to_canonical(copied_constraints)

    lagr_hess = lagrangian_hessian(constr, hess_wrapped)

    state = OptimizeResult(niter=0, nfev=1, ngev=1,
                           ncev=1, njev=1, nhev=0,
                           cg_niter=0, cg_info={})
    return_all = options.get("return_all", False)
    if return_all:
        state.allvecs = []
        state.allmult = []

    method = 'tr_interior_point'

    def stop_criteria(state):
        barrier_tol = options.get("barrier_tol", 1e-8)
        if verbose >= 2:
            ip_printer.print_problem_iter(state.niter,
                                          state.nfev,
                                          state.cg_niter,
                                          state.barrier_parameter,
                                          state.trust_radius,
                                          state.penalty,
                                          state.optimality,
                                          state.constr_violation)
        state.status = None
        if (callback is not None) and callback(state):
            state.status = 3
        elif state.optimality < gtol and state.constr_violation < gtol:
            state.status = 1
        elif (state.trust_radius < xtol
              and state.barrier_parameter < barrier_tol):
            state.status = 2
        elif state.niter > max_iter:
            state.status = 0
        return state.status in (0, 1, 2, 3)

    result = tr_interior_point(
        fun, grad_wrapped, lagr_hess,
        n_vars, constr.n_ineq, constr.n_eq,
        constr.constr, constr.jac,
        x0, f0, g0, constr.c_ineq0, constr.J_ineq0,
        constr.c_eq0, constr.J_eq0, stop_criteria,
        constr.enforce_feasibility,
        xtol, state, **options)

    result.method = method
    result.message = 'done'

    return result

class Rosenbrock:
    def __init__(self, n=2, random_state=0):
        rng = np.random.RandomState(random_state)
        self.x0 = rng.uniform(-1, 1, n)
        self.x_opt = np.ones(n)

    def fun(self, x):
        x = np.asarray(x)
        r = np.sum(100.0 * (x[1:] - x[:-1]**2.0)**2.0 + (1 - x[:-1])**2.0,
                   axis=0)
        return r

    def grad(self, x):
        x = np.asarray(x)
        xm = x[1:-1]
        xm_m1 = x[:-2]
        xm_p1 = x[2:]
        der = np.zeros_like(x)
        der[1:-1] = (200 * (xm - xm_m1**2) -
                     400 * (xm_p1 - xm**2) * xm - 2 * (1 - xm))
        der[0] = -400 * x[0] * (x[1] - x[0]**2) - 2 * (1 - x[0])
        der[-1] = 200 * (x[-1] - x[-2]**2)
        return der

    def hess(self, x):
        x = np.atleast_1d(x)
        H = np.diag(-400 * x[:-1], 1) - np.diag(400 * x[:-1], -1)
        diagonal = np.zeros(len(x), dtype=x.dtype)
        diagonal[0] = 1200 * x[0]**2 - 400 * x[1] + 2
        diagonal[-1] = 200
        diagonal[1:-1] = 202 + 1200 * x[1:-1]**2 - 400 * x[2:]
        H = H + np.diag(diagonal)
        return H

    @property
    def constr(self):
        lb = np.array([0, 0])
        ub = np.array([0.5, 0.5])
        kind = ("interval", lb, ub)
        box = BoxConstraint(kind)
        return box

prob = Rosenbrock()
result = minimize_constrained(prob.fun, prob.x0,
                              prob.grad, prob.hess,
                              prob.constr)

print (result)


from django.conf.urls import patterns, include, url
from django.contrib import admin

urlpatterns = patterns('',

	url(r'^$', 'band.views.home', name='home'),
	url(r'^bio/', 'band.views.bio', name='bio'),
    url(r'^disc/', 'band.views.disc', name='disc'),
    url(r'^tour/', 'band.views.tour', name='tour'),
    # Examples:
    # url(r'^$', 'helloworld.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),

    url(r'^admin/', include(admin.site.urls)),
)


# _*_ coding: utf-8 _*_

import json

data = {
	"operator": "ct",
	"vendor" : "xx",
	"timestamp" : "20190705",
	"data" : {
		"id" : 1,
		"age" : 18,
		"city" : "shanghai",
		"top" : ["com","cn"]
	}
}

print(type(data))
print(data)

with open('data.json', 'w') as f:
	json.dump(data, f, sort_keys=True, indent=4, separators=(',', ': '))

with open("data.json",'r') as f:
	d = json.load(f)
	print(type(d),type(d['data']['age']))
	print(d)


import random

from discord import Message
from discord.ext import commands

class Alea(commands.Cog, name="Juste Prix", description="Juste Prix le jeux"):
    def __init__(self, bot: commands.Bot) -> None:
        super().__init__()
        self.bot = bot
        self.juste_prix = -1

    #      ██╗██╗   ██╗███████╗████████╗███████╗    ██████╗ ██████╗ ██╗██╗  ██╗
    #      ██║██║   ██║██╔════╝╚══██╔══╝██╔════╝    ██╔══██╗██╔══██╗██║╚██╗██╔╝
    #      ██║██║   ██║███████╗   ██║   █████╗      ██████╔╝██████╔╝██║ ╚███╔╝
    # ██   ██║██║   ██║╚════██║   ██║   ██╔══╝      ██╔═══╝ ██╔══██╗██║ ██╔██╗
    # ╚█████╔╝╚██████╔╝███████║   ██║   ███████╗    ██║     ██║  ██║██║██╔╝ ██╗
    #  ╚════╝  ╚═════╝ ╚══════╝   ╚═╝   ╚══════╝    ╚═╝     ╚═╝  ╚═╝╚═╝╚═╝  ╚═╝
    @commands.command()
    async def alea(self, ctx: commands.Context, stop=""):
        """Pick a random number between 0 and 1000,
        i will tell to the next message if they are close to it.
        if you specify any argument after this command, it will stop the alea
        """
        if stop != "":
            self.juste_prix = -1
        else:
            self.juste_prix = random.randint(0, 1000)
        await ctx.channel.send("Just Prix started")

    @commands.Cog.listener("on_message")
    async def wait_message_juste_prix(self, message: Message):
        if self.juste_prix != -1:
            if int(message.content) < self.juste_prix:
                await message.channel.send("plus haut")
            if int(message.content) > self.juste_prix:
                await message.channel.send("plus bas")
            if int(message.content) == self.juste_prix:
                await message.channel.send("c'est ça !")
                self.juste_prix = -1

async def setup(bot: commands.Bot):
    await bot.add_cog(Alea(bot))
    print(f"cog loaded: {__file__}")

async def teardown(bot: commands.Bot):
    print(f"cog unloaded: {bot}{__file__}")


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from compas.utilities import pairwise

from compas.datastructures import mesh_flip_cycles
from compas.datastructures import Mesh
from compas_rhino.artists import MeshArtist

mesh = Mesh.from_json('cablenet.json')

# make lookup dicts for vertex normals of the cablenet
key_normal = {key: mesh.vertex_normal(key) for key in mesh.vertices()}

# ==============================================================================
# Intrados and extrados
# ==============================================================================

idos = mesh.copy()
edos = idos.copy()

# offset the intrados from the cablenet
# by 2cm in the direction of the corresponding cablenet vertex normal
for key, attr in idos.vertices(True):
    nx, ny, nz = key_normal[key]

    attr['x'] += 0.02 * nx
    attr['y'] += 0.02 * ny
    attr['z'] += 0.02 * nz

# offset the intrados from the intrados
# by 2cm in the direction of the corresponding cablenet vertex normal
for key, attr in edos.vertices(True):
    nx, ny, nz = key_normal[key]

    attr['x'] += 0.06 * nx
    attr['y'] += 0.06 * ny
    attr['z'] += 0.06 * nz

# ==============================================================================
# Concrete volume
# ==============================================================================

volume = idos.copy()
volume.name = 'concrete1'

# flip its cycles to make the bottom normals point downwards
mesh_flip_cycles(volume)

# set the key offset
dkey = volume._max_int_key + 1

# add the vertices of the extrados
for key, attr in edos.vertices(True):
    volume.add_vertex(key=key + dkey, **attr)

# add the faces of the extrados
for fkey in edos.faces():
    vertices = edos.face_vertices(fkey)
    vertices = [key + dkey for key in vertices]

    volume.add_face(vertices)

# construct a polygon of boundary vertices
boundary = edos.vertices_on_boundary(ordered=True)
boundary.append(boundary[0])

# add the "side" faces
for a, b in pairwise(boundary):
    volume.add_face([b, a, a + dkey, b + dkey])

# ==============================================================================
# Export and visualisation
# ==============================================================================

# export
volume.to_json('concrete1.json')

# visualize
artist = MeshArtist(volume, layer="Concrete1")
artist.clear_layer()
artist.draw_mesh(color=(0, 0, 255))


# -*- coding: UTF-8 -*-
"""
title: 可互换矩形的组数
You are given n rectangles represented by a 0-indexed 2D integer array rectangles, where rectangles[i] = [widthi, heighti] denotes the width and height of the ith rectangle.
Two rectangles i and j (i < j) are considered interchangeable if they have the same width-to-height ratio. More formally, two rectangles are interchangeable if widthi/heighti == widthj/heightj (using decimal division, not integer division).
Return the number of pairs of interchangeable rectangles in rectangles.

Example 1:
Input: rectangles = [[4,8],[3,6],[10,20],[15,30]]
Output: 6
Explanation: The following are the interchangeable pairs of rectangles by index (0-indexed):
- Rectangle 0 with rectangle 1: 4/8 == 3/6.
- Rectangle 0 with rectangle 2: 4/8 == 10/20.
- Rectangle 0 with rectangle 3: 4/8 == 15/30.
- Rectangle 1 with rectangle 2: 3/6 == 10/20.
- Rectangle 1 with rectangle 3: 3/6 == 15/30.
- Rectangle 2 with rectangle 3: 10/20 == 15/30.

Example 2:
Input: rectangles = [[4,5],[7,8]]
Output: 0
Explanation: There are no interchangeable pairs of rectangles.

Constraints:
n == rectangles.length
1 <= n <= 10^5
rectangles[i].length == 2
1 <= widthi, heighti <= 10^5
"""
from collections import defaultdict
from typing import List

class Solution:
    def interchangeableRectangles(self, rectangles: List[List[int]]) -> int:
        def gcd(a: int, b: int) -> int:
            while b:
                a, b = b, a % b
            return a

        ratio2cnt = defaultdict(int)
        for w, h in rectangles:
            g = gcd(w, h)
            ratio2cnt[(w // g, h // g)] += 1
        res = 0
        for cnt in ratio2cnt.values():
            # 组合数 cnt个矩形中任选2个进行互换
            res += cnt * (cnt - 1) // 2
        return res

    def interchangeableRectangles_2(self, rectangles: List[List[int]]) -> int:
        def gcd(a: int, b: int) -> int:
            while b:
                a, b = b, a % b
            return a

        ratio2cnt = defaultdict(int)
        res = 0
        for w, h in rectangles:
            g = gcd(w, h)
            # cnt * (cnt - 1) // 2 = 0 + 1 + 2 + …… + cnt-1 = sum(range(cnt))
            res += ratio2cnt[(w // g, h // g)]
            ratio2cnt[(w // g, h // g)] += 1
        return res

if __name__ == '__main__':
    print(Solution().interchangeableRectangles_2(rectangles=[[4, 8], [3, 6], [10, 20], [15, 30]]))


from typing import Sequence, Tuple

import numpy as np
import torch
from scipy.signal import resample
from torch import Tensor
from wfdb import Record

class Resample:
  def __init__(self, fs: int):
    assert fs > 0
    self.fs = fs

  def __call__(self, record: Record) -> Record:
    if record.fs != self.fs:
      size, _ = record.p_signal.shape
      resampled_length = int(size * self.fs / record.fs)
      p_signal = resample(record.p_signal, num=resampled_length)
      return Record(
        p_signal=p_signal,
        record_name=record.record_name,
        fs=self.fs,
        sig_name=record.sig_name,
        comments=record.comments)
    return record

class CenterCrop:
  def __init__(self, size: int):
    assert size > 0
    self.size = size

  def __call__(self, record: Record) -> Record:
    size, _ = record.p_signal.shape
    if size > self.size:
      offset = (size - self.size) // 2
      p_signal = record.p_signal[offset:offset + self.size]
      return Record(
        p_signal=p_signal,
        record_name=record.record_name,
        fs=record.fs,
        sig_name=record.sig_name,
        comments=record.comments)
    return record

class MaskedPad:
  def __init__(self, size):
    assert size > 0
    self.size = size

  def __call__(self, record: Record) -> Tuple[Record, Tensor]:
    size, num_channels = record.p_signal.shape
    if size < self.size:
      sig_start = self.size - size
      p_signal = np.zeros_like(record.p_signal, shape=(self.size, num_channels))
      p_signal[sig_start:] = record.p_signal
      mask = torch.zeros(self.size)
      mask[sig_start:] = 1.
    else:
      p_signal = record.p_signal
      mask = torch.ones(size)
    record = Record(
      p_signal=p_signal,
      record_name=record.record_name,
      fs=record.fs,
      sig_name=record.sig_name,
      comments=record.comments)
    return record, mask

class Normalize:
  def __call__(self, record: Record) -> Record:
    mean = record.p_signal.mean(axis=0)
    std = record.p_signal.std(axis=0)
    p_signal = (record.p_signal - mean) / (std + 1e-9)
    return Record(
      p_signal=p_signal,
      record_name=record.record_name,
      fs=record.fs,
      sig_name=record.sig_name,
      comments=record.comments)

class SelectLeads:
  def __init__(self, leads: Sequence[str]):
    assert len(set(leads)) == len(leads)  # all leads are unique
    self.leads = leads

  def __call__(self, record: Record) -> Record:
    lead_indices = []
    for selected_lead in self.leads:
      lead_indices.append(record.sig_name.index(selected_lead))
    p_signal = record.p_signal[:, lead_indices]
    sig_name = list(self.leads)
    return Record(
      p_signal=p_signal,
      record_name=record.record_name,
      fs=record.fs,
      sig_name=sig_name,
      comments=record.comments)

class ReplaceNaN:
  """Replace nan with the mean computed over an entire lead."""
  def __call__(self, record: Record) -> Record:
    if np.isnan(record.p_signal).any():
      _, num_channels = record.p_signal.shape
      mean = np.nanmean(record.p_signal, axis=0)
      p_signal = record.p_signal.copy()
      for i in range(num_channels):
        np.nan_to_num(p_signal[:, i], copy=False, nan=mean[i])
      return Record(
        p_signal=p_signal,
        record_name=record.record_name,
        fs=record.fs,
        sig_name=record.sig_name,
        comments=record.comments)
    return record

class ToTensor:
  def __init__(self, channels_first: bool = False):
    self.channels_first = channels_first

  def __call__(self, record: Record) -> Tensor:
    p_signal = torch.from_numpy(record.p_signal).float()
    if self.channels_first:
      p_signal = p_signal.transpose(0, 1)
    return p_signal


"""
This script allows testing the back-end API by allowing data to be sent as JSON and pretty-print the JSON response
"""
import argparse
import json
import os.path

import requests

URL = 'http://localhost:8080'

METHOD_CHOICES = ('GET', 'POST', 'PUT', 'DELETE')

parser = argparse.ArgumentParser(description='Sends API requests to the back-end API to visualise the JSON response '
                                             'data')
parser.add_argument('-u', '--url', default=URL, required=False, help='The base URL where the API is hosted at')
parser.add_argument('-f', '--file', default=None, required=False, help='A request JSON file')
parser.add_argument('-e', '--endpoint', default=None, required=False, help='The endpoint to send the request to')
parser.add_argument('-d', '--data', default=None, help='The JSON data to send to the API. Can be a JSON String or a JSON file')
parser.add_argument('-H', '--headers', default=None,
                    help='Headers as a JSON-like String {"Content-Type": "application/json"}')
parser.add_argument('-m', '--method', default='GET', help='The method to send the request with',
                    choices=METHOD_CHOICES)

methods = {
    'GET': requests.get,
    'POST': requests.post,
    'PUT': requests.put,
    'DELETE': requests.delete
}

args = parser.parse_args()

FILE = args.file
ENDPOINT = args.endpoint
DATA = None
HEADERS = None

if FILE and ENDPOINT:
    parser.error('You can only provide a request file using -f or an endpoint using -e. Note that the arguments other'
                 ' than -h or -f are only valid when -e is used')
elif not FILE and not ENDPOINT:
    parser.error('You must specify either -f or -e')
elif FILE:
    if os.path.exists(FILE):
        with open(FILE, 'r') as f:
            request: dict = json.load(f)

        URL = request.get('url', URL)

        if 'endpoint' not in request:
            print('endpoint must be specified in the request file')
            exit(1)
        else:
            ENDPOINT = request.get('endpoint')

        DATA = request.get('data', None)
        HEADERS = request.get('headers', None)
        METHOD = request.get('method', 'GET')

        if METHOD not in METHOD_CHOICES:
            print(f'{METHOD} not a recognized method from {METHOD_CHOICES}')
            exit(1)
    else:
        print(f'The file {FILE} does not exist')
        exit(1)
else:
    DATA = args.data
    HEADERS = args.headers
    METHOD = args.method
    URL = args.url

if not ENDPOINT.startswith('/'):
    ENDPOINT = f'/{ENDPOINT}'

if DATA and isinstance(DATA, str):
    try:
        DATA = json.loads(DATA)
    except ValueError:
        with open(DATA, 'r') as f:
            DATA = json.load(f)

if HEADERS and isinstance(HEADERS, str):
    HEADERS = json.loads(args.headers)

def parse_response(response):
    if response.status_code >= 400:
        print(f'An error occurred: Status Code {response.status_code}')
        print(f'Response Body: {response.content.decode()}')
    else:
        print(f'Response {response.status_code}')
        print(f'JSON:')
        response_json = response.json()
        response_json = json.dumps(response_json, indent=2)
        print(response_json)

def main():
    url = f'{URL}{ENDPOINT}'

    default_headers = {'Content-Type': 'application/json'}

    if HEADERS:
        headers = {**default_headers, **HEADERS}
    else:
        headers = default_headers

    method = methods.get(METHOD, methods['GET'])
    response = method(url, json=DATA, headers=headers)

    parse_response(response)

if __name__ == '__main__':
    main()


# coding: utf-8

"""
    ExaVault API

    See our API reference documentation at https://www.exavault.com/developer/api-docs/  # noqa: E501

    OpenAPI spec version: 2.0
    Contact: support@exavault.com
    Generated by: https://github.com/swagger-api/swagger-codegen.git
"""

from __future__ import absolute_import

import re  # noqa: F401

# python 2 and python 3 compatibility library
import six

from exavault.api_client import ApiClient

class RecipientsApi(object):
    """NOTE: This class is auto generated by the swagger code generator program.

    Do not edit the class manually.
    Ref: https://github.com/swagger-api/swagger-codegen
    """

    def __init__(self, api_client=None):
        if api_client is None:
            api_client = ApiClient()
        self.api_client = api_client

    def resend_invitations_for_share(self, ev_api_key, ev_access_token, share_id, **kwargs):  # noqa: E501
        """Resend invitations to share recipients  # noqa: E501

        Resend invitations to one or all recipients attached to specified share. The most recent message that was sent for the share will be re-used for this email.  You can use [GET /shares/{id}](#operation/getShareById) to view the recipient list and message history for a share. Use [PATCH /shares/{id}](#operation/updateShareById) to add or remove recipients.  # noqa: E501
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please pass async_req=True
        >>> thread = api.resend_invitations_for_share(ev_api_key, ev_access_token, share_id, async_req=True)
        >>> result = thread.get()

        :param async_req bool
        :param str ev_api_key: API Key required to make the API call. (required)
        :param str ev_access_token: Access token required to make the API call. (required)
        :param int share_id: ID of the share to resend invites for. (required)
        :param ResendInvitationsRequestBody body:
        :return: ShareRecipientsResponse
                 If the method is called asynchronously,
                 returns the request thread.
        """
        kwargs['_return_http_data_only'] = True
        if kwargs.get('async_req'):
            return self.resend_invitations_for_share_with_http_info(ev_api_key, ev_access_token, share_id, **kwargs)  # noqa: E501
        else:
            (data) = self.resend_invitations_for_share_with_http_info(ev_api_key, ev_access_token, share_id, **kwargs)  # noqa: E501
            return data

    def resend_invitations_for_share_with_http_info(self, ev_api_key, ev_access_token, share_id, **kwargs):  # noqa: E501
        """Resend invitations to share recipients  # noqa: E501

        Resend invitations to one or all recipients attached to specified share. The most recent message that was sent for the share will be re-used for this email.  You can use [GET /shares/{id}](#operation/getShareById) to view the recipient list and message history for a share. Use [PATCH /shares/{id}](#operation/updateShareById) to add or remove recipients.  # noqa: E501
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please pass async_req=True
        >>> thread = api.resend_invitations_for_share_with_http_info(ev_api_key, ev_access_token, share_id, async_req=True)
        >>> result = thread.get()

        :param async_req bool
        :param str ev_api_key: API Key required to make the API call. (required)
        :param str ev_access_token: Access token required to make the API call. (required)
        :param int share_id: ID of the share to resend invites for. (required)
        :param ResendInvitationsRequestBody body:
        :return: ShareRecipientsResponse
                 If the method is called asynchronously,
                 returns the request thread.
        """

        all_params = ['ev_api_key', 'ev_access_token', 'share_id', 'body']  # noqa: E501
        all_params.append('async_req')
        all_params.append('_return_http_data_only')
        all_params.append('_preload_content')
        all_params.append('_request_timeout')

        params = locals()
        for key, val in six.iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method resend_invitations_for_share" % key
                )
            params[key] = val
        del params['kwargs']
        # verify the required parameter 'ev_api_key' is set
        if ('ev_api_key' not in params or
                params['ev_api_key'] is None):
            raise ValueError("Missing the required parameter `ev_api_key` when calling `resend_invitations_for_share`")  # noqa: E501
        # verify the required parameter 'ev_access_token' is set
        if ('ev_access_token' not in params or
                params['ev_access_token'] is None):
            raise ValueError("Missing the required parameter `ev_access_token` when calling `resend_invitations_for_share`")  # noqa: E501
        # verify the required parameter 'share_id' is set
        if ('share_id' not in params or
                params['share_id'] is None):
            raise ValueError("Missing the required parameter `share_id` when calling `resend_invitations_for_share`")  # noqa: E501

        collection_formats = {}

        path_params = {}
        if 'share_id' in params:
            path_params['shareId'] = params['share_id']  # noqa: E501

        query_params = []

        header_params = {}
        if 'ev_api_key' in params:
            header_params['ev-api-key'] = params['ev_api_key']  # noqa: E501
        if 'ev_access_token' in params:
            header_params['ev-access-token'] = params['ev_access_token']  # noqa: E501

        form_params = []
        local_var_files = {}

        body_params = None
        if 'body' in params:
            body_params = params['body']
        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.select_header_accept(
            ['application/json'])  # noqa: E501

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.select_header_content_type(  # noqa: E501
            ['application/json'])  # noqa: E501

        # Authentication setting
        auth_settings = []  # noqa: E501

        return self.api_client.call_api(
            '/recipients/shares/invites/{shareId}', 'POST',
            path_params,
            query_params,
            header_params,
            body=body_params,
            post_params=form_params,
            files=local_var_files,
            response_type='ShareRecipientsResponse',  # noqa: E501
            auth_settings=auth_settings,
            async_req=params.get('async_req'),
            _return_http_data_only=params.get('_return_http_data_only'),
            _preload_content=params.get('_preload_content', True),
            _request_timeout=params.get('_request_timeout'),
            collection_formats=collection_formats)


import time
from typing import Callable, Tuple, Union

import numpy as np

def function(x: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
    """Function for which we find the root: $f(x) = x^3 - 2x - 5$.

    Args:
        x: Value(s) at which to evaluate the function.

    Returns:
        Numpy array or float with function evaluations for the provided `x`.

    """
    return (x - 2) ** 3

def check_strict_monotonicity(
    function: Callable, a: float = 0.0, b: float = 1.0
) -> bool:
    """Check whether `function` is strictly monotonically increasing or
    decreasing over the interval `[a, b]`.

    Args:
        function: Function to check for strict monotonicity.
        a: Left endpoint of the interval.
        b: Right endpoint of the interval.

    Returns:
        Boolean indicating whether `function` is strictly monotonically
        increasing or decreasing
    """
    if function(x=a) > function(x=b):
        print(
            f"Function `f` is strictly monotonically decreasing over the "
            f"interval {[a, b]}."
        )
        is_increasing = False
    elif function(x=a) < function(x=b):
        print(
            f"Function `f` is strictly monotonically increasing over the "
            f"interval {[a, b]}."
        )
        is_increasing = True
    else:
        raise ValueError(
            f"Function `f` is not strictly monotonic over the "
            f"interval {[a, b]}."
        )

    return is_increasing

def find_root(
    function: Callable,
    a: float = 0.0,
    b: float = 1.0,
    tolerance: float = 1e-6,
    is_increasing: bool = True,
    y: float = 0.0,
    run_time_limit: float = 60.0,
) -> Tuple[float, float, float]:
    """Find the root of the function using bisection method.

    Args:
        a: Left endpoint of the interval.
        b: Right endpoint of the interval.
        tolerance: Tolerance level for the root.
        is_increasing: Boolean indicating whether the function is strictly
            monotonically increasing or decreasing over the interval `[a, b]`.
        y: If we want to find the inverse value of `function` for `y`, we can
            do this by finding the root of the function `y - f(x)`.
        run_time_limit: If the algorithm runs for longer than this time limit,
            it will stop and return the current best estimate.

    Returns:
        Float with the root of the function, the required time to find the root
        and the function evaluation at the root.

    Raises:
        AssertionError: If the interval is not defined as `[a, b]` with
            `a < b`.
        AssertionError: If the tolerance is not a positive number.
    """
    start_time = time.perf_counter()

    assert a < b, "The interval must be defined as `[a, b]` with `a < b`."
    assert tolerance > 0, "The tolerance must be a positive number."

    is_increasing = check_strict_monotonicity(a=a, b=b, function=function)
    if y != 0.0:
        # We are searching for root of `y - function(x)`, so if `function(x)`
        # is strictly monotonically increasing, then `y - function(x)` is
        # strictly monotonically decreasing and vice versa.
        is_increasing = not is_increasing

    converged = False
    while not converged:
        # 1. Find the midpoint `c = (a + b) / 2`.
        c = (a + b) / 2
        # 2. Evaluate the function at the midpoint.
        if y == 0.0:
            # We are finding the root of `function`.
            func_midpoint = function(x=c)
        else:
            # We are finding the inverse value of `function` for `y`.
            func_midpoint = y - function(x=c)
        # 3. If the function value at the midpoint is close enough to zero, we
        # are done. Otherwise, if the function value at the midpoint is
        # positive, the root is in the left half of the interval, otherwise it
        # is in the right half of the interval.
        if abs(func_midpoint) <= tolerance:
            return c, time.perf_counter() - start_time, func_midpoint
        else:
            if is_increasing:
                if func_midpoint > 0:
                    b = c
                else:
                    a = c
            else:
                if func_midpoint > 0:
                    a = c
                else:
                    b = c
            converged = False

        if time.perf_counter() - start_time > run_time_limit:
            print(
                f"Time limit of {run_time_limit} s reached. "
                f"Returning current best estimate, which is {c}."
                f"\nFunction evaluation: {func_midpoint}."
            )

            return c, time.perf_counter() - start_time, func_midpoint

def find_inverse_value(
    function: Callable,
    y: float,
    a: float = 0.0,
    b: float = 1.0,
    tolerance: float = 1e-6,
    is_increasing: bool = True,
) -> Tuple[float, float]:
    """Find the inverse value of `function` using bisection method.
    This is done as described in
    https://math.stackexchange.com/questions/1440818/finding-the-inverse-of-a-function-using-bisection-method

    Args:
        y: Value for which to find the inverse value of `function`, i.e.
            `function^{-1}(y)`.
        --for the rest of the arguments, see `find_root`--

    Returns:
        Float with the inverse value of `function` for `y` and the required
        time to find the inverse value.
    """
    start_time = time.perf_counter()

    assert a <= y <= b, "The value `y` must be in the interval `[a, b]`."

    inverse_value, _, _ = find_root(
        function=function,
        a=a,
        b=b,
        tolerance=tolerance,
        is_increasing=is_increasing,
        y=y,
        run_time_limit=180.0,  # 3 minutes
    )

    return inverse_value, time.perf_counter() - start_time

def main() -> None:
    root, time_req_root, func_eval = find_root(
        function=function, a=-5.0, b=5.0, tolerance=1e-10
    )
    print(
        f"Found root at `x = {root}` in {round(time_req_root, 6)} s."
        f"\nFunction evaluation: {func_eval}.\n\n"
    )

    y = 3.0
    inverse, time_req_inverse = find_inverse_value(
        function=function, y=y, a=0.0, b=5.0, tolerance=1e-10
    )
    print(
        f"Found inverse value `f^(-1)(y={y}) = {round(inverse, 6)}` in "
        f"{round(time_req_inverse, 6)} s."
    )

if __name__ == "__main__":
    main()


#!/usr/bin/env python

import sys
import os
import signal
import json
from pathlib import Path
import requests
import threading, queue
import time
from tqdm import tqdm

package_q = queue.Queue(maxsize=500)
results_q = queue.Queue(maxsize=500)

base_url = "https://api.npmjs.org/downloads/point/last-year/"

to_get = []

def get_worker():
    # We can't do bulk requests for scoped packages, but we can for
    # everything else

    while True:

        package = package_q.get()

        url = base_url + package
        try:
            resp = requests.get(url=url, timeout=10)
        except:
            # Lots of weird things happen when this times out in a thread,
            # this is the lazy solution
            os.kill(os.getpid(), signal.SIGINT)

        dl_data = resp.json()

        if "error" in dl_data:
            dl_data = {
                "downloads": -1,
                "package": package
            }

        results_q.put(dl_data)
        package_q.task_done()

def put_worker():

    while True:
        result = results_q.get()

        package = result["package"]

        the_file = "downloads/%s" % package

        with open(the_file, "w") as fh:
            fh.write(json.dumps(result))

        #print(the_file)

        results_q.task_done()

# Main
#

threading.Thread(target=put_worker, daemon=True).start()
threading.Thread(target=get_worker, daemon=True).start()
threading.Thread(target=get_worker, daemon=True).start()
threading.Thread(target=get_worker, daemon=True).start()
threading.Thread(target=get_worker, daemon=True).start()
threading.Thread(target=get_worker, daemon=True).start()
threading.Thread(target=get_worker, daemon=True).start()
threading.Thread(target=get_worker, daemon=True).start()
threading.Thread(target=get_worker, daemon=True).start()

with open("all_packages.json") as fh:
    data = json.load(fh)

    all_packages = data['rows']

#for filename in tqdm(all_packages, miniters=1):
for filename in all_packages:

    one_id = filename["id"]
    the_file = "downloads/%s" % one_id

    # We only want scoped things
    if not one_id.startswith('@'):
        continue

    # Things that start with a @ are special in npm
    if one_id.startswith('@'):
        (the_dir, the_package) = one_id.split('/')
        if os.path.exists("downloads/%s" % the_dir):
            # We're fine
            pass
        else:
            os.mkdir("downloads/%s" % the_dir)

    if os.path.exists(the_file):
        continue

    print(one_id)
    package_q.put(one_id)

while not package_q.empty():
    print("Waiting on package queue")
    time.sleep(1)

while not results_q.empty():
    print("Waiting on results queue")
    time.sleep(1)


import json
import logging

from django.conf import settings
from django.core.cache import cache
from django.utils import timezone

import requests
from app.utils import get_location_from_ip
from cacheops import cached_as
from dashboard.models import Activity, Tip, UserAction
from dashboard.utils import _get_utm_from_cookie
from kudos.models import KudosTransfer
from marketing.utils import handle_marketing_callback
from perftools.models import JSONStore
from ptokens.models import PersonalToken
from retail.helpers import get_ip
from townsquare.models import Announcement

RECORD_VISIT_EVERY_N_SECONDS = 60 * 60

logger = logging.getLogger(__name__)

@cached_as(JSONStore.objects.filter(view='posts', key='posts'), timeout=1200)
def fetchPost(qt='2'):
    jsonstores = JSONStore.objects.filter(view='posts', key='posts')
    if jsonstores.exists():
        return jsonstores.first().data

@cached_as(Announcement.objects.filter(key__in=['footer', 'header']), timeout=1200)
def get_sitewide_announcements():
    announcements = Announcement.objects.filter(
        key__in=['footer', 'header'], valid_to__gt=timezone.now(), valid_from__lt=timezone.now()
    )
    announcement = announcements.filter(key='footer').first()
    header_msg, footer_msg, nav_salt = '', '', 0
    if announcement:
        footer_msg = announcement.title + announcement.desc
    announcement = announcements.filter(key='header').first()
    if announcement:
        header_msg = announcement.title + announcement.desc
        nav_salt = announcement.salt
    return header_msg, footer_msg, nav_salt

def preprocess(request):
    """Handle inserting pertinent data into the current context."""

    # make lbcheck super lightweight

    if request.path == '/lbcheck':
        return {}

    ptoken = None

    search_url = ''
    user_is_authenticated = request.user.is_authenticated
    profile = request.user.profile if user_is_authenticated and hasattr(request.user, 'profile') else None
    if user_is_authenticated and profile and profile.pk:
        # what actions to take?
        record_join = not profile.last_visit
        record_visit = not profile.last_visit or profile.last_visit < (
            timezone.now() - timezone.timedelta(seconds=RECORD_VISIT_EVERY_N_SECONDS)
        )
        if record_visit:
            try:
                profile.last_visit = timezone.now()
                profile.save()
            except Exception as e:
                logger.exception(e)
            try:
                from dashboard.tasks import profile_dict
                profile_dict.delay(profile.pk)
            except Exception as e:
                logger.exception(e)
            metadata = {
                'useragent': request.META['HTTP_USER_AGENT'],
                'referrer': request.META.get('HTTP_REFERER', None),
                'path': request.META.get('PATH_INFO', None),
                'session_key': request.session._session_key,
            }
            ip_address = get_ip(request)
            UserAction.objects.create(
                user=request.user,
                profile=profile,
                action='Visit',
                location_data=get_location_from_ip(ip_address),
                ip_address=ip_address,
                utm=_get_utm_from_cookie(request),
                metadata=metadata,
            )

        if record_join:
            Activity.objects.create(profile=profile, activity_type='joined')

        ptoken = PersonalToken.objects.filter(token_owner_profile=profile).first()

    # Check if user's location supports pTokens
    try:
        current_location = profile.locations[-1]
        is_location_blocked_for_ptokens = current_location['country_code'] == settings.PTOKEN_BLOCKED_REGION['country_code'] \
            and current_location['region'] == settings.PTOKEN_BLOCKED_REGION['region']
    except:
        # If user is not logged in
        is_location_blocked_for_ptokens = False

    # handles marketing callbacks
    if request.GET.get('cb'):
        callback = request.GET.get('cb')
        handle_marketing_callback(callback, request)

    header_msg, footer_msg, nav_salt = get_sitewide_announcements()

    try:
        onboard_tasks = JSONStore.objects.get(key='onboard_tasks').data
    except JSONStore.DoesNotExist:
        onboard_tasks = []

    # town square wall post max length
    max_length_offset = abs(((
        request.user.profile.created_on
        if hasattr(request.user, 'profile') and request.user.is_authenticated else timezone.now()
    ) - timezone.now()).days)
    max_length = 600 + max_length_offset

    context = {
        'STATIC_URL': settings.STATIC_URL,
        'MEDIA_URL': settings.MEDIA_URL,
        'max_length': max_length,
        'max_length_offset': max_length_offset,
        'search_url': f'{settings.BASE_URL}user_lookup',
        'base_url': settings.BASE_URL,
        'github_handle': request.user.username.lower() if user_is_authenticated else False,
        'email': request.user.email if user_is_authenticated else False,
        'name': request.user.get_full_name() if user_is_authenticated else False,
        'raven_js_version': settings.RAVEN_JS_VERSION,
        'raven_js_dsn': settings.SENTRY_JS_DSN,
        'release': settings.RELEASE,
        'env': settings.ENV,
        'header_msg': header_msg,
        'nav_salt': nav_salt,
        'footer_msg': footer_msg,
        'INFURA_V3_PROJECT_ID': settings.INFURA_V3_PROJECT_ID,
        'giphy_key': settings.GIPHY_KEY,
        'youtube_key': settings.YOUTUBE_API_KEY,
        'fortmatic_live_key': settings.FORTMATIC_LIVE_KEY,
        'fortmatic_test_key': settings.FORTMATIC_TEST_KEY,
        'orgs': profile.organizations if profile else [],
        'profile_id': profile.id if profile else '',
        'is_pro': profile.is_pro if profile else False,
        'hotjar': settings.HOTJAR_CONFIG,
        'ipfs_config': {
            'host': settings.JS_IPFS_HOST,
            'port': settings.IPFS_API_PORT,
            'protocol': settings.IPFS_API_SCHEME,
            'root': settings.IPFS_API_ROOT,
        },
        'access_token': profile.access_token if profile else '',
        'is_staff': request.user.is_staff if user_is_authenticated else False,
        'is_moderator': profile.is_moderator if profile else False,
        'is_alpha_tester': profile.is_alpha_tester if profile else False,
        'user_groups': list(profile.user_groups) if profile else False,
        'persona_is_funder': profile.persona_is_funder if profile else False,
        'persona_is_hunter': profile.persona_is_hunter if profile else False,
        'pref_do_not_track': profile.pref_do_not_track if profile else False,
        'profile_url': profile.url if profile else False,
        'quests_live': settings.QUESTS_LIVE,
        'onboard_tasks': onboard_tasks,
        'ptoken_abi': settings.PTOKEN_ABI,
        'ptoken_factory_address': settings.PTOKEN_FACTORY_ADDRESS,
        'ptoken_factory_abi': settings.PTOKEN_FACTORY_ABI,
        'ptoken_address': ptoken.token_address if ptoken else '',
        'ptoken_id': ptoken.id if ptoken else None,
        'is_location_blocked_for_ptokens': is_location_blocked_for_ptokens,
        'match_payouts_abi': settings.MATCH_PAYOUTS_ABI,
        'match_payouts_address': settings.MATCH_PAYOUTS_ADDRESS,
        'mautic_id': profile.mautic_id if profile else None,
    }
    context['json_context'] = json.dumps(context)
    context['last_posts'] = cache.get_or_set('last_posts', fetchPost, 5000)

    if context['github_handle']:
        context['unclaimed_tips'] = Tip.objects.filter(
            receive_txid='', username__iexact=context['github_handle'], web3_type='v3',
        ).send_happy_path().cache(timeout=60)
        context['unclaimed_kudos'] = KudosTransfer.objects.filter(
            receive_txid='', username__iexact="@" + context['github_handle'], web3_type='v3',
        ).send_happy_path().cache(timeout=60)

        if not settings.DEBUG:
            context['unclaimed_tips'] = context['unclaimed_tips'].filter(network='mainnet').cache(timeout=60)
            context['unclaimed_kudos'] = context['unclaimed_kudos'].filter(network='mainnet').cache(timeout=60)

    return context


import numpy as np
from scipy import stats, linalg

class StatFunctions():
    def false_discovery_rate(p_values, q = 0.05):
        if q < 0:
            q = 0
        elif q > 1:
            q = 1

        x = np.arange(len(p_values))/len(p_values)
        p_values = np.sort(p_values)

        index = np.sum(p_values <= q*x)
        if index > 0:
            r = p_values[index]
        else:
            r = 0
        return r

    def quantiles(values, P = 100):
        assert len(values) > 0, 'No elements in values'
        if len(values.shape) == 1:
            return StatFunctions.quantiles_1d(values, P)
        elif len(values.shape) == 2:
            return StatFunctions.quantiles_2d(values, P)
        elif len(values.shape) == 3:
            return StatFunctions.quantiles_3d(values, P)

    def quantiles_1d(values, P = 100):
        # NOTE the implementation of percentile in python might differ from quantile in matlab
        percentiles = []
        for k in range(P):
            percentiles.append(np.percentile(values, k*100/(P-1)))
        Q = np.array(percentiles)
        return Q

    def quantiles_2d(values, P = 100):
        Q = []
        for i in range(values.shape[1]):
            current_values = values[:,i].copy()
            # NOTE the implementation of percentile in python might differ from quantile in matlab
            percentiles = []
            for k in range(P):
                percentiles.append(np.percentile(current_values, k*100/(P-1)))
            Q.append(percentiles)
        Q = np.array(Q).T
        return Q

    def quantiles_3d(values, P = 100):
        Q = np.zeros([P, values.shape[1], values.shape[2]])
        for i in range(values.shape[1]):
            for j in range(values.shape[2]):
                current_values = values[:,i,j].copy().T
                # NOTE the implementation of percentile in python might differ from quantile in matlab
                percentiles = []
                for k in range(P):
                    percentiles.append(np.percentile(current_values, k*100/(P-1)))
                Q[:,i,j] = np.array(percentiles)
        Q = np.squeeze(Q)
        return Q

    def p_value(observed_difference, random_differences, single = True):
        assert len(np.shape(observed_difference)) == len(np.shape(random_differences)) - 1
        if isinstance(random_differences, list):
            random_differences = np.array(random_differences)
        M = random_differences.size
        if len(np.shape(observed_difference)) == 0:
            return StatFunctions.p_value_scalar(observed_difference, random_differences, single)
        elif len(np.shape(observed_difference)) == 1:
            return StatFunctions.p_value_vector(observed_difference, random_differences, single)
        elif len(np.shape(observed_difference)) == 2:
            return StatFunctions.p_value_matrix(observed_difference, random_differences, single)

    def p_value_scalar(observed_difference, random_differences, single):
        M = len(random_differences) + 1
        normalized_observed_difference = observed_difference - np.mean(random_differences)
        normalized_random_differences = random_differences - np.mean(random_differences)
        if single:
            if normalized_observed_difference > 0:
                p_value = (np.sum(normalized_random_differences > normalized_observed_difference) + 1) / M
            else:
                p_value = (np.sum(normalized_random_differences < normalized_observed_difference) + 1) / M
        else:
            p_value = (np.sum(np.abs(normalized_random_differences) > np.abs(normalized_observed_difference)) + 1) / M
        return p_value

    def p_value_vector(observed_difference, random_differences, single):
        N = np.shape(random_differences)[-1]
        p_value = np.ones(N)
        for n in range(N):
            p_value[n] = StatFunctions.p_value_scalar(observed_difference[n], random_differences[:,n], single)
        return p_value

    def p_value_matrix(observed_difference, random_differences, single):
        N = np.shape(random_differences)[-1]
        p_value = np.ones([N, N])
        for n in range(N):
            p_value[n,:] = StatFunctions.p_value_vector(observed_difference[:,n], random_differences[:,:,n], single)
        return p_value

    def bonferroni(p_values, p):
        # calculates the Bonferroni correction for p-values. p is the starting p-value.
        r = p / len(p_values)
        return r

    def correlation(data, correlation_type):
        correlation_type = correlation_type.replace(' ', '_')
        corr = np.zeros((data.shape[1], data.shape[1]), dtype=np.float)
        try:
            correlation_function = eval('StatFunctions.correlation_{}'.format(correlation_type))
            corr = correlation_function(data)
        except Exception as e:
            print('Correlation function {} not implemented'.format(correlation_type))
            print(e)
        return corr

    def correlation_pearson(data):
        return np.corrcoef(data)

    def correlation_spearman(data):
        rank = np.argsort(data)
        return np.corrcoef(rank)

    def correlation_kendall(data):
        correlation = np.zeros((data.shape[0], data.shape[0]))
        for i in range(data.shape[0]):
            for j in range(data.shape[0]):
                correlation[i, j] = stats.kendalltau(data[i], data[j])[0]
        return correlation

    def correlation_partial_pearson(data):
        data = data.T
        data = np.asarray(data)
        nodes = data.shape[1]
        correlation = np.zeros((nodes, nodes), dtype=np.float)
        for i in range(nodes):
            correlation[i, i] = 1
            for j in range(i+1, nodes):
                idx = np.ones(nodes, dtype=np.bool)
                idx[i] = False
                idx[j] = False
                beta_i = linalg.lstsq(data[:, idx], data[:, j])[0]
                beta_j = linalg.lstsq(data[:, idx], data[:, i])[0]
                res_j = data[:, j] - data[:, idx].dot(beta_i)
                res_i = data[:, i] - data[:, idx].dot(beta_j)
                corr = stats.pearsonr(res_i, res_j)[0]
                correlation[i, j] = corr
                correlation[j, i] = corr
        return correlation

    def correlation_partial_spearman(data):
        data = data.T
        data = np.asarray(data)
        nodes = data.shape[1]
        correlation = np.zeros((nodes, nodes), dtype=np.float)
        for i in range(nodes):
            correlation[i, i] = 1
            for j in range(i+1, nodes):
                idx = np.ones(nodes, dtype=np.bool)
                idx[i] = False
                idx[j] = False
                beta_i = linalg.lstsq(data[:, idx], data[:, j])[0]
                beta_j = linalg.lstsq(data[:, idx], data[:, i])[0]
                res_j = data[:, j] - data[:, idx].dot(beta_i)
                res_i = data[:, i] - data[:, idx].dot(beta_j)
                corr = stats.spearmanr(res_i, res_j)[0]
                correlation[i, j] = corr
                correlation[j, i] = corr
        return correlation