# Path: sksurv/base.py
# class SurvivalAnalysisMixin:
# 
#     def score(self, X, y):
#         """Returns the concordance index of the prediction.
# 
#         Parameters
#         ----------
#         X : array-like, shape = (n_samples, n_features)
#             Test samples.
# 
#         y : structured array, shape = (n_samples,)
#             A structured array containing the binary event indicator
#             as first field, and time of event or time of censoring as
#             second field.
# 
#         Returns
#         -------
#         cindex : float
#             Estimated concordance index.
#         """
#         from .metrics import concordance_index_censored
#         name_event, name_time = y.dtype.names
# 
#         risk_score = self.predict(X)
#         if not getattr(self, "_predict_risk_score", True):
#             risk_score *= -1  # convert prediction on time scale to risk scale
# 
#         result = concordance_index_censored(y[name_event], y[name_time], risk_score)
#         return result[0]
# 
# Path: sksurv/functions.py
# class StepFunction:
#     """Callable step function.
# 
#     .. math::
# 
#         f(z) = a * y_i + b,
#         x_i \\leq z < x_{i + 1}
# 
#     Parameters
#     ----------
#     x : ndarray, shape = (n_points,)
#         Values on the x axis in ascending order.
# 
#     y : ndarray, shape = (n_points,)
#         Corresponding values on the y axis.
# 
#     a : float, optional, default: 1.0
#         Constant to multiply by.
# 
#     b : float, optional, default: 0.0
#         Constant offset term.
#     """
#     def __init__(self, x, y, a=1., b=0.):
#         check_consistent_length(x, y)
#         self.x = x
#         self.y = y
#         self.a = a
#         self.b = b
# 
#     def __call__(self, x):
#         """Evaluate step function.
# 
#         Parameters
#         ----------
#         x : float|array-like, shape=(n_values,)
#             Values to evaluate step function at.
# 
#         Returns
#         -------
#         y : float|array-like, shape=(n_values,)
#             Values of step function at `x`.
#         """
#         x = numpy.atleast_1d(x)
#         if not numpy.isfinite(x).all():
#             raise ValueError("x must be finite")
#         if numpy.min(x) < self.x[0] or numpy.max(x) > self.x[-1]:
#             raise ValueError(
#                 "x must be within [%f; %f]" % (self.x[0], self.x[-1]))
#         i = numpy.searchsorted(self.x, x, side='left')
#         not_exact = self.x[i] != x
#         i[not_exact] -= 1
#         value = self.a * self.y[i] + self.b
#         if value.shape[0] == 1:
#             return value[0]
#         return value
# 
#     def __repr__(self):
#         return "StepFunction(x=%r, y=%r, a=%r, b=%r)" % (self.x, self.y, self.a, self.b)
# 
# Path: sksurv/util.py
# def check_array_survival(X, y):
#     """Check that all arrays have consistent first dimensions.
# 
#     Parameters
#     ----------
#     X : array-like
#         Data matrix containing feature vectors.
# 
#     y : structured array with two fields
#         A structured array containing the binary event indicator
#         as first field, and time of event or time of censoring as
#         second field.
# 
#     kwargs : dict
#         Additional arguments passed to :func:`sklearn.utils.check_array`.
# 
#     Returns
#     -------
#     event : array, shape=[n_samples,], dtype=bool
#         Binary event indicator.
# 
#     time : array, shape=[n_samples,], dtype=float
#         Time of event or censoring.
#     """
#     event, time = check_y_survival(y)
#     check_consistent_length(X, event, time)
#     return event, time


from math import ceil
from sklearn.base import BaseEstimator
from sklearn.tree import _tree
from sklearn.tree._classes import DENSE_SPLITTERS
from sklearn.tree._splitter import Splitter
from sklearn.tree._tree import BestFirstTreeBuilder, DepthFirstTreeBuilder, Tree
from sklearn.utils.validation import check_is_fitted, check_random_state
from ..base import SurvivalAnalysisMixin
from ..functions import StepFunction
from ..util import check_array_survival
from ._criterion import LogrankCriterion
import numbers
import warnings
import numpy as np



__all__ = ["SurvivalTree"]

DTYPE = _tree.DTYPE


def _array_to_step_function(x, array):
    n_samples = array.shape[0]
    funcs = np.empty(n_samples, dtype=np.object_)
    for i in range(n_samples):
        funcs[i] = StepFunction(x=x,
                                y=array[i])
    return funcs



