/*!
 * Copyright (c) 2017-2023 by Contributors
 * \file c_api.h
 * \author Hyunsu Cho
 * \brief C API of Treelite, used for interfacing with other languages
 */

/* Note: Make sure to use slash-asterisk form of comments in this file
   (like this one). Do not use double-slash (//). */

#ifndef TREELITE_C_API_H_
#define TREELITE_C_API_H_

#ifdef __cplusplus
#define TREELITE_EXTERN_C extern "C"
#include <cstdint>
#include <cstdio>
#else
#define TREELITE_EXTERN_C
#include <stdint.h>
#include <stdio.h>
#endif

/* Special symbols for DLL library on Windows */
#if defined(_MSC_VER) || defined(_WIN32)
#define TREELITE_DLL TREELITE_EXTERN_C __declspec(dllexport)
#define TREELITE_DLL_STRUCT TREELITE_EXTERN_C struct __declspec(dllexport)
#else
#define TREELITE_DLL TREELITE_EXTERN_C __attribute__((visibility("default")))
#define TREELITE_DLL_STRUCT TREELITE_EXTERN_C struct __attribute__((visibility("default")))
#endif

/*!
 * \defgroup opaque_handles C API: Opaque handles
 * \{
 */
/*! \brief Handle to a decision tree ensemble model */
typedef void* TreeliteModelHandle;
/*! \brief Handle to a model builder object */
typedef void* TreeliteModelBuilderHandle;
/*! \brief Handle to a configuration of GTIL predictor */
typedef void* TreeliteGTILConfigHandle;
/*! \} */

/*!
 * \defgroup buffer_protocol C API: Python buffer protocol (PEP 3118)
 * \{
 */
/*! \brief Represent a frame in the Python buffer protocol (PEP 3118).
 *
 * We use a simplified representation to hold only 1-D arrays with stride 1.
 */
TREELITE_DLL_STRUCT TreelitePyBufferFrame {
  void* buf;
  char* format;
  size_t itemsize;
  size_t nitem;
};
/*! \} */

#ifndef __cplusplus
typedef struct TreelitePyBufferFrame TreelitePyBufferFrame;
#endif

/*!
 * \defgroup model_loader C API: Model loaders for XGBoost and LightGBM
 * \{
 */
/*!
 * \brief Load a model file generated by XGBoost (dmlc/xgboost), stored in the legacy binary
 *        format.
 * \param filename Name of model file
 * \param config_json JSON string consisting key-value pairs; used for configuring the model parser
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadXGBoostModelLegacyBinary(
    char const* filename, char const* config_json, TreeliteModelHandle* out);
/*!
 * \brief Load an XGBoost model from a memory buffer using the legacy binary format.
 * \param buf Memory buffer
 * \param len Size of memory buffer
 * \param config_json Null-terminated JSON string consisting key-value pairs; used for configuring
 *                    the model parser
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadXGBoostModelLegacyBinaryFromMemoryBuffer(
    void const* buf, size_t len, char const* config_json, TreeliteModelHandle* out);
/*!
 * \brief Load a model file generated by XGBoost (dmlc/xgboost), stored in the JSON format.
 * \param filename Name of model file
 * \param config_json Null-terminated JSON string consisting key-value pairs; used for configuring
 *                    the model parser
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadXGBoostModel(
    char const* filename, char const* config_json, TreeliteModelHandle* out);
/*!
 * \brief Load an XGBoost model from a JSON string
 * \param json_str JSON string containing the XGBoost model
 * \param length Length of the JSON string
 * \param config_json Null-terminated JSON string consisting key-value pairs; used for configuring
 *                    the model parser
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadXGBoostModelFromString(
    char const* json_str, size_t length, char const* config_json, TreeliteModelHandle* out);
/*!
 * \brief Load a model file generated by LightGBM (Microsoft/LightGBM). The
 *        model file must contain a decision tree ensemble.
 * \param filename Name of model file
 * \param config_json Null-terminated JSON string consisting key-value pairs; used for configuring
 *                    the model parser
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadLightGBMModel(
    char const* filename, char const* config_json, TreeliteModelHandle* out);
/*!
 * \brief Load a LightGBM model from a string. The string should be created with the
 *        model_to_string() method in LightGBM.
 * \param model_str Model string
 * \param config_json Null-terminated JSON string consisting key-value pairs; used for configuring
 *                    the model parser
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadLightGBMModelFromString(
    char const* model_str, char const* config_json, TreeliteModelHandle* out);
/*! \} */

