/**
 * @file core/tree/rectangle_tree/r_plus_plus_tree_split_policy.hpp
 * @author Mikhail Lozhnikov
 *
 * Definition and implementation of the RPlusPlusTreeSplitPolicy class, a class
 * that helps to determine the subtree into which we should insert an
 * intermediate node.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_SPLIT_POLICY_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_SPLIT_POLICY_HPP

namespace mlpack {
namespace tree {

/**
 * The RPlusPlusTreeSplitPolicy helps to determine the subtree into which
 * we should insert a child of an intermediate node that is being split.
 * This class is designed for the R++ tree.
 */
class RPlusPlusTreeSplitPolicy
{
 public:
  //! Indicate that the child should be split.
  static const int SplitRequired = 0;
  //! Indicate that the child should be inserted to the first subtree.
  static const int AssignToFirstTree = 1;
  //! Indicate that the child should be inserted to the second subtree.
  static const int AssignToSecondTree = 2;

  /**
   * This method returns SplitRequired if a child of an intermediate node should
   * be split, AssignToFirstTree if the child should be inserted to the first
   * subtree, AssignToSecondTree if the child should be inserted to the second
   * subtree. The method makes desicion according to the maximum bounding
   * rectangle of the child, the axis along which the intermediate node is being
   * split and the coordinate at which the node is being split.
   *
   * @param child A child of the node that is being split.
   * @param axis The axis along which the node is being split.
   * @param cut The coordinate at which the node is being split.
   */
  template<typename TreeType>
  static int GetSplitPolicy(const TreeType& child,
                            const size_t axis,
                            const typename TreeType::ElemType cut)
  {
    if (child.AuxiliaryInfo().OuterBound()[axis].Hi() <= cut)
      return AssignToFirstTree;
    else if (child.AuxiliaryInfo().OuterBound()[axis].Lo() >= cut)
      return AssignToSecondTree;

    return SplitRequired;
  }

  /**
   * Return the maximum bounding rectangle of the node.
   * This method should always return the bound that is used for the
   * decision-making in GetSplitPolicy().
   *
   * @param node The node whose bound is requested.
   */
  template<typename TreeType>
  static const
      bound::HRectBound<metric::EuclideanDistance, typename TreeType::ElemType>&
          Bound(const TreeType& node)
  {
    return node.AuxiliaryInfo().OuterBound();
  }
};

} //  namespace tree
} //  namespace mlpack

#endif //  MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_SPLIT_POLICY_HPP