# Path: sksurv/base.py
# class SurvivalAnalysisMixin:
# 
#     def score(self, X, y):
#         """Returns the concordance index of the prediction.
# 
#         Parameters
#         ----------
#         X : array-like, shape = (n_samples, n_features)
#             Test samples.
# 
#         y : structured array, shape = (n_samples,)
#             A structured array containing the binary event indicator
#             as first field, and time of event or time of censoring as
#             second field.
# 
#         Returns
#         -------
#         cindex : float
#             Estimated concordance index.
#         """
#         from .metrics import concordance_index_censored
#         name_event, name_time = y.dtype.names
# 
#         risk_score = self.predict(X)
#         if not getattr(self, "_predict_risk_score", True):
#             risk_score *= -1  # convert prediction on time scale to risk scale
# 
#         result = concordance_index_censored(y[name_event], y[name_time], risk_score)
#         return result[0]
# 
# Path: sksurv/functions.py
# class StepFunction:
#     """Callable step function.
# 
#     .. math::
# 
#         f(z) = a * y_i + b,
#         x_i \\leq z < x_{i + 1}
# 
#     Parameters
#     ----------
#     x : ndarray, shape = (n_points,)
#         Values on the x axis in ascending order.
# 
#     y : ndarray, shape = (n_points,)
#         Corresponding values on the y axis.
# 
#     a : float, optional, default: 1.0
#         Constant to multiply by.
# 
#     b : float, optional, default: 0.0
#         Constant offset term.
#     """
#     def __init__(self, x, y, a=1., b=0.):
#         check_consistent_length(x, y)
#         self.x = x
#         self.y = y
#         self.a = a
#         self.b = b
# 
#     def __call__(self, x):
#         """Evaluate step function.
# 
#         Parameters
#         ----------
#         x : float|array-like, shape=(n_values,)
#             Values to evaluate step function at.
# 
#         Returns
#         -------
#         y : float|array-like, shape=(n_values,)
#             Values of step function at `x`.
#         """
#         x = numpy.atleast_1d(x)
#         if not numpy.isfinite(x).all():
#             raise ValueError("x must be finite")
#         if numpy.min(x) < self.x[0] or numpy.max(x) > self.x[-1]:
#             raise ValueError(
#                 "x must be within [%f; %f]" % (self.x[0], self.x[-1]))
#         i = numpy.searchsorted(self.x, x, side='left')
#         not_exact = self.x[i] != x
#         i[not_exact] -= 1
#         value = self.a * self.y[i] + self.b
#         if value.shape[0] == 1:
#             return value[0]
#         return value
# 
#     def __repr__(self):
#         return "StepFunction(x=%r, y=%r, a=%r, b=%r)" % (self.x, self.y, self.a, self.b)
# 
# Path: sksurv/util.py
# def check_array_survival(X, y):
#     """Check that all arrays have consistent first dimensions.
# 
#     Parameters
#     ----------
#     X : array-like
#         Data matrix containing feature vectors.
# 
#     y : structured array with two fields
#         A structured array containing the binary event indicator
#         as first field, and time of event or time of censoring as
#         second field.
# 
#     kwargs : dict
#         Additional arguments passed to :func:`sklearn.utils.check_array`.
# 
#     Returns
#     -------
#     event : array, shape=[n_samples,], dtype=bool
#         Binary event indicator.
# 
#     time : array, shape=[n_samples,], dtype=float
#         Time of event or censoring.
#     """
#     event, time = check_y_survival(y)
#     check_consistent_length(X, event, time)
#     return event, time


# Path: sksurv/tree/tree.py
from math import ceil
from sklearn.base import BaseEstimator
from sklearn.tree import _tree
from sklearn.tree._classes import DENSE_SPLITTERS
from sklearn.tree._splitter import Splitter
from sklearn.tree._tree import BestFirstTreeBuilder, DepthFirstTreeBuilder, Tree
from sklearn.utils.validation import check_is_fitted, check_random_state
from ..base import SurvivalAnalysisMixin
from ..functions import StepFunction
from ..util import check_array_survival
from ._criterion import LogrankCriterion
import numbers
import warnings
import numpy as np




__all__ = ["SurvivalTree"]

DTYPE = _tree.DTYPE


def _array_to_step_function(x, array):
    n_samples = array.shape[0]
    funcs = np.empty(n_samples, dtype=np.object_)
    for i in range(n_samples):
        funcs[i] = StepFunction(x=x,
                                y=array[i])
    return funcs



class SurvivalTree(BaseEstimator, SurvivalAnalysisMixin):