/*!
 * \defgroup sklearn C API: Model loaders for scikit-learn
 * \{
 */
/*!
 * \brief Load a scikit-learn RandomForestRegressor model from a collection of arrays. Refer to
 *        https://scikit-learn.org/stable/auto_examples/tree/plot_unveil_tree_structure.html to
 *        learn the meaning of the arrays in detail. Note that this function can also be used to
 *        load an ensemble of extremely randomized trees (sklearn.ensemble.ExtraTreesRegressor).
 * \param n_estimators Number of trees in the random forest
 * \param n_features Number of features in the training data
 * \param n_targets Number of targets (outputs)
 * \param node_count node_count[i] stores the number of nodes in the i-th tree
 * \param children_left children_left[i][k] stores the ID of the left child node of node k of the
 *                      i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param children_right children_right[i][k] stores the ID of the right child node of node k of the
 *                       i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param feature feature[i][k] stores the ID of the feature used in the binary tree split at node k
 *                of the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param threshold threshold[i][k] stores the threshold used in the binary tree split at node k of
 *                  the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param value value[i][k] stores the leaf output of node k of the i-th tree. This is only defined
 *              if node k is a leaf node.
 * \param n_node_samples n_node_samples[i][k] stores the number of data samples associated with
 *                       node k of the i-th tree.
 * \param weighted_n_node_samples weighted_n_node_samples[i][k] stores the sum of weighted data
 *                                samples associated with node k of the i-th tree.
 * \param impurity impurity[i][k] stores the impurity measure (gini, entropy etc) associated with
 *                 node k of the i-th tree.
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadSKLearnRandomForestRegressor(int n_estimators, int n_features,
    int n_targets, int64_t const* node_count, int64_t const** children_left,
    int64_t const** children_right, int64_t const** feature, double const** threshold,
    double const** value, int64_t const** n_node_samples, double const** weighted_n_node_samples,
    double const** impurity, TreeliteModelHandle* out);
/*!
 * \brief Load a scikit-learn IsolationForest model from a collection of arrays. Refer to
 *        https://scikit-learn.org/stable/auto_examples/tree/plot_unveil_tree_structure.html to
 *        learn the meaning of the arrays in detail.
 * \param n_estimators Number of trees in the isolation forest
 * \param n_features Number of features in the training data
 * \param node_count node_count[i] stores the number of nodes in the i-th tree
 * \param children_left children_left[i][k] stores the ID of the left child node of node k of the
 *                      i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param children_right children_right[i][k] stores the ID of the right child node of node k of the
 *                       i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param feature feature[i][k] stores the ID of the feature used in the binary tree split at node k
 *                of the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param threshold threshold[i][k] stores the threshold used in the binary tree split at node k of
 *                  the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param value value[i][k] stores the expected isolation depth of node k of the i-th tree. This is
 *              only defined if node k is a leaf node.
 * \param n_node_samples n_node_samples[i][k] stores the number of data samples associated with
 *                       node k of the i-th tree.
 * \param weighted_n_node_samples weighted_n_node_samples[i][k] stores the sum of weighted data
 *                                samples associated with node k of the i-th tree.
 * \param impurity Not used, but must be passed as array of arrays for each tree and node.
 * \param ratio_c Standardizing constant to use for calculation of the anomaly score.
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadSKLearnIsolationForest(int n_estimators, int n_features,
    int64_t const* node_count, int64_t const** children_left, int64_t const** children_right,
    int64_t const** feature, double const** threshold, double const** value,
    int64_t const** n_node_samples, double const** weighted_n_node_samples, double const** impurity,
    double ratio_c, TreeliteModelHandle* out);
/*!
 * \brief Load a scikit-learn RandomForestClassifier model from a collection of arrays. Refer to
 *        https://scikit-learn.org/stable/auto_examples/tree/plot_unveil_tree_structure.html to
 *        learn the meaning of the arrays in detail. Note that this function can also be used to
 *        load an ensemble of extremely randomized trees (sklearn.ensemble.ExtraTreesClassifier).
 * \param n_estimators Number of trees in the random forest
 * \param n_features Number of features in the training data
 * \param n_targets Number of targets (outputs)
 * \param n_classes n_classes[i] stores the number of classes in the i-th target
 * \param node_count node_count[i] stores the number of nodes in the i-th tree
 * \param children_left children_left[i][k] stores the ID of the left child node of node k of the
 *                      i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param children_right children_right[i][k] stores the ID of the right child node of node k of the
 *                       i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param feature feature[i][k] stores the ID of the feature used in the binary tree split at node k
 *                of the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param threshold threshold[i][k] stores the threshold used in the binary tree split at node k of
 *                  the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param value value[i][k] stores the leaf output of node k of the i-th tree. This is only defined
 *              if node k is a leaf node.
 * \param n_node_samples n_node_samples[i][k] stores the number of data samples associated with
 *                       node k of the i-th tree.
 * \param weighted_n_node_samples weighted_n_node_samples[i][k] stores the sum of weighted data
 *                                samples associated with node k of the i-th tree.
 * \param impurity impurity[i][k] stores the impurity measure (gini, entropy etc) associated with
 *                 node k of the i-th tree.
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadSKLearnRandomForestClassifier(int n_estimators, int n_features,
    int n_targets, int32_t const* n_classes, int64_t const* node_count,
    int64_t const** children_left, int64_t const** children_right, int64_t const** feature,
    double const** threshold, double const** value, int64_t const** n_node_samples,
    double const** weighted_n_node_samples, double const** impurity, TreeliteModelHandle* out);
/*!
 * \brief Load a scikit-learn GradientBoostingRegressor model from a collection of arrays. Refer
 *        to https://scikit-learn.org/stable/auto_examples/tree/plot_unveil_tree_structure.html to
 *        learn the meaning of the arrays in detail.
 *        Note: GradientBoostingRegressor does not support multiple targets (outputs).
 * \param n_iter Number of boosting iterations
 * \param n_features Number of features in the training data
 * \param node_count node_count[i] stores the number of nodes in the i-th tree
 * \param children_left children_left[i][k] stores the ID of the left child node of node k of the
 *                      i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param children_right children_right[i][k] stores the ID of the right child node of node k of the
 *                       i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param feature feature[i][k] stores the ID of the feature used in the binary tree split at node k
 *                of the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param threshold threshold[i][k] stores the threshold used in the binary tree split at node k of
 *                  the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param value value[i][k] stores the leaf output of node k of the i-th tree. This is only defined
 *              if node k is a leaf node.
 * \param n_node_samples n_node_samples[i][k] stores the number of data samples associated with
 *                       node k of the i-th tree.
 * \param weighted_n_node_samples weighted_n_node_samples[i][k] stores the sum of weighted data
 *                                samples associated with node k of the i-th tree.
 * \param impurity impurity[i][k] stores the impurity measure (gini, entropy etc) associated with
 *                 node k of the i-th tree.
 * \param base_scores Baseline predictions for outputs. At prediction, margin scores will be
 *                    adjusted by this amount before applying the post-processing (link)
 *                    function. Required shape: (1,)
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadSKLearnGradientBoostingRegressor(int n_iter, int n_features,
    int64_t const* node_count, int64_t const** children_left, int64_t const** children_right,
    int64_t const** feature, double const** threshold, double const** value,
    int64_t const** n_node_samples, double const** weighted_n_node_samples, double const** impurity,
    double const* base_scores, TreeliteModelHandle* out);
/*!
 * \brief Load a scikit-learn GradientBoostingClassifier model from a collection of arrays. Refer
 *        to https://scikit-learn.org/stable/auto_examples/tree/plot_unveil_tree_structure.html to
 *        learn the meaning of the arrays in detail.
 *        Note: GradientBoostingClassifier does not support multiple targets (outputs).
 * \param n_iter Number of boosting iterations
 * \param n_features Number of features in the training data
 * \param n_classes Number of classes in the target variable
 * \param node_count node_count[i] stores the number of nodes in the i-th tree
 * \param children_left children_left[i][k] stores the ID of the left child node of node k of the
 *                      i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param children_right children_right[i][k] stores the ID of the right child node of node k of the
 *                       i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param feature feature[i][k] stores the ID of the feature used in the binary tree split at node k
 *                of the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param threshold threshold[i][k] stores the threshold used in the binary tree split at node k of
 *                  the i-th tree. This is only defined if node k is an internal (non-leaf) node.
 * \param value value[i][k] stores the leaf output of node k of the i-th tree. This is only defined
 *              if node k is a leaf node.
 * \param n_node_samples n_node_samples[i][k] stores the number of data samples associated with
 *                       node k of the i-th tree.
 * \param weighted_n_node_samples weighted_n_node_samples[i][k] stores the sum of weighted data
 *                                samples associated with node k of the i-th tree.
 * \param impurity impurity[i][k] stores the impurity measure (gini, entropy etc) associated with
 *                 node k of the i-th tree.
 * \param base_scores Baseline predictions for outputs. At prediction, margin scores will be
 *                    adjusted by this amount before applying the post-processing (link)
 *                    function. Required shape: (n_classes,)
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadSKLearnGradientBoostingClassifier(int n_iter, int n_features,
    int n_classes, int64_t const* node_count, int64_t const** children_left,
    int64_t const** children_right, int64_t const** feature, double const** threshold,
    double const** value, int64_t const** n_node_samples, double const** weighted_n_node_samples,
    double const** impurity, double const* base_scores, TreeliteModelHandle* out);

/*!
 * \brief Load a scikit-learn HistGradientBoostingRegressor model from a collection of arrays.
 *        Note: HistGradientBoostingRegressor does not support multiple targets (outputs).
 * \param n_iter Number of boosting iterations
 * \param n_features Number of features in the training data
 * \param node_count node_count[i] stores the number of nodes in the i-th tree
 * \param nodes nodes[i][k] stores the k-th node of the i-th tree.
 * \param expected_sizeof_node_struct Expected size of Node struct, in bytes
 * \param n_categorical_splits n_categorical_splits[i] stores the number of categorical splits
 *                             in the i-th tree.
 * \param raw_left_cat_bitsets raw_left_cat_bitsets[i][k] stores the bitmaps for node k of tree i.
 *                             The bitmaps are used to represent categorical tests.
 *                             Shape of raw_left_cat_bitsets[i]: (n_categorical_splits, 8)
 * \param known_cat_bitsets Bitsets representing the list of known categories per categorical
 *                          feature. Shape: (n_categorical_features, 8)
 * \param known_cat_bitsets_offset_map Map from an original feature index to the corresponding
 *                                     index in the known_cat_bitsets array. Shape: (n_features,)
 * \param features_map Mapping to re-order features. This is needed because HistGradientBoosting
 *                     estimator internally re-orders features using ColumnTransformer so that
 *                     the categorical features come before the numerical features.
 * \param categories_map Mapping to transform categorical features. This is needed because
 *                       HistGradientBoosting estimator embeds an OrdinalEncoder.
 *                       categories_map[i] represents the mapping for i-th categorical feature.
 * \param base_scores Baseline predictions for outputs. At prediction, margin scores will be
 *                    adjusted by this amount before applying the post-processing (link)
 *                    function. Required shape: (1,)
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadSKLearnHistGradientBoostingRegressor(int n_iter, int n_features,
    int64_t const* node_count, void const** nodes, int expected_sizeof_node_struct,
    uint32_t n_categorical_splits, uint32_t const** raw_left_cat_bitsets,
    uint32_t const* known_cat_bitsets, uint32_t const* known_cat_bitsets_offset_map,
    int32_t const* features_map, int64_t const** categories_map, double const* base_scores,
    TreeliteModelHandle* out);

/*!
 * \brief Load a scikit-learn HistGradientBoostingClassifier model from a collection of arrays.
 *        Note: HistGradientBoostingClassifier does not support multiple targets (outputs).
 * \param n_iter Number of boosting iterations
 * \param n_features Number of features in the training data
 * \param n_classes Number of classes in the target variable
 * \param node_count node_count[i] stores the number of nodes in the i-th tree
 * \param nodes nodes[i][k] stores the k-th node of the i-th tree.
 * \param expected_sizeof_node_struct Expected size of Node struct, in bytes
 * \param n_categorical_splits n_categorical_splits[i] stores the number of categorical splits
 *                             in the i-th tree.
 * \param raw_left_cat_bitsets raw_left_cat_bitsets[i][k] stores the bitmaps for node k of tree i.
 *                             The bitmaps are used to represent categorical tests.
 *                             Shape of raw_left_cat_bitsets[i]: (n_categorical_splits, 8)
 *
 * \param known_cat_bitsets Bitsets representing the list of known categories per categorical
 *                          feature. Shape: (n_categorical_features, 8)
 * \param known_cat_bitsets_offset_map Map from an original feature index to the corresponding
 *                                     index in the known_cat_bitsets array. Shape: (n_features,)
 * \param features_map Mapping to re-order features. This is needed because HistGradientBoosting
 *                     estimator internally re-orders features using ColumnTransformer so that
 *                     the categorical features come before the numerical features.
 * \param categories_map Mapping to transform categorical features. This is needed because
 *                       HistGradientBoosting estimator embeds an OrdinalEncoder.
 *                       categories_map[i] represents the mapping for i-th categorical feature.
 * \param base_scores Baseline predictions for outputs. At prediction, margin scores will be
 *                    adjusted by this amount before applying the post-processing (link)
 *                    function. Required shape: (1,) for binary classification;
 *                    (n_classes,) for multi-class classification
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteLoadSKLearnHistGradientBoostingClassifier(int n_iter, int n_features,
    int n_classes, int64_t const* node_count, void const** nodes, int expected_sizeof_node_struct,
    uint32_t n_categorical_splits, uint32_t const** raw_left_cat_bitsets,
    uint32_t const* known_cat_bitsets, uint32_t const* known_cat_bitsets_offset_map,
    int32_t const* features_map, int64_t const** categories_map, double const* base_scores,
    TreeliteModelHandle* out);
/*! \} */

