# Path: hep_ml/commonutils.py
# def check_sample_weight(y_true, sample_weight):
#     """
#     Checks the weights, returns normalized version
#     :param y_true: numpy.array of shape [n_samples]
#     :param sample_weight: array-like of shape [n_samples] or None
#     :returns: numpy.array with weights of shape [n_samples]"""
#     if sample_weight is None:
#         return numpy.ones(len(y_true), dtype=numpy.float)
#     else:
#         sample_weight = numpy.array(sample_weight, dtype=numpy.float)
#         assert len(y_true) == len(sample_weight), \
#             "The length of weights is different: not {0}, but {1}".format(len(y_true), len(sample_weight))
#         return sample_weight
# 
# def sigmoid_function(x, width):
#     """ Sigmoid function is smoothing of Heaviside function,
#     the less width, the closer we are to Heaviside function
#     :type x: array-like with floats, arbitrary shape
#     :type width: float, if width == 0, this is simply Heaviside function
#     """
#     assert width >= 0, 'the width should be non-negative'
#     if abs(width) > 0.0001:
#         return expit(x / width)
#     else:
#         return (x > 0) * 1.0
# 
# def compute_cut_for_efficiency(efficiency, mask, y_pred, sample_weight=None):
#     """ Computes such cut(s), that provide given signal efficiency.
#     :type efficiency: float or numpy.array with target efficiencies, shape = [n_effs]
#     :type mask: array-like, shape = [n_samples], True for needed classes
#     :type y_pred: array-like, shape = [n_samples], predictions or scores (float)
#     :type sample_weight: None | array-like, shape = [n_samples]
#     :return: float or numpy.array, shape = [n_effs]
#     """
#     sample_weight = check_sample_weight(mask, sample_weight)
#     assert len(mask) == len(y_pred), 'lengths are different'
#     efficiency = numpy.array(efficiency)
#     is_signal = mask > 0.5
#     y_pred, sample_weight = y_pred[is_signal], sample_weight[is_signal]
#     return weighted_percentile(y_pred, 1. - efficiency, sample_weight=sample_weight)


import numpy
from .commonutils import check_sample_weight, sigmoid_function, compute_cut_for_efficiency
from sklearn.utils.validation import column_or_1d
    x_mean = numpy.average(x, weights=weights) + 1e-100
    normed = x / x_mean
    normed[normed < 1e-10] = 1e-10
    return numpy.average(normed * numpy.log(normed), weights=weights)


def compute_theil_on_bins(y_pred, mask, bin_indices, target_efficiencies, sample_weight):
    y_pred = column_or_1d(y_pred)
    sample_weight = check_sample_weight(y_pred, sample_weight=sample_weight)

    # ignoring events from other classes
    y_pred = y_pred[mask]
    bin_indices = bin_indices[mask]
    sample_weight = sample_weight[mask]

    bin_weights = compute_bin_weights(bin_indices=bin_indices, sample_weight=sample_weight)
    cuts = compute_cut_for_efficiency(target_efficiencies, mask=numpy.ones(len(y_pred), dtype=bool),
                                      y_pred=y_pred, sample_weight=sample_weight)
    result = 0.
    for cut in cuts:
        bin_efficiencies = compute_bin_efficiencies(y_pred, bin_indices=bin_indices,
                                                    cut=cut, sample_weight=sample_weight)
        result += theil(bin_efficiencies, weights=bin_weights)
    return result / len(cuts)


def compute_theil_on_groups(y_pred, mask, groups_indices, target_efficiencies, sample_weight):
    y_pred = column_or_1d(y_pred)
    sample_weight = check_sample_weight(y_pred, sample_weight=sample_weight)
    groups_weights = compute_group_weights(groups_indices, sample_weight=sample_weight)

# Path: hep_ml/commonutils.py
# def check_sample_weight(y_true, sample_weight):
#     """
#     Checks the weights, returns normalized version
#     :param y_true: numpy.array of shape [n_samples]
#     :param sample_weight: array-like of shape [n_samples] or None
#     :returns: numpy.array with weights of shape [n_samples]"""
#     if sample_weight is None:
#         return numpy.ones(len(y_true), dtype=numpy.float)
#     else:
#         sample_weight = numpy.array(sample_weight, dtype=numpy.float)
#         assert len(y_true) == len(sample_weight), \
#             "The length of weights is different: not {0}, but {1}".format(len(y_true), len(sample_weight))
#         return sample_weight
# 
# def sigmoid_function(x, width):
#     """ Sigmoid function is smoothing of Heaviside function,
#     the less width, the closer we are to Heaviside function
#     :type x: array-like with floats, arbitrary shape
#     :type width: float, if width == 0, this is simply Heaviside function
#     """
#     assert width >= 0, 'the width should be non-negative'
#     if abs(width) > 0.0001:
#         return expit(x / width)
#     else:
#         return (x > 0) * 1.0
# 
# def compute_cut_for_efficiency(efficiency, mask, y_pred, sample_weight=None):
#     """ Computes such cut(s), that provide given signal efficiency.
#     :type efficiency: float or numpy.array with target efficiencies, shape = [n_effs]
#     :type mask: array-like, shape = [n_samples], True for needed classes
#     :type y_pred: array-like, shape = [n_samples], predictions or scores (float)
#     :type sample_weight: None | array-like, shape = [n_samples]
#     :return: float or numpy.array, shape = [n_effs]
#     """
#     sample_weight = check_sample_weight(mask, sample_weight)
#     assert len(mask) == len(y_pred), 'lengths are different'
#     efficiency = numpy.array(efficiency)
#     is_signal = mask > 0.5
#     y_pred, sample_weight = y_pred[is_signal], sample_weight[is_signal]
#     return weighted_percentile(y_pred, 1. - efficiency, sample_weight=sample_weight)


# Path: hep_ml/metrics_utils.py
import numpy
from .commonutils import check_sample_weight, sigmoid_function, compute_cut_for_efficiency
from sklearn.utils.validation import column_or_1d

    x_mean = numpy.average(x, weights=weights) + 1e-100
    normed = x / x_mean
    normed[normed < 1e-10] = 1e-10
    return numpy.average(normed * numpy.log(normed), weights=weights)


def compute_theil_on_bins(y_pred, mask, bin_indices, target_efficiencies, sample_weight):
    y_pred = column_or_1d(y_pred)
    sample_weight = check_sample_weight(y_pred, sample_weight=sample_weight)

    # ignoring events from other classes
    y_pred = y_pred[mask]
    bin_indices = bin_indices[mask]
    sample_weight = sample_weight[mask]

    bin_weights = compute_bin_weights(bin_indices=bin_indices, sample_weight=sample_weight)
    cuts = compute_cut_for_efficiency(target_efficiencies, mask=numpy.ones(len(y_pred), dtype=bool),
                                      y_pred=y_pred, sample_weight=sample_weight)
    result = 0.
    for cut in cuts:
        bin_efficiencies = compute_bin_efficiencies(y_pred, bin_indices=bin_indices,
                                                    cut=cut, sample_weight=sample_weight)
        result += theil(bin_efficiencies, weights=bin_weights)
    return result / len(cuts)


def compute_theil_on_groups(y_pred, mask, groups_indices, target_efficiencies, sample_weight):
    y_pred = column_or_1d(y_pred)
    sample_weight = check_sample_weight(y_pred, sample_weight=sample_weight)
    groups_weights = compute_group_weights(groups_indices, sample_weight=sample_weight)

    cuts = compute_cut_for_efficiency(target_efficiencies, mask=mask,