/*!
 * \defgroup model_builder C API: Functions to build model objects
 * \{
 */
/*!
 * \brief Initialize a model builder object from a JSON string.
 *
 * The JSON string must contain all relevant metadata, including:
 * - threshold_type: Type of thresholds in the tree model
 * - leaf_output_type: Type of leaf outputs in the tree model
 * - metadata: Model metadata, consisting of following subfields:
 *   * num_feature: Number of features
 *   * task_type: Task type
 *   * average_tree_output: Whether to average outputs of trees
 *   * num_target: Number of targets
 *   * num_class: Number of classes. num_class[i] is the number of classes of target i.
 *   * leaf_vector_shape: Shape of the output from each leaf node
 * - tree_annotation: Annotation for individual trees, consisting of following subfields:
 *   * num_tree: Number of trees
 *   * target_id: target_id Target that each tree is associated with
 *   * class_id: Class that each tree is associated with
 * - postprocessor: Postprocessor for prediction outputs, consisting of following subfields:
 *   * name: Name of postprocessor
 *   * config_json: Optional JSON string to configure the postprocessor
 * - base_scores: Baseline scores for targets and classes, before adding tree outputs.
 *                Also known as the intercept.
 * - attributes: Arbitrary JSON object, to be stored in the "attributes" field in the
 *               model object.
 *
 * \param json_str JSON string containing relevant metadata.
 * \param out Model builder object
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteGetModelBuilder(char const* json_str, TreeliteModelBuilderHandle* out);
/*!
 * \brief Delete model builder object from memory
 * \param model_builder Model builder object to be deleted
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteDeleteModelBuilder(TreeliteModelBuilderHandle model_builder);
/*!
 * \brief Start a new tree
 * \param model_builder Model builder object
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderStartTree(TreeliteModelBuilderHandle model_builder);
/*!
 * \brief End the current tree
 * \param model_builder Model builder object
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderEndTree(TreeliteModelBuilderHandle model_builder);
/*!
 * \brief Start a new node
 * \param model_builder Model builder object
 * \param node_key Integer key that unique identifies the node.
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderStartNode(
    TreeliteModelBuilderHandle model_builder, int node_key);
/*!
 * \brief End the current node
 * \param model_builder Model builder object
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderEndNode(TreeliteModelBuilderHandle model_builder);
/*!
 * \brief Declare the current node as a numerical test node, where the test is of form
 *        [feature value] [cmp] [threshold]. Data points for which the test evaluates to True
 *        will be mapped to the left child node; all other data points (for which the test
 *        evaluates to False) will be mapped to the right child node.
 * \param model_builder Model builder object
 * \param split_index Feature ID
 * \param threshold Threshold
 * \param default_left Whether the missing value should be mapped to the left child
 * \param cmp Comparison operator
 * \param left_child_key Integer key that unique identifies the left child node.
 * \param right_child_key  Integer key that unique identifies the right child node.
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderNumericalTest(TreeliteModelBuilderHandle model_builder,
    int32_t split_index, double threshold, int default_left, char const* cmp, int left_child_key,
    int right_child_key);
/*!
 * \brief Declare the current node as a categorical test node, where the test is of form
 *        [feature value] \\in [category list].
 * \param model_builder Model builder object
 * \param split_index Feature ID
 * \param default_left Whether the missing value should be mapped to the left child
 * \param category_list List of categories to be tested for match
 * \param category_list_len Length of category_list
 * \param category_list_right_child Whether the data points for which the test evaluates to True
 *                                  should be mapped to the right child or the left child.
 * \param left_child_key Integer key that unique identifies the left child node.
 * \param right_child_key Integer key that unique identifies the right child node.
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderCategoricalTest(TreeliteModelBuilderHandle model_builder,
    int32_t split_index, int default_left, uint32_t const* category_list, size_t category_list_len,
    int category_list_right_child, int left_child_key, int right_child_key);
/*!
 * \brief Declare the current node as a leaf node with a scalar output
 * \param model_builder Model builder object
 * \param leaf_value Value of leaf output
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderLeafScalar(
    TreeliteModelBuilderHandle model_builder, double leaf_value);
/*!
 * \brief Declare the current node as a leaf node with a vector output (float32)
 * \param model_builder Model builder object
 * \param leaf_vector Value of leaf output
 * \param leaf_vector_len Length of leaf_vector
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderLeafVectorFloat32(
    TreeliteModelBuilderHandle model_builder, float const* leaf_vector, size_t leaf_vector_len);
/*!
 * \brief Declare the current node as a leaf node with a vector output (float64)
 * \param model_builder Model builder object
 * \param leaf_vector Value of leaf output
 * \param leaf_vector_len Length of leaf_vector
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderLeafVectorFloat64(
    TreeliteModelBuilderHandle model_builder, double const* leaf_vector, size_t leaf_vector_len);
/*!
 * \brief Specify the gain (loss reduction) that's resulted from the current split.
 * \param model_builder Model builder object
 * \param gain Gain (loss reduction)
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderGain(TreeliteModelBuilderHandle model_builder, double gain);
/*!
 * \brief Specify the number of data points (samples) that are mapped to the current node.
 * \param model_builder Model builder object
 * \param data_count Number of data points
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderDataCount(
    TreeliteModelBuilderHandle model_builder, uint64_t data_count);
/*!
 * \brief Specify the weighted sample count or the sum of Hessians for the data points that
 *        are mapped to the current node.
 * \param model_builder Model builder object
 * \param sum_hess Weighted sample count or the sum of Hessians
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteModelBuilderSumHess(
    TreeliteModelBuilderHandle model_builder, double sum_hess);
/*!
 * \brief Conclude model building and obtain the final model object.
 * \param model_builder Model builder object
 * \param out Final model object
 */
TREELITE_DLL int TreeliteModelBuilderCommitModel(
    TreeliteModelBuilderHandle model_builder, TreeliteModelHandle* out);
/*! \} */

/*!
 * \defgroup model_manager C API: Functions to query and modify model objects
 * \{
 */
/*!
 * \brief Dump a model object as a JSON string
 * \param handle The handle to the model object
 * \param pretty_print Whether to pretty-print JSON string (0 for false, != 0 for true)
 * \param out_json_str The JSON string
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteDumpAsJSON(
    TreeliteModelHandle handle, int pretty_print, char const** out_json_str);
/*!
 * \brief Query the input type of a Treelite model object
 * \param model Treelite Model object
 * \param out_str String representation of input type
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGetInputType(TreeliteModelHandle model, char const** out_str);
/*!
 * \brief Query the output type of a Treelite model object
 * \param model Treelite Model object
 * \param out_str String representation of output type
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGetOutputType(TreeliteModelHandle model, char const** out_str);
/*!
 * \brief Query the number of trees in the model
 * \param model Model to query
 * \param out Number of trees
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteQueryNumTree(TreeliteModelHandle model, size_t* out);
/*!
 * \brief Query the number of features used in the model
 * \param model Model to query
 * \param out Number of features
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteQueryNumFeature(TreeliteModelHandle model, int* out);
/*!
 * \brief Concatenate multiple model objects into a single model object by copying
 *        all member trees into the destination model object
 * \param objs Pointer to the beginning of the list of model objects
 * \param len Number of model objects
 * \param out Used to save the concatenated model
 */
TREELITE_DLL int TreeliteConcatenateModelObjects(
    TreeliteModelHandle const* objs, size_t len, TreeliteModelHandle* out);
/*!
 * \brief Delete model from memory
 * \param handle Model to remove
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteFreeModel(TreeliteModelHandle handle);
/*! \} */

/*!
 * \defgroup serializer C API: Functions to serialize model objects
 * \{
 */
/*!
 * \brief Serialize (persist) a model object to disk
 * \param handle Handle to the model object
 * \param filename Name of the file to which to serialize the model. The file will be using a
 *                 binary format that's optimized to store the Treelite model object efficiently.
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteSerializeModelToFile(TreeliteModelHandle handle, char const* filename);
/*!
 * \brief Deserialize (load) a model object from disk
 * \param filename Name of the file from which to deserialize the model. The file should be created
 *                 by a call to \ref TreeliteSerializeModelToFile.
 * \param out Handle to the model object
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteDeserializeModelFromFile(char const* filename, TreeliteModelHandle* out);
/*!
 * \brief Serialize (persist) a model object to a byte sequence
 * \param handle Handle to the model object
 * \param out_bytes Byte sequence containing serialized model
 * \param out_bytes_len Length of out_bytes
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteSerializeModelToBytes(
    TreeliteModelHandle handle, char const** out_bytes, size_t* out_bytes_len);
/*!
 * \brief Deserialize (load) a model object from a byte sequence
 * \param bytes Byte sequence containing serialized model. The string should be created by a call to
 *              \ref TreeliteSerializeModelToBytes.
 * \param bytes_len Length of bytes
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteDeserializeModelFromBytes(
    char const* bytes, size_t bytes_len, TreeliteModelHandle* out);

/*!
 * \brief Serialize a model object using the Python buffer protocol (PEP 3118).
 * \param handle Handle to the model object
 * \param out_frames Pointer to buffer frames
 * \param out_num_frames Number of buffer frames
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteSerializeModelToPyBuffer(
    TreeliteModelHandle handle, TreelitePyBufferFrame** out_frames, size_t* out_num_frames);
/*!
 * \brief Deserialize a model object using the Python buffer protocol (PEP 3118).
 * \param frames Buffer frames
 * \param num_frames Number of buffer frames
 * \param out Loaded model
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteDeserializeModelFromPyBuffer(
    TreelitePyBufferFrame* frames, size_t num_frames, TreeliteModelHandle* out);
/*! \} */

/*!
 * \defgroup gtil C API: General Tree Inference Library (GTIL)
 * GTIL provides a reference implementation for predicting with decision trees.
 * \{
 */

/*!
 * \brief Load a configuration for GTIL predictor from a JSON string.
 * \param config_json a JSON string with the following fields:
 *   - "nthread" (optional): Number of threads used for initializing DMatrix.
 *                           Set <= 0 to use all CPU cores.
 *   - "predict_type" (required): Must be one of the following.
 *     - "default": Sum over trees and apply post-processing
 *     - "raw": Sum over trees, but don't apply post-processing; get raw margin scores instead.
 *     - "leaf_id": Output one (integer) leaf ID per tree.
 *     - "score_per_tree": Output one or more margin scores per tree.
 * \param out Parsed configuration
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGTILParseConfig(char const* config_json, TreeliteGTILConfigHandle* out);

/*!
 * \brief Delete a GTIL configuration from memory
 * \param handle Handle to the GTIL configuration to be deleted
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGTILDeleteConfig(TreeliteGTILConfigHandle handle);

/*!
 * \brief Given a data matrix, query the necessary shape of array to hold predictions for all
 *        data points.
 * \param model Treelite Model object
 * \param num_row Number of rows in the input
 * \param config Configuration of GTIL predictor. Set this by calling \ref TreeliteGTILParseConfig.
 * \param out_shape Array of dimensions
 * \param out_ndim Number of dimensions in out_shape
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGTILGetOutputShape(TreeliteModelHandle model, uint64_t num_row,
    TreeliteGTILConfigHandle config, uint64_t const** out, uint64_t* out_ndim);

/*!
 * \brief Predict with a 2D dense array
 * \param model Treelite Model object
 * \param input The 2D data array, laid out in row-major layout
 * \param input_type Data type of the data matrix
 * \param num_row Number of rows in the data matrix.
 * \param output Pointer to buffer to store the output. Call \ref TreeliteGTILGetOutputShape to get
 *               the amount of buffer you should allocate for this parameter.
 * \param config Configuration of GTIL predictor. Set this by calling \ref TreeliteGTILParseConfig.
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGTILPredict(TreeliteModelHandle model, void const* input,
    char const* input_type, uint64_t num_row, void* output, TreeliteGTILConfigHandle config);

/*!
 * \brief Predict with sparse data with CSR (compressed sparse row) layout.
 *
 * In the CSR layout, data[row_ptr[i]:row_ptr[i+1]] store the nonzero entries of row i, and
 * col_ind[row_ptr[i]:row_ptr[i+1]] stores the corresponding column indices.
 *
 * \param model Treelite Model object
 * \param data Nonzero elements in the data matrix
 * \param input_type Data type of the data matrix
 * \param col_ind Feature indices. col_ind[i] indicates the feature index associated with data[i].
 * \param row_ptr Pointer to row headers. Length is [num_row] + 1.
 * \param num_row Number of rows in the data matrix.
 * \param output Pointer to buffer to store the output. Call \ref GetOutputShape to get
 *               the amount of buffer you should allocate for this parameter.
 * \param config Configuration of GTIL predictor. Set this by calling \ref TreeliteGTILParseConfig.
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGTILPredictSparse(TreeliteModelHandle model, void const* data,
    char const* input_type, uint64_t const* col_ind, uint64_t const* row_ptr, uint64_t num_row,
    void* output, TreeliteGTILConfigHandle config);

/*! \} */

/*!
 * \defgroup accessor C API: Getters and setters for tree fields
 * The getter and setter methods are useful for modifying a tree model in-place. This interface
 * should be used by an expert user, as the interface performs no validation for the setter.
 * \{
 */
/*!
 * \brief Get a field in the header.
 *
 * This function returns the requested field using the Python buffer protocol (PEP 3118).
 * \param model Treelite Model object
 * \param name Name of the field
 * \param out_frame Buffer frame representing the requested field
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGetHeaderField(
    TreeliteModelHandle model, char const* name, TreelitePyBufferFrame* out_frame);
/*!
 * \brief Get a field in a tree.
 *
 * This function returns the requested field using the Python buffer protocol (PEP 3118).
 * \param model Treelite Model object
 * \param tree_id ID of the tree
 * \param name Name of the field
 * \param out_frame Buffer frame representing the requested field
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteGetTreeField(TreeliteModelHandle model, uint64_t tree_id, char const* name,
    TreelitePyBufferFrame* out_frame);
/*!
 * \brief Set a field in the header.
 *
 * This function accepts the field's new value using the Python buffer protocol (PEP 3118).
 * \param model Treelite Model object
 * \param name Name of the field
 * \param frame Buffer frame representing the new value for the field
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteSetHeaderField(
    TreeliteModelHandle model, char const* name, TreelitePyBufferFrame frame);
/*!
 * \brief Set a field in a tree.
 *
 * This function accepts the field's new value using the Python buffer protocol (PEP 3118).
 * \param model Treelite Model object
 * \param tree_id ID of the tree
 * \param name Name of the field
 * \param frame Buffer frame representing the new value for the field
 * \return 0 for success; -1 for failure
 */
TREELITE_DLL int TreeliteSetTreeField(
    TreeliteModelHandle model, uint64_t tree_id, char const* name, TreelitePyBufferFrame frame);
/*! \} */

/*!
 * \brief Display last error; can be called by multiple threads
 * Note. Each thread will get the last error occured in its own context.
 * \return Error string
 */
TREELITE_DLL char const* TreeliteGetLastError(void);

/*!
 * \brief Register callback function for LOG(INFO) messages -- helpful messages
 *        that are not errors.
 * Note: This function can be called by multiple threads. The callback function
 *       will run on the thread that registered it
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteRegisterLogCallback(void (*callback)(char const*));

/*!
 * \brief Register callback function for LOG(WARNING) messages
 * Note: This function can be called by multiple threads. The callback function
 *       will run on the thread that registered it
 * \return 0 for success, -1 for failure
 */
TREELITE_DLL int TreeliteRegisterWarningCallback(void (*callback)(char const*));

#ifdef __cplusplus
extern "C" {
extern char const* TREELITE_VERSION;
}
#else
extern char const* TREELITE_VERSION;
#endif

#endif  // TREELITE_C_API_H_
