text:
/** \author Jia Pan */

#ifndef FCL_TRAVERSAL_NODE_OCTREE_H
#define FCL_TRAVERSAL_NODE_OCTREE_H

#include "fcl/collision_data.h"
#include "fcl/traversal/traversal_node_base.h"
#include "fcl/narrowphase/narrowphase.h"
#include "fcl/shape/geometric_shapes_utility.h"
#include "fcl/octree.h"
#include "fcl/BVH/BVH_model.h"

namespace fcl
{

/// @brief Algorithms for collision related with octree
template<typename NarrowPhaseSolver>
class OcTreeSolver
{
private:
  const NarrowPhaseSolver* solver;

  mutable const CollisionRequest* crequest;
  mutable const DistanceRequest* drequest;

  mutable CollisionResult* cresult;
  mutable DistanceResult* dresult;

public:
  OcTreeSolver(const NarrowPhaseSolver* solver_) : solver(solver_),
                                                   crequest(NULL),
                                                   drequest(NULL),
                                                   cresult(NULL),
                                                   dresult(NULL)
  {
  }

  /// @brief collision between two octrees
  void OcTreeIntersect(const OcTree* tree1, const OcTree* tree2,
                       const Transform3f& tf1, const Transform3f& tf2,
                       const CollisionRequest& request_,
                       CollisionResult& result_) const
  {
    crequest = &request_;
    cresult = &result_;

    OcTreeIntersectRecurse(tree1, tree1->getRoot(), tree1->getRootBV(),
                           tree2, tree2->getRoot(), tree2->getRootBV(),
                           tf1, tf2);
  }

  /// @brief distance between two octrees
  void OcTreeDistance(const OcTree* tree1, const OcTree* tree2,
                      const Transform3f& tf1, const Transform3f& tf2,
                      const DistanceRequest& request_,
                      DistanceResult& result_) const
  {
    drequest = &request_;
    dresult = &result_;

    OcTreeDistanceRecurse(tree1, tree1->getRoot(), tree1->getRootBV(),
                          tree2, tree2->getRoot(), tree2->getRootBV(),
                          tf1, tf2);
  }

  /// @brief collision between octree and mesh
  template<typename BV>
  void OcTreeMeshIntersect(const OcTree* tree1, const BVHModel<BV>* tree2,
                           const Transform3f& tf1, const Transform3f& tf2,
                           const CollisionRequest& request_,
                           CollisionResult& result_) const
  {
    crequest = &request_;
    cresult = &result_;

    OcTreeMeshIntersectRecurse(tree1, tree1->getRoot(), tree1->getRootBV(),
                               tree2, 0,
                               tf1, tf2);
  }

  /// @brief distance between octree and mesh
  template<typename BV>
  void OcTreeMeshDistance(const OcTree* tree1, const BVHModel<BV>* tree2,
                          const Transform3f& tf1, const Transform3f& tf2,
                          const DistanceRequest& request_,
                          DistanceResult& result_) const
  {
    drequest = &request_;
    dresult = &result_;

    OcTreeMeshDistanceRecurse(tree1, tree1->getRoot(), tree1->getRootBV(),
                              tree2, 0,
                              tf1, tf2);
  }

  /// @brief collision between mesh and octree
  template<typename BV>
  void MeshOcTreeIntersect(const BVHModel<BV>* tree1, const OcTree* tree2,
                           const Transform3f& tf1, const Transform3f& tf2,
                           const CollisionRequest& request_,
                           CollisionResult& result_) const

  {
    crequest = &request_;
    cresult = &result_;

    OcTreeMeshIntersectRecurse(tree2, tree2->getRoot(), tree2->getRootBV(),
                               tree1, 0,
                               tf2, tf1);
  }

  /// @brief distance between mesh and octree
  template<typename BV>
  void MeshOcTreeDistance(const BVHModel<BV>* tree1, const OcTree* tree2,
                          const Transform3f& tf1, const Transform3f& tf2,
                          const DistanceRequest& request_,
                          DistanceResult& result_) const
  {
    drequest = &request_;
    dresult = &result_;

    OcTreeMeshDistanceRecurse(tree1, 0,
                              tree2, tree2->getRoot(), tree2->getRootBV(),
                              tf1, tf2);
  }

  /// @brief collision between octree and shape
  template<typename S>
  void OcTreeShapeIntersect(const OcTree* tree, const S& s,
                            const Transform3f& tf1, const Transform3f& tf2,
                            const CollisionRequest& request_,
                            CollisionResult& result_) const
  {
    crequest = &request_;
    cresult = &result_;

    AABB bv2;
    computeBV<AABB>(s, Transform3f(), bv2);
    OBB obb2;
    convertBV(bv2, tf2, obb2);
    OcTreeShapeIntersectRecurse(tree, tree->getRoot(), tree->getRootBV(),
                                s, obb2,
                                tf1, tf2);

  }

  /// @brief collision between shape and octree
  template<typename S>
  void ShapeOcTreeIntersect(const S& s, const OcTree* tree,
                            const Transform3f& tf1, const Transform3f& tf2,
                            const CollisionRequest& request_,
                            CollisionResult& result_) const
  {
    crequest = &request_;
    cresult = &result_;

    AABB bv1;
    computeBV<AABB>(s, Transform3f(), bv1);
    OBB obb1;
    convertBV(bv1, tf1, obb1);
    OcTreeShapeIntersectRecurse(tree, tree->getRoot(), tree->getRootBV(),
                                s, obb1,
                                tf2, tf1);
  }

  /// @brief distance between octree and shape
  template<typename S>
  void OcTreeShapeDistance(const OcTree* tree, const S& s,
                           const Transform3f& tf1, const Transform3f& tf2,
                           const DistanceRequest& request_,
                           DistanceResult& result_) const
  {
    drequest = &request_;
    dresult = &result_;

    AABB aabb2;
    computeBV<AABB>(s, tf2, aabb2);
    OcTreeShapeDistanceRecurse(tree, tree->getRoot(), tree->getRootBV(),
                               s, aabb2,
                               tf1, tf2);
  }

  /// @brief distance between shape and octree
  template<typename S>
  void ShapeOcTreeDistance(const S& s, const OcTree* tree,
                           const Transform3f& tf1, const Transform3f& tf2,
                           const DistanceRequest& request_,
                           DistanceResult& result_) const
  {
    drequest = &request_;
    dresult = &result_;

    AABB aabb1;
    computeBV<AABB>(s, tf1, aabb1);
    OcTreeShapeDistanceRecurse(tree, tree->getRoot(), tree->getRootBV(),
                               s, aabb1,
                               tf2, tf1);
  }

private:
  template<typename S>
  bool OcTreeShapeDistanceRecurse(const OcTree* tree1, const OcTree::OcTreeNode* root1, const AABB& bv1,
                                  const S& s, const AABB& aabb2,
                                  const Transform3f& tf1, const Transform3f& tf2) const
  {
    if(!root1->hasChildren())
    {
      if(tree1->isNodeOccupied(root1))
      {
        Box box;
        Transform3f box_tf;
        constructBox(bv1, tf1, box, box_tf);

        FCL_REAL dist;
        Vec3f closest_p1, closest_p2;
        solver->shapeDistance(box, box_tf, s, tf2, &dist, &closest_p1, &closest_p2);

        dresult->update(dist, tree1, &s, root1 - tree1->getRoot(), DistanceResult::NONE, closest_p1, closest_p2);

        return drequest->isSatisfied(*dresult);
      }
      else
        return false;
    }

    if(!tree1->isNodeOccupied(root1)) return false;

    for(unsigned int i = 0; i < 8; ++i)
    {
      if(root1->childExists(i))
      {
        const OcTree::OcTreeNode* child = root1->getChild(i);
        AABB child_bv;
        computeChildBV(bv1, i, child_bv);

        AABB aabb1;
        convertBV(child_bv, tf1, aabb1);
        FCL_REAL d = aabb1.distance(aabb2);
        if(d < dresult->min_distance)
        {
          if(OcTreeShapeDistanceRecurse(tree1, child, child_bv, s, aabb2, tf1, tf2))
            return true;
        }
      }
    }

    return false;
  }

  template<typename S>
  bool OcTreeShapeIntersectRecurse(const OcTree* tree1, const OcTree::OcTreeNode* root1, const AABB& bv1,
                                   const S& s, const OBB& obb2,
                                   const Transform3f& tf1, const Transform3f& tf2) const
  {
    if(!root1)
    {
      OBB obb1;
      convertBV(bv1, tf1, obb1);
      if(obb1.overlap(obb2))
      {
        Box box;
        Transform3f box_tf;
        constructBox(bv1, tf1, box, box_tf);

        if(solver->shapeIntersect(box, box_tf, s, tf2, NULL))
        {
          AABB overlap_part;
          AABB aabb1, aabb2;
          computeBV<AABB, Box>(box, box_tf, aabb1);
          computeBV<AABB, S>(s, tf2, aabb2);
          aabb1.overlap(aabb2, overlap_part);
          cresult->addCostSource(CostSource(overlap_part, tree1->getOccupancyThres() * s.cost_density), crequest->num_max_cost_sources);
        }
      }

      return false;
    }
    else if(!root1->hasChildren())
    {
      if(tree1->isNodeOccupied(root1) && s.isOccupied()) // occupied area
      {
        OBB obb1;
        convertBV(bv1, tf1, obb1);
        if(obb1.overlap(obb2))
        {
          Box box;
          Transform3f box_tf;
          constructBox(bv1, tf1, box, box_tf);

          bool is_intersect = false;
          if(!crequest->enable_contact)
          {
            if(solver->shapeIntersect(box, box_tf, s, tf2, NULL))
            {
              is_intersect = true;
              if(cresult->numContacts() < crequest->num_max_contacts)
                cresult->addContact(Contact(tree1, &s, root1 - tree1->getRoot(), Contact::NONE));
            }
          }
          else
          {
            std::vector<ContactPoint> contacts;
            if(solver->shapeIntersect(box, box_tf, s, tf2, &contacts))
            {
              is_intersect = true;
              if(crequest->num_max_contacts > cresult->numContacts())
              {
                const size_t free_space = crequest->num_max_contacts - cresult->numContacts();
                size_t num_adding_contacts;

                // If the free space is not enough to add all the new contacts, we add contacts in descent order of penetration depth.
                if (free_space < contacts.size())
                {
                  std::partial_sort(contacts.begin(), contacts.begin() + free_space, contacts.end(), std::bind(comparePenDepth, std::placeholders::_2, std::placeholders::_1));
                  num_adding_contacts = free_space;
                }
                else
                {
                  num_adding_contacts = contacts.size();
                }

                for(size_t i = 0; i < num_adding_contacts; ++i)
                  cresult->addContact(Contact(tree1, &s, root1 - tree1->getRoot(), Contact::NONE, contacts[i].pos, contacts[i].normal, contacts[i].penetration_depth));
              }
            }
          }

          if(is_intersect && crequest->enable_cost)
          {
            AABB overlap_part;
            AABB aabb1, aabb2;
            computeBV<AABB, Box>(box, box_tf, aabb1);
            computeBV<AABB, S>(s, tf2, aabb2);
            aabb1.overlap(aabb2, overlap_part);
          }

          return crequest->isSatisfied(*cresult);
        }
        else return false;
      }
      else if(!tree1->isNodeFree(root1) && !s.isFree() && crequest->enable_cost) // uncertain area
      {
        OBB obb1;
        convertBV(bv1, tf1, obb1);
        if(obb1.overlap(obb2))
        {
          Box box;
          Transform3f box_tf;
          constructBox(bv1, tf1, box, box_tf);

          if(solver->shapeIntersect(box, box_tf, s, tf2, NULL))
          {
            AABB overlap_part;
            AABB aabb1, aabb2;
            computeBV<AABB, Box>(box, box_tf, aabb1);
            computeBV<AABB, S>(s, tf2, aabb2);
            aabb1.overlap(aabb2, overlap_part);
          }
        }

        return false;
      }
      else // free area
        return false;
    }

    /// stop when 1) bounding boxes of two objects not overlap; OR
    ///           2) at least of one the nodes is free; OR
    ///           2) (two uncertain nodes or one node occupied and one node uncertain) AND cost not required
    if(tree1->isNodeFree(root1) || s.isFree()) return false;
    else if((tree1->isNodeUncertain(root1) || s.isUncertain()) && !crequest->enable_cost) return false;
    else
    {
      OBB obb1;
      convertBV(bv1, tf1, obb1);
      if(!obb1.overlap(obb2)) return false;
    }

    for(unsigned int i = 0; i < 8; ++i)
    {
      if(root1->childExists(i))
      {
        const OcTree::OcTreeNode* child = root1->getChild(i);
        AABB child_bv;
        computeChildBV(bv1, i, child_bv);

        if(OcTreeShapeIntersectRecurse(tree1, child, child_bv, s, obb2, tf1, tf2))
          return true;
      }
      else if(!s.isFree() && crequest->enable_cost)
      {
        AABB child_bv;
        computeChildBV(bv1, i, child_bv);

        if(OcTreeShapeIntersectRecurse(tree1, NULL, child_bv, s, obb2, tf1, tf2))
          return true;
      }
    }

    return false;
  }

  template<typename BV>
  bool OcTreeMeshDistanceRecurse(const OcTree* tree1, const OcTree::OcTreeNode* root1, const AABB& bv1,
                                 const BVHModel<BV>* tree2, int root2,
                                 const Transform3f& tf1, const Transform3f& tf2) const
  {
    if(!root1->hasChildren() && tree2->getBV(root2).isLeaf())
    {
      if(tree1->isNodeOccupied(root1))
      {
        Box box;
        Transform3f box_tf;
        constructBox(bv1, tf1, box, box_tf);

        int primitive_id = tree2->getBV(root2).primitiveId();
        const Triangle& tri_id = tree2->tri_indices[primitive_id];
        const Vec3f& p1 = tree2->vertices[tri_id[0]];
        const Vec3f& p2 = tree2->vertices[tri_id[1]];
        const Vec3f& p3 = tree2->vertices[tri_id[2]];

        FCL_REAL dist;
        Vec3f closest_p1, closest_p2;
        solver->shapeTriangleDistance(box, box_tf, p1, p2, p3, tf2, &dist, &closest_p1, &closest_p2);

        dresult->update(dist, tree1, tree2, root1 - tree1->getRoot(), primitive_id);

        return drequest->isSatisfied(*dresult);
      }
      else
        return false;
    }

    if(!tree1->isNodeOccupied(root1)) return false;

    if(tree2->getBV(root2).isLeaf() || (root1->hasChildren() && (bv1.size() > tree2->getBV(root2).bv.size())))
    {
      for(unsigned int i = 0; i < 8; ++i)
      {
        if(root1->childExists(i))
        {
          const OcTree::OcTreeNode* child = root1->getChild(i);
          AABB child_bv;
          computeChildBV(bv1, i, child_bv);

          FCL_REAL d;
          AABB aabb1, aabb2;
          convertBV(child_bv, tf1, aabb1);
          convertBV(tree2->getBV(root2).bv, tf2, aabb2);
          d = aabb1.distance(aabb2);

          if(d < dresult->min_distance)
          {
            if(OcTreeMeshDistanceRecurse(tree1, child, child_bv, tree2, root2, tf1, tf2))
              return true;
          }
        }
      }
    }
    else
    {
      FCL_REAL d;
      AABB aabb1, aabb2;
      convertBV(bv1, tf1, aabb1);
      int child = tree2->getBV(root2).leftChild();
      convertBV(tree2->getBV(child).bv, tf2, aabb2);
      d = aabb1.distance(aabb2);

      if(d < dresult->min_distance)
      {
        if(OcTreeMeshDistanceRecurse(tree1, root1, bv1, tree2, child, tf1, tf2))
          return true;
      }

      child = tree2->getBV(root2).rightChild();
      convertBV(tree2->getBV(child).bv, tf2, aabb2);
      d = aabb1.distance(aabb2);

      if(d < dresult->min_distance)
      {
        if(OcTreeMeshDistanceRecurse(tree1, root1, bv1, tree2, child, tf1, tf2))
          return true;
      }
    }

    return false;
  }

  template<typename BV>
  bool OcTreeMeshIntersectRecurse(const OcTree* tree1, const OcTree::OcTreeNode* root1, const AABB& bv1,
                                  const BVHModel<BV>* tree2, int root2,
                                  const Transform3f& tf1, const Transform3f& tf2) const
  {
    if(!root1)
    {
      if(tree2->getBV(root2).isLeaf())
      {
        OBB obb1, obb2;
        convertBV(bv1, tf1, obb1);
        convertBV(tree2->getBV(root2).bv, tf2, obb2);
        if(obb1.overlap(obb2))
        {
          Box box;
          Transform3f box_tf;
          constructBox(bv1, tf1, box, box_tf);

          int primitive_id = tree2->getBV(root2).primitiveId();
          const Triangle& tri_id = tree2->tri_indices[primitive_id];
          const Vec3f& p1 = tree2->vertices[tri_id[0]];
          const Vec3f& p2 = tree2->vertices[tri_id[1]];
          const Vec3f& p3 = tree2->vertices[tri_id[2]];

          if(solver->shapeTriangleIntersect(box, box_tf, p1, p2, p3, tf2, NULL, NULL, NULL))
          {
            AABB overlap_part;
            AABB aabb1;
            computeBV<AABB, Box>(box, box_tf, aabb1);
            AABB aabb2(tf2.transform(p1), tf2.transform(p2), tf2.transform(p3));
            aabb1.overlap(aabb2, overlap_part);
            cresult->addCostSource(CostSource(overlap_part, tree1->getOccupancyThres() * tree2->cost_density), crequest->num_max_cost_sources);
          }
        }

        return false;
      }
      else
      {
        if(OcTreeMeshIntersectRecurse(tree1, root1, bv1, tree2, tree2->getBV(root2).leftChild(), tf1, tf2))
          return true;

        if(OcTreeMeshIntersectRecurse(tree1, root1, bv1, tree2, tree2->getBV(root2).rightChild(), tf1, tf2))
          return true;

        return false;
      }
    }
    else if(!root1->hasChildren() && tree2->getBV(root2).isLeaf())
    {
      if(tree1->isNodeOccupied(root1) && tree2->isOccupied())
      {
        OBB obb1, obb2;
        convertBV(bv1, tf1, obb1);
        convertBV(tree2->getBV(root2).bv, tf2, obb2);
        if(obb1.overlap(obb2))
        {
          Box box;
          Transform3f box_tf;
          constructBox(bv1, tf1, box, box_tf);

          int primitive_id = tree2->getBV(root2).primitiveId();
          const Triangle& tri_id = tree2->tri_indices[primitive_id];
          const Vec3f& p1 = tree2->vertices[tri_id[0]];
          const Vec3f& p2 = tree2->vertices[tri_id[1]];
          const Vec3f& p3 = tree2->vertices[tri_id[2]];

          bool is_intersect = false;
          if(!crequest->enable_contact)
          {
            if(solver->shapeTriangleIntersect(box, box_tf, p1, p2, p3, tf2, NULL, NULL, NULL))
            {
              is_intersect = true;
              if(cresult->numContacts() < crequest->num_max_contacts)
                cresult->addContact(Contact(tree1, tree2, root1 - tree1->getRoot(), root2));
            }
          }
          else
          {
            Vec3f contact;
            FCL_REAL depth;
            Vec3f normal;

            if(solver->shapeTriangleIntersect(box, box_tf, p1, p2, p3, tf2, &contact, &depth, &normal))
            {
              is_intersect = true;
              if(cresult->numContacts() < crequest->num_max_contacts)
                cresult->addContact(Contact(tree1, tree2, root1 - tree1->getRoot(), root2, contact, normal, depth));
            }
          }

          if(is_intersect && crequest->enable_cost)
          {
            AABB overlap_part;
            AABB aabb1;
            computeBV<AABB, Box>(box, box_tf, aabb1);
            AABB aabb2(tf2.transform(p1), tf2.transform(p2), tf2.transform(p3));
            aabb1.overlap(aabb2, overlap_part);
	    cresult->addCostSource(CostSource(overlap_part, root1->getOccupancy() * tree2->cost_density), crequest->num_max_cost_sources);
          }

          return crequest->isSatisfied(*cresult);
        }
        else
          return false;
      }
      else if(!tree1->isNodeFree(root1) && !tree2->isFree() && crequest->enable_cost) // uncertain area
      {
        OBB obb1, obb2;
        convertBV(bv1, tf1, obb1);
        convertBV(tree2->getBV(root2).bv, tf2, obb2);
        if(obb1.overlap(obb2))
        {
          Box box;
          Transform3f box_tf;
          constructBox(bv1, tf1, box, box_tf);

          int primitive_id = tree2->getBV(root2).primitiveId();
          const Triangle& tri_id = tree2->tri_indices[primitive_id];
          const Vec3f& p1 = tree2->vertices[tri_id[0]];
          const Vec3f& p2 = tree2->vertices[tri_id[1]];
          const Vec3f& p3 = tree2->vertices[tri_id[2]];

          if(solver->shapeTriangleIntersect(box, box_tf, p1, p2, p3, tf2, NULL, NULL, NULL))
          {
            AABB overlap_part;
            AABB aabb1;
            computeBV<AABB, Box>(box, box_tf, aabb1);
            AABB aabb2(tf2.transform(p1), tf2.transform(p2), tf2.transform(p3));
            aabb1.overlap(aabb2, overlap_part);
	    cresult->addCostSource(CostSource(overlap_part, root1->getOccupancy() * tree2->cost_density), crequest->num_max_cost_sources);
          }
        }

        return false;
      }
      else // free area
        return false;
    }

    /// stop when 1) bounding boxes of two objects not overlap; OR
    ///           2) at least one of the nodes is free; OR
    ///           2) (two uncertain nodes OR one node occupied and one node uncertain) AND cost not required
    if(tree1->isNodeFree(root1) || tree2->isFree()) return false;
    else if((tree1->isNodeUncertain(root1) || tree2->isUncertain()) && !crequest->enable_cost) return false;
    else
    {
      OBB obb1, obb2;
      convertBV(bv1, tf1, obb1);
      convertBV(tree2->getBV(root2).bv, tf2, obb2);
      if(!obb1.overlap(obb2)) return false;
    }

    if(tree2->getBV(root2).isLeaf() || (root1->hasChildren() && (bv1.size() > tree2->getBV(root2).bv.size())))
    {
      for(unsigned int i = 0; i < 8; ++i)
      {
        if(root1->childExists(i))
        {
          const OcTree::OcTreeNode* child = root1->getChild(i);
          AABB child_bv;
          computeChildBV(bv1, i, child_bv);

          if(OcTreeMeshIntersectRecurse(tree1, child, child_bv, tree2, root2, tf1, tf2))
            return true;
        }
	else if(!tree2->isFree() && crequest->enable_cost)
        {
          AABB child_bv;
          computeChildBV(bv1, i, child_bv);

          if(OcTreeMeshIntersectRecurse(tree1, NULL, child_bv, tree2, root2, tf1, tf2))
            return true;
        }
      }
    }
    else
    {
      if(OcTreeMeshIntersectRecurse(tree1, root1, bv1, tree2, tree2->getBV(root2).leftChild(), tf1, tf2))
        return true;

      if(OcTreeMeshIntersectRecurse(tree1, root1, bv1, tree2, tree2->getBV(root2).rightChild(), tf1, tf2))
        return true;

    }

    return false;
  }

  bool OcTreeDistanceRecurse(const OcTree* tree1, const OcTree::OcTreeNode* root1, const AABB& bv1,
                             const OcTree* tree2, const OcTree::OcTreeNode* root2, const AABB& bv2,
                             const Transform3f& tf1, const Transform3f& tf2) const
  {
    if(!root1->hasChildren() && !root2->hasChildren())
    {
      if(tree1->isNodeOccupied(root1) && tree2->isNodeOccupied(root2))
      {
        Box box1, box2;
        Transform3f box1_tf, box2_tf;
        constructBox(bv1, tf1, box1, box1_tf);
        constructBox(bv2, tf2, box2, box2_tf);

        FCL_REAL dist;
        Vec3f closest_p1, closest_p2;
        solver->shapeDistance(box1, box1_tf, box2, box2_tf, &dist, &closest_p1, &closest_p2);

        dresult->update(dist, tree1, tree2, root1 - tree1->getRoot(), root2 - tree2->getRoot(), closest_p1, closest_p2);

        return drequest->isSatisfied(*dresult);
      }
      else
        return false;
    }

    if(!tree1->isNodeOccupied(root1) || !tree2->isNodeOccupied(root2)) return false;

    if(!root2->hasChildren() || (root1->hasChildren() && (bv1.size() > bv2.size())))
    {
      for(unsigned int i = 0; i < 8; ++i)
      {
        if(root1->childExists(i))
        {
          const OcTree::OcTreeNode* child = root1->getChild(i);
          AABB child_bv;
          computeChildBV(bv1, i, child_bv);

          FCL_REAL d;
          AABB aabb1, aabb2;
          convertBV(bv1, tf1, aabb1);
          convertBV(bv2, tf2, aabb2);
          d = aabb1.distance(aabb2);

          if(d < dresult->min_distance)
          {

            if(OcTreeDistanceRecurse(tree1, child, child_bv, tree2, root2, bv2, tf1, tf2))
              return true;
          }
        }
      }
    }
    else
    {
      for(unsigned int i = 0; i < 8; ++i)
      {
        if(root2->childExists(i))
        {
          const OcTree::OcTreeNode* child = root2->getChild(i);
          AABB child_bv;
          computeChildBV(bv2, i, child_bv);

          FCL_REAL d;
          AABB aabb1, aabb2;
          convertBV(bv1, tf1, aabb1);
          convertBV(bv2, tf2, aabb2);
          d = aabb1.distance(aabb2);

          if(d < dresult->min_distance)
          {
            if(OcTreeDistanceRecurse(tree1, root1, bv1, tree2, child, child_bv, tf1, tf2))
              return true;
          }
        }
      }
    }

    return false;
  }

  bool OcTreeIntersectRecurse(const OcTree* tree1, const OcTree::OcTreeNode* root1, const AABB& bv1,
                              const OcTree* tree2, const OcTree::OcTreeNode* root2, const AABB& bv2,
                              const Transform3f& tf1, const Transform3f& tf2) const
  {
    if(!root1 && !root2)
    {
      OBB obb1, obb2;
      convertBV(bv1, tf1, obb1);
      convertBV(bv2, tf2, obb2);

      if(obb1.overlap(obb2))
      {
        Box box1, box2;
        Transform3f box1_tf, box2_tf;
        constructBox(bv1, tf1, box1, box1_tf);
        constructBox(bv2, tf2, box2, box2_tf);

        AABB overlap_part;
        AABB aabb1, aabb2;
        computeBV<AABB, Box>(box1, box1_tf, aabb1);
        computeBV<AABB, Box>(box2, box2_tf, aabb2);
        aabb1.overlap(aabb2, overlap_part);
        cresult->addCostSource(CostSource(overlap_part, tree1->getOccupancyThres() * tree2->getOccupancyThres()), crequest->num_max_cost_sources);
      }

      return false;
    }
    else if(!root1 && root2)
    {
      if(root2->hasChildren())
      {
        for(unsigned int i = 0; i < 8; ++i)
        {
          if(root2->childExists(i))
          {
            const OcTree::OcTreeNode* child = root2->getChild(i);
            AABB child_bv;
            computeChildBV(bv2, i, child_bv);
            if(OcTreeIntersectRecurse(tree1, NULL, bv1, tree2, child, child_bv, tf1, tf2))
              return true;
          }
          else
          {
            AABB child_bv;
            computeChildBV(bv2, i, child_bv);
            if(OcTreeIntersectRecurse(tree1, NULL, bv1, tree2, NULL, child_bv, tf1, tf2))
              return true;
          }
        }
      }
      else
      {
        if(OcTreeIntersectRecurse(tree1, NULL, bv1, tree2, NULL, bv2, tf1, tf2))
          return true;
      }

      return false;
    }
    else if(root1 && !root2)
    {
      if(root1->hasChildren())
      {
        for(unsigned int i = 0; i < 8; ++i)
        {
          if(root1->childExists(i))
          {
            const OcTree::OcTreeNode* child = root1->getChild(i);
            AABB child_bv;
            computeChildBV(bv1, i,  child_bv);
            if(OcTreeIntersectRecurse(tree1, child, child_bv, tree2, NULL, bv2, tf1, tf2))
              return true;
          }
          else
          {
            AABB child_bv;
            computeChildBV(bv1, i, child_bv);
            if(OcTreeIntersectRecurse(tree1, NULL, child_bv, tree2, NULL, bv2, tf1, tf2))
              return true;
          }
        }
      }
      else
      {
        if(OcTreeIntersectRecurse(tree1, NULL, bv1, tree2, NULL, bv2, tf1, tf2))
          return true;
      }

      return false;
    }
    else if(!root1->hasChildren() && !root2->hasChildren())
    {
      if(tree1->isNodeOccupied(root1) && tree2->isNodeOccupied(root2)) // occupied area
      {
        bool is_intersect = false;
        if(!crequest->enable_contact)
        {
          OBB obb1, obb2;
          convertBV(bv1, tf1, obb1);
          convertBV(bv2, tf2, obb2);

          if(obb1.overlap(obb2))
          {
            is_intersect = true;
            if(cresult->numContacts() < crequest->num_max_contacts)
              cresult->addContact(Contact(tree1, tree2, root1 - tree1->getRoot(), root2 - tree2->getRoot()));
          }
        }
        else
        {
          Box box1, box2;
          Transform3f box1_tf, box2_tf;
          constructBox(bv1, tf1, box1, box1_tf);
          constructBox(bv2, tf2, box2, box2_tf);

          std::vector<ContactPoint> contacts;
          if(solver->shapeIntersect(box1, box1_tf, box2, box2_tf, &contacts))
          {
            is_intersect = true;
            if(crequest->num_max_contacts > cresult->numContacts())
            {
              const size_t free_space = crequest->num_max_contacts - cresult->numContacts();
              size_t num_adding_contacts;

              // If the free space is not enough to add all the new contacts, we add contacts in descent order of penetration depth.
              if (free_space < contacts.size())
              {
                std::partial_sort(contacts.begin(), contacts.begin() + free_space, contacts.end(), std::bind(comparePenDepth, std::placeholders::_2, std::placeholders::_1));
                num_adding_contacts = free_space;
              }
              else
              {
                num_adding_contacts = contacts.size();
              }

              for(size_t i = 0; i < num_adding_contacts; ++i)
                cresult->addContact(Contact(tree1, tree2, root1 - tree1->getRoot(), root2 - tree2->getRoot(), contacts[i].pos, contacts[i].normal, contacts[i].penetration_depth));
            }
          }
        }

        if(is_intersect && crequest->enable_cost)
        {
          Box box1, box2;
          Transform3f box1_tf, box2_tf;
          constructBox(bv1, tf1, box1, box1_tf);
          constructBox(bv2, tf2, box2, box2_tf);

          AABB overlap_part;
          AABB aabb1, aabb2;
          computeBV<AABB, Box>(box1, box1_tf, aabb1);
          computeBV<AABB, Box>(box2, box2_tf, aabb2);
          aabb1.overlap(aabb2, overlap_part);
	  cresult->addCostSource(CostSource(overlap_part, root1->getOccupancy() * root2->getOccupancy()), crequest->num_max_cost_sources);
        }

        return crequest->isSatisfied(*cresult);
      }
      else if(!tree1->isNodeFree(root1) && !tree2->isNodeFree(root2) && crequest->enable_cost) // uncertain area (here means both are uncertain or one uncertain and one occupied)
      {
        OBB obb1, obb2;
        convertBV(bv1, tf1, obb1);
        convertBV(bv2, tf2, obb2);

        if(obb1.overlap(obb2))
        {
          Box box1, box2;
          Transform3f box1_tf, box2_tf;
          constructBox(bv1, tf1, box1, box1_tf);
          constructBox(bv2, tf2, box2, box2_tf);

          AABB overlap_part;
          AABB aabb1, aabb2;
          computeBV<AABB, Box>(box1, box1_tf, aabb1);
          computeBV<AABB, Box>(box2, box2_tf, aabb2);
          aabb1.overlap(aabb2, overlap_part);
	  cresult->addCostSource(CostSource(overlap_part, root1->getOccupancy() * root2->getOccupancy()), crequest->num_max_cost_sources);
        }

        return false;
      }
      else // free area (at least one node is free)
        return false;
    }

    /// stop when 1) bounding boxes of two objects not overlap; OR
    ///           2) at least one of the nodes is free; OR
    ///           2) (two uncertain nodes OR one node occupied and one node uncertain) AND cost not required
    if(tree1->isNodeFree(root1) || tree2->isNodeFree(root2)) return false;
    else if((tree1->isNodeUncertain(root1) || tree2->isNodeUncertain(root2)) && !crequest->enable_cost) return false;
    else
    {
      OBB obb1, obb2;
      convertBV(bv1, tf1, obb1);
      convertBV(bv2, tf2, obb2);
      if(!obb1.overlap(obb2)) return false;
    }

    if(!root2->hasChildren() || (root1->hasChildren() && (bv1.size() > bv2.size())))
    {
      for(unsigned int i = 0; i < 8; ++i)
      {
        if(root1->childExists(i))
        {
          const OcTree::OcTreeNode* child = root1->getChild(i);
          AABB child_bv;
          computeChildBV(bv1, i, child_bv);

          if(OcTreeIntersectRecurse(tree1, child, child_bv,
                                    tree2, root2, bv2,
                                    tf1, tf2))
            return true;
        }
        else if(!tree2->isNodeFree(root2) && crequest->enable_cost)
        {
          AABB child_bv;
          computeChildBV(bv1, i, child_bv);

          if(OcTreeIntersectRecurse(tree1, NULL, child_bv,
                                    tree2, root2, bv2,
                                    tf1, tf2))
            return true;
        }
      }
    }
    else
    {
      for(unsigned int i = 0; i < 8; ++i)
      {
        if(root2->childExists(i))
        {
          const OcTree::OcTreeNode* child = root2->getChild(i);
          AABB child_bv;
          computeChildBV(bv2, i, child_bv);

          if(OcTreeIntersectRecurse(tree1, root1, bv1,
                                    tree2, child, child_bv,
                                    tf1, tf2))
            return true;
        }
        else if(!tree1->isNodeFree(root1) && crequest->enable_cost)
        {
          AABB child_bv;
          computeChildBV(bv2, i, child_bv);

          if(OcTreeIntersectRecurse(tree1, root1, bv1,
                                    tree2, NULL, child_bv,
                                    tf1, tf2))
            return true;
        }
      }
    }

    return false;
  }
};

/// @brief Traversal node for octree collision
template<typename NarrowPhaseSolver>
class OcTreeCollisionTraversalNode : public CollisionTraversalNodeBase
{
public:
  OcTreeCollisionTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  bool BVTesting(int, int) const
  {
    return false;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeIntersect(model1, model2, tf1, tf2, request, *result);
  }

  const OcTree* model1;
  const OcTree* model2;

  Transform3f tf1, tf2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;
};

/// @brief Traversal node for octree distance
template<typename NarrowPhaseSolver>
class OcTreeDistanceTraversalNode : public DistanceTraversalNodeBase
{
public:
  OcTreeDistanceTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  FCL_REAL BVTesting(int, int) const
  {
    return -1;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeDistance(model1, model2, tf1, tf2, request, *result);
  }

  const OcTree* model1;
  const OcTree* model2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;
};

/// @brief Traversal node for shape-octree collision
template<typename S, typename NarrowPhaseSolver>
class ShapeOcTreeCollisionTraversalNode : public CollisionTraversalNodeBase
{
public:
  ShapeOcTreeCollisionTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  bool BVTesting(int, int) const
  {
    return false;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeShapeIntersect(model2, *model1, tf2, tf1, request, *result);
  }

  const S* model1;
  const OcTree* model2;

  Transform3f tf1, tf2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;
};

/// @brief Traversal node for octree-shape collision
template<typename S, typename NarrowPhaseSolver>
class OcTreeShapeCollisionTraversalNode : public CollisionTraversalNodeBase
{
public:
  OcTreeShapeCollisionTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  bool BVTesting(int, int) const
  {
    return false;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeShapeIntersect(model1, *model2, tf1, tf2, request, *result);
  }

  const OcTree* model1;
  const S* model2;

  Transform3f tf1, tf2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;
};

/// @brief Traversal node for shape-octree distance
template<typename S, typename NarrowPhaseSolver>
class ShapeOcTreeDistanceTraversalNode : public DistanceTraversalNodeBase
{
public:
  ShapeOcTreeDistanceTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  FCL_REAL BVTesting(int, int) const
  {
    return -1;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeShapeDistance(model2, *model1, tf2, tf1, request, *result);
  }

  const S* model1;
  const OcTree* model2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;
};

/// @brief Traversal node for octree-shape distance
template<typename S, typename NarrowPhaseSolver>
class OcTreeShapeDistanceTraversalNode : public DistanceTraversalNodeBase
{
public:
  OcTreeShapeDistanceTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  FCL_REAL BVTesting(int, int) const
  {
    return -1;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeShapeDistance(model1, *model2, tf1, tf2, request, *result);
  }

  const OcTree* model1;
  const S* model2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;
};

/// @brief Traversal node for mesh-octree collision
template<typename BV, typename NarrowPhaseSolver>
class MeshOcTreeCollisionTraversalNode : public CollisionTraversalNodeBase
{
public:
  MeshOcTreeCollisionTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  bool BVTesting(int, int) const
  {
    return false;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeMeshIntersect(model2, model1, tf2, tf1, request, *result);
  }

  const BVHModel<BV>* model1;
  const OcTree* model2;

  Transform3f tf1, tf2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;
};

/// @brief Traversal node for octree-mesh collision
template<typename BV, typename NarrowPhaseSolver>
class OcTreeMeshCollisionTraversalNode : public CollisionTraversalNodeBase
{
public:
  OcTreeMeshCollisionTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  bool BVTesting(int, int) const
  {
    return false;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeMeshIntersect(model1, model2, tf1, tf2, request, *result);
  }

  const OcTree* model1;
  const BVHModel<BV>* model2;

  Transform3f tf1, tf2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;
};

/// @brief Traversal node for mesh-octree distance
template<typename BV, typename NarrowPhaseSolver>
class MeshOcTreeDistanceTraversalNode : public DistanceTraversalNodeBase
{
public:
  MeshOcTreeDistanceTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  FCL_REAL BVTesting(int, int) const
  {
    return -1;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeMeshDistance(model2, model1, tf2, tf1, request, *result);
  }

  const BVHModel<BV>* model1;
  const OcTree* model2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;

};

/// @brief Traversal node for octree-mesh distance
template<typename BV, typename NarrowPhaseSolver>
class OcTreeMeshDistanceTraversalNode : public DistanceTraversalNodeBase
{
public:
  OcTreeMeshDistanceTraversalNode()
  {
    model1 = NULL;
    model2 = NULL;

    otsolver = NULL;
  }

  FCL_REAL BVTesting(int, int) const
  {
    return -1;
  }

  void leafTesting(int, int) const
  {
    otsolver->OcTreeMeshDistance(model1, model2, tf1, tf2, request, *result);
  }

  const OcTree* model1;
  const BVHModel<BV>* model2;

  const OcTreeSolver<NarrowPhaseSolver>* otsolver;

};

}

#endif


text:
use strict;
use Getopt::Long;
use File::Basename qw(basename dirname);

my $usage= "\n$0
--mat     \t<str>\tnumeric matrix, with header line, the first line without first column head
--outDir  \t<str>\toutput directory
--dist    \t<str>\tdistance method, currently support:
\t\teuclidean, maximum, manhattan, canberra, binary or minkowski.
--cor     \t<str>\tcorrelation, should not defined --dist simultaneously. current support:
\t\tpearson, kendall, or spearman
--TOMdis  \t<str>\ttopological overlap measure distance object saved in .RData file. (dissTOM)
--clst    \t<str>\tcluster method used, currently support
\t\thcl_average\thierarchical clustering. using average between group
\t\thcl_ward.D\thierarchical clustering. using ward.D between group
\t\thcl_ward.D2\thierarchical clustering. using ward.D2 between group
\t\thcl_single\thierarchical clustering. using single between group
\t\thcl_complete\thierarchical clustering. using complete between group
\t\thcl_mcquitty\thierarchical clustering. using mcquitty between group
\t\thcl_median\thierarchical clustering. using median between group
\t\thcl_centroid\thierarchical clustering. using centroid between group
\t\tkmean\tthe start cluster numbers is sqrt(Nfeatures)
--test\t\ttest for the distance and clustering method. and validate the different methods
\t\tthe combination of distance and clustering methods:
\t\tdistance: euclidean (EUC), manhattan (MAN), maximum (SUP), canberra (CAN), binary (BIN), minkowski (MIN)
\t\tcor: pearson (PE), kendall (KE), spearman (SP), Goodman-Kruskal (GK), cosine (COS),
\t\tcluster: kmean (KMN), k-medoid (KM), hcl_average (AL), hcl_complete (CL), hcl_single(SL), hcl_ward.D (WDL),
\t\t\thcl_ward.D2 (WD2L), hcl_mcquitty (MCL), hcl_median (MEL), hcl_centroid (CEL)

NOTE: this clustering pipeline will only work for row clustering

\n";

my($mat,$cor,$dist,$clst,$outDir,$test,$TOMdis);

GetOptions(
		"mat:s"=>\$mat,
		"cor:s"=>\$cor,
		"outDir:s"=>\$outDir,
		"dist:s"=>\$dist,
		"clst:s"=>\$clst,
		"test:s"=>\$test,
		"TOMdis:s"=>\$TOMdis
);

die $usage if !defined $mat;

my $mat_base = basename $mat;

`mkdir -p $outDir` if ! -e "$outDir";

my $rscp;

if(!defined $test){
	my($dist_cls);
	if(defined $dist){
		$dist_cls="dist";
	}else{
		$dist_cls="cor";
	}

	$rscp = qq#
\#read in numeric matrix
dt <- read.table("$mat",stringsAsFactors=FALSE)
\#for distance matrix
if(identical("$dist_cls","dist")){
	dt.dst <- dist(dt,method="$dist") \#for every row
}
if(identical("$dist_cls","cor")){
	dt.dst <- as.dist(1-cor(t(dt),method="$cor"))
}

\#for hierarchical clustering
if(length(grep("hcl","$clst"))==1){
	clsmeth = strsplit("$clst","_")[[1]]
	dt.hcl <- hclust(dt.dst,method=clsmeth[2])
}

\#for kmean clustering
\#if(identical("$clst","kmean")){
	\#ns <- round(sqrt(
\#}

q('no')
#;
	open O,">","$outDir/$mat_base.cluster_based_on_numeric.R";
	print O $rscp;
	close O;

}

if(defined $test){
	my $tom_flag;
	if(defined $TOMdis){
		$tom_flag="y";
	}else{
		$tom_flag="n";
	}
	$rscp=qq#
options(stringsAsFactors=FALSE)
dt <- read.table("$mat",stringsAsFactors=FALSE)
dt <- as.matrix(dt)

\#pca representative to judge the cluster numbers
dt.prcomp <- prcomp(t(dt))
dt.pc.load <- as.data.frame(dt.prcomp\$rotation)

library(GGally)
library(ggplot2)
aa <- ggpairs(dt.pc.load[,1:3],
              lower = "blank")
for(i in 1:3){
	for(j in 1:3){
		if(i>j){
				aa <- putPlot(aa,ggplot(data.frame(x=dt.pc.load[,j],y=dt.pc.load[,i]))+
						geom_point(aes(x=x,y=y)) + geom_density2d(aes(x=x,y=y)),i,j)
		}
	}
}
pdf("$outDir/$mat_base.pca_pairPlot.pdf")
print(aa)
dev.off()

library(rococo)
gk.cor <- function(x){
	gk.dt <- matrix(0,nr=NROW(x),nc=NROW(x))
	dimnames(gk.dt) <- list(rownames(x),rownames(x))
	for(i in 1:NROW(x)){
		for(j in 1:NROW(x)){
			gk.dt[i,j] <- rococo(as.numeric(x[i,]),as.numeric(x[j,]),similarity="classical")
		}
	}
	return(gk.dt)
}

\#abbreviations
\#distance: euclidean (EUC), manhattan (MAN), maximum (SUP), canberra (CAN), binary (BIN), minkowski (MIN)
\#cor: pearson (PE), kendall (KE), spearman (SP), Goodman-Kruskal (GK), cosine (COS),
\#cluster: kmean (KMN), k-medoid (KM), hcl_average (AL), hcl_complete (CL), hcl_single(SL), hcl_ward.D (WDL),
\#hcl_ward.D2 (WD2L), hcl_mcquitty (MCL), hcl_median (MEL), hcl_centroid (CEL)
abr <- c("euclidean","manhattan","maximum","canberra","binary","minkowski","pearson","kendall","spearman","Goodman-Kruskal","cosine")
names(abr) <- c("EUC","MAN","SUP","CAN","BIN","MIN","PE","KE","SP","GK","COS")
abr <- c(abr,"kmean","k-medoid","hcl_average","hcl_complete","hcl_single","hcl_ward.D","hcl_ward.D2","hcl_mcquitty","hcl_median","hcl_centroid")
names(abr) <- c(names(abr)[1:11],"KMN","KM","AL","CL","SL","WDL","WD2L","MCL","MEL","CEL")
abr <- c(abr,"TOM");names(abr)[22] <- c("TOM")

\#distance: euclidean (EUC), manhattan (MAN), maximum (SUP), canberra (CAN), binary (BIN), minkowski (MIN)
\#cor: pearson (PE), kendall (KE), spearman (SP), Goodman-Kruskal (GK), cosine (COS),

library(lsa)
\#calculate distance
dst.lst <- list()
dst.lst[["EUC"]] <- dist(dt,method="euclidean")
dst.lst[["MAN"]] <- dist(dt,method="manhattan")
dst.lst[["SUP"]] <- dist(dt,method="maximum")
dst.lst[["CAN"]] <- dist(dt,method="canberra")
dst.lst[["BIN"]] <- dist(dt,method="binary")
dst.lst[["MIN"]] <- dist(dt,method="minkowski")
dst.lst[["PE"]] <- as.dist(1-cor(t(dt),method="pearson"))
dst.lst[["KE"]] <- as.dist(1-cor(t(dt),method="kendall"))
dst.lst[["SP"]] <- as.dist(1-cor(t(dt),method="spearman"))
\#dst.lst[["GK"]] <- as.dist(1-gk.cor(dt))
dst.lst[["COS"]] <- as.dist(1-cosine(t(dt)))
if(identical("$tom_flag","y")){
	load("$TOMdis")
	dst.lst[["TOM"]] <- as.dist(dissTOM)
}
save(dst.lst,file="$outDir/$mat_base.dst.RData")

\#plot multidimentional scaling
mds.dt <- data.frame()
for(i in names(dst.lst)){
	tmp.dt <- cmdscale(dst.lst[[i]])
	mds.dt <- rbind(mds.dt,tmp.dt)
}
mds.dt <- cbind(mds.dt,rep(abr[names(dst.lst)],each=nrow(dt)))
colnames(mds.dt) <- c("ScalingDimension1","ScalingDimension2","Method")
pdf("$outDir/$mat_base.dst.mds_plot.pdf")
aa <- ggplot(mds.dt) + geom_point(aes(x=ScalingDimension1,y=ScalingDimension2))+
		geom_density2d(aes(x=ScalingDimension1,y=ScalingDimension2))+
		facet_wrap(~Method,scales="free")
print(aa)
dev.off()

\#cluster: kmean (KMN), k-medoid (KM), hcl_average (AL), hcl_complete (CL), hcl_single(SL), hcl_ward.D (WDL),
\#hcl_ward.D2 (WD2L), hcl_mcquitty (MCL), hcl_median (MEL), hcl_centroid (CEL)

\#hierarchical clustering
hcl.lst <- list()
for(i in names(dst.lst)){
	hcl.lst[[i]][["WDL"]] <- hclust(dst.lst[[i]],method="ward.D")
	hcl.lst[[i]][["WD2L"]] <- hclust(dst.lst[[i]],method="ward.D2")
	hcl.lst[[i]][["AL"]] <- hclust(dst.lst[[i]],method="average")
	hcl.lst[[i]][["CL"]] <- hclust(dst.lst[[i]],method="complete")
	hcl.lst[[i]][["SL"]] <- hclust(dst.lst[[i]],method="single")
	hcl.lst[[i]][["MCL"]] <- hclust(dst.lst[[i]],method="mcquitty")
	hcl.lst[[i]][["MEL"]] <- hclust(dst.lst[[i]],method="median")
	hcl.lst[[i]][["CEL"]] <- hclust(dst.lst[[i]],method="centroid")
}

save(hcl.lst,dst.lst,abr,file="$outDir/$mat_base.hcl.RData")

\#cophenetic correlation
cop.mt <- matrix(0,nr=length(hcl.lst[[1]]),nc=length(hcl.lst))
dimnames(cop.mt) <- list(x=abr[names(hcl.lst[[1]])],y=abr[names(hcl.lst)])
for(i in names(dst.lst)){
	for(j in names(hcl.lst[[1]])){
		cop.mt[abr[j],abr[i]] <- cor(dst.lst[[i]],cophenetic(hcl.lst[[i]][[j]]))
	}
}
write.csv(cop.mt,file="$outDir/$mat_base.cophenetic_cor.csv")

\#dynamic cut tree
\#here we using dynamicTreeCut to select the cluster number, and then evaluate various
\#clustering methods
library(dynamicTreeCut)
library(clusterSim)
dynamic.tree <- list()
hcl.lst.al <- list()
dynamic.tree.idx <- matrix(nc=length(hcl.lst),nr=5)
dimnames(dynamic.tree.idx) <- list(c("DB","CH","Gamma","CI","S"),abr[names(hcl.lst)])
for(i in names(hcl.lst)){
	dynamic.tree[[i]] <- cutreeDynamic(dendro=hcl.lst[[i]][["AL"]],minClusterSize=2,distM=as.matrix(dst.lst[[i]]),
			                       cutHeight=as.numeric(quantile(hcl.lst[[1]][["AL"]]\$height,0.90)))
	names(dynamic.tree[[i]]) <- hcl.lst[[i]][["AL"]]\$labels
	if(length(which(dynamic.tree[[i]]==0)) > length(dynamic.tree[[i]])*0.5){
		dynamic.tree.idx["DB",abr[i]] <- "NA"
		dynamic.tree.idx["CH",abr[i]] <- "NA"
		dynamic.tree.idx["Gamma",abr[i]] <- "NA"
		dynamic.tree.idx["CI",abr[i]] <- "NA"
		dynamic.tree.idx["S",abr[i]] <- "NA"
		dynamic.tree <- dynamic.tree[-pmatch(i,names(dynamic.tree))]
	}else{
		dynamic.tree.idx["DB",abr[i]] <- index.DB(x=dt,cl=dynamic.tree[[i]],centrotypes="centroids")\$DB \#Minimum value of the index
		dynamic.tree.idx["CH",abr[i]] <- index.G1(x=as.data.frame(dt),cl=dynamic.tree[[i]]) \#Maximum value of the index
		dynamic.tree.idx["Gamma",abr[i]] <- index.G2(d=dst.lst[[i]],cl=dynamic.tree[[i]]) \#Maximum value of the index
		dynamic.tree.idx["CI",abr[i]] <- index.G3(d=dst.lst[[i]],cl=dynamic.tree[[i]]) \#Minimum value of the index
		dynamic.tree.idx["S",abr[i]] <- index.S(d=dst.lst[[i]],cl=dynamic.tree[[i]]) \#Maximum value of the index
		hcl.lst.al[[i]] <- hcl.lst[[i]][["AL"]]
	}
}
save(dynamic.tree,hcl.lst.al,abr,dst.lst,file="$outDir/$mat_base.tree.RData")

\#select optimized methods
dynamic.tree.idx.slc <- vector("character",length=dim(dynamic.tree.idx)[1])
names(dynamic.tree.idx.slc) <- rownames(dynamic.tree.idx)
dynamic.tree.idx.slc["DB"] <- colnames(dynamic.tree.idx)[which.min(dynamic.tree.idx["DB",])]
dynamic.tree.idx.slc["CH"] <- colnames(dynamic.tree.idx)[which.max(dynamic.tree.idx["CH",])]
dynamic.tree.idx.slc["Gamma"] <- colnames(dynamic.tree.idx)[which.max(dynamic.tree.idx["Gamma",])]
dynamic.tree.idx.slc["CI"] <- colnames(dynamic.tree.idx)[which.min(dynamic.tree.idx["CI",])]
dynamic.tree.idx.slc["S"] <- colnames(dynamic.tree.idx)[which.max(dynamic.tree.idx["S",])]

write.csv(data.frame(dynamic.tree.idx,Optimize=dynamic.tree.idx.slc),file="$outDir/$mat_base.tree_idx.csv")

\#cluster: kmean (KMN), k-medoid (KM), K-centroids (KC)
\#select cluster numbers using gap statistics
\#library(cluster)
\#kn <- NROW(dt)/2
\#gap.kn <- clusGap(dt,FUN=pam,K.max=kn,B=100)
\#pdf("$outDir/$mat_base.gap_stat.pdf")
\#plot(gap.kn)
\#dev.off()
\#sink("$outDir/$mat_base.gap_stat.txt")
\#print(gap.kn,method="globalmax")
\#print(gap.kn,method="firstmax")
\#print(gap.kn,method="Tibs2001SEmax")
\#print(gap.kn,method="firstSEmax")
\#print(gap.kn,method="globalSEmax")
\#sink()
\#
\#\#after determining the cluster number, then fuzzy clustering can be used
\#\#fanny {cluster}
\#
\#for(i in names(dst.lst)){
\#	clst.lst[[i]][["KMN"]] <- kmean(dt,centers=kn,nstart=25)
\#	clst.lst[[i]][["KM"]] <- pam(dt,k=kn) \#from cluster package
\#	clst.lst[[i]][["KC"]] <- kcca \#from flexclust package
\#}

\#clusterRepro
\#pvclust

\#do model based clustering
\#et.mc <- Mclust(et.sig.log,G=1:20)
\#plot(et.mc,what = "BIC")

q('no')
#;

	open O,">","$outDir/$mat_base.test.R";
	print O $rscp;
	close O;

	`R CMD BATCH $outDir/$mat_base.test.R $outDir/$mat_base.test.Rout`;

}

redpajama_set_name: RedPajamaGithub


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<title>BounceNotification (Google App Engine API for Java)</title>
<meta name="date" content="2016-07-15">
<link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">

JavaScript is disabled on your browser.

Skip navigation links

Overview

Package

Class

Tree

Deprecated

Index

Help

Prev Class

Next Class

Frames

No Frames

All Classes

Summary:

Nested |

Field |

Constr |

Method

Detail:

Field |

Constr |

Method

com.google.appengine.api.mail

Class BounceNotification

java.lang.Object

com.google.appengine.api.mail.BounceNotification

public final class BounceNotification
extends java.lang.Object

The BounceNotification object represents an incoming bounce
 notification.

Nested Class Summary

Nested Classes

Modifier and Type
Class and Description

static class
BounceNotification.Details

The BounceNotification.Details class describes either the original
 message that caused a bounce, or the notification message describing the
 bounce.

Method Summary

All Methods Instance Methods Concrete Methods

Modifier and Type
Method and Description

BounceNotification.Details
getNotification()

BounceNotification.Details
getOriginal()

MimeMessage
getRawMessage()

Methods inherited from class java.lang.Object

equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Method Detail

getRawMessage

public final MimeMessage getRawMessage()

Returns:

the original MIME message that caused the bounce.

getOriginal

public final BounceNotification.Details getOriginal()

Returns:

the parsed Details of the original message.

getNotification

public final BounceNotification.Details getNotification()

Returns:

the parsed Details describing the bounce.

Skip navigation links

Overview

Package

Class

Tree

Deprecated

Index

Help

Java is a registered trademark of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.

Prev Class

Next Class

Frames

No Frames

All Classes

Summary:

Nested |

Field |

Constr |

Method

Detail:

Field |

Constr |

Method


text:
/**
 * Autogenerated by renren-api2-generator 2013-07-05 11:01:59
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
package com.renren.api.service;
import java.util.*;
/**
 *
 * 照片
 */
public class Photo {

	/**
	 * 照片的ID
	 */
	private long id;
	/**
	 * 照片所属相册的ID
	 */
	private long albumId;
	/**
	 * 照片所有者的ID
	 */
	private long ownerId;
	/**
	 * 照片的描述
	 */
	private String description;
	/**
	 * 照片对应的各种类型图片
	 */
	private List<Image> images;
	/**
	 * 照片的创建时间
	 */
	private String createTime;
	/**
	 * 照片被浏览的次数
	 */
	private int viewCount;
	/**
	 * 照片被回复的次数
	 */
	private int commentCount;
	public void setId(long id) {
		this.id = id;
	}
	public long getId() {
		return this.id;
	}
	public void setAlbumId(long albumId) {
		this.albumId = albumId;
	}
	public long getAlbumId() {
		return this.albumId;
	}
	public void setOwnerId(long ownerId) {
		this.ownerId = ownerId;
	}
	public long getOwnerId() {
		return this.ownerId;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	public String getDescription() {
		return this.description;
	}
	public void setImages(List<Image> images) {
		this.images = images;
	}
	public List<Image> getImages() {
		return this.images;
	}
	public void setCreateTime(String createTime) {
		this.createTime = createTime;
	}
	public String getCreateTime() {
		return this.createTime;
	}
	public void setViewCount(int viewCount) {
		this.viewCount = viewCount;
	}
	public int getViewCount() {
		return this.viewCount;
	}
	public void setCommentCount(int commentCount) {
		this.commentCount = commentCount;
	}
	public int getCommentCount() {
		return this.commentCount;
	}
}


text:
1. https://github.com/mindpin/chatflow/issues/14
2. https://github.com/mindpin/chatflow/issues/15
3. https://github.com/mindpin/chatflow/issues/16

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'im-chat-parser'
```

## Usage

```ruby

require 'im-chat-parser'

# single: 表示 QQ 单人对话
# multiple: 表示 QQ 多人群对话
chat = ImChatParser.load('文本文件绝对路径')

chat.lines.length

# Get all chat lines
lines = chat.lines

# Get each chat line user
user = chat.lines[0]
user.names
user.qq_num

# Get all chat users
users = chat.users
users[0].qq_num
users[0].names

```

## Contributing

1. Fork it ( https://github.com/[my-github-username]/im-chat-parser/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request


text:
/*
 * Qa full api
 *
 * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
 *
 * OpenAPI spec version: all
 *
 * Generated by: https://github.com/swagger-api/swagger-codegen.git
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using NUnit.Framework;

using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using HostMe.Sdk.Api;
using HostMe.Sdk.Model;
using HostMe.Sdk.Client;
using System.Reflection;

namespace HostMe.Sdk.Test
{
    /// <summary>
    ///  Class for testing AdditionalInfo
    /// </summary>
    /// <remarks>
    /// This file is automatically generated by Swagger Codegen.
    /// Please update the test case below to test the model.
    /// </remarks>
    [TestFixture]
    public class AdditionalInfoTests
    {
        // TODO uncomment below to declare an instance variable for AdditionalInfo
        //private AdditionalInfo instance;

        /// <summary>
        /// Setup before each test
        /// </summary>
        [SetUp]
        public void Init()
        {
            // TODO uncomment below to create an instance of AdditionalInfo
            //instance = new AdditionalInfo();
        }

        /// <summary>
        /// Clean up after each test
        /// </summary>
        [TearDown]
        public void Cleanup()
        {

        }

        /// <summary>
        /// Test an instance of AdditionalInfo
        /// </summary>
        [Test]
        public void AdditionalInfoInstanceTest()
        {
            // TODO uncomment below to test "IsInstanceOfType" AdditionalInfo
            //Assert.IsInstanceOfType<AdditionalInfo> (instance, "variable 'instance' is a AdditionalInfo");
        }

        /// <summary>
        /// Test the property 'Allergy'
        /// </summary>
        [Test]
        public void AllergyTest()
        {
            // TODO unit test for the property 'Allergy'
        }
        /// <summary>
        /// Test the property 'Vegetarian'
        /// </summary>
        [Test]
        public void VegetarianTest()
        {
            // TODO unit test for the property 'Vegetarian'
        }
        /// <summary>
        /// Test the property '_Event'
        /// </summary>
        [Test]
        public void _EventTest()
        {
            // TODO unit test for the property '_Event'
        }
        /// <summary>
        /// Test the property 'BabyChair'
        /// </summary>
        [Test]
        public void BabyChairTest()
        {
            // TODO unit test for the property 'BabyChair'
        }
        /// <summary>
        /// Test the property 'Stroller'
        /// </summary>
        [Test]
        public void StrollerTest()
        {
            // TODO unit test for the property 'Stroller'
        }
        /// <summary>
        /// Test the property 'Vip'
        /// </summary>
        [Test]
        public void VipTest()
        {
            // TODO unit test for the property 'Vip'
        }
        /// <summary>
        /// Test the property 'Booth'
        /// </summary>
        [Test]
        public void BoothTest()
        {
            // TODO unit test for the property 'Booth'
        }
        /// <summary>
        /// Test the property 'HighTop'
        /// </summary>
        [Test]
        public void HighTopTest()
        {
            // TODO unit test for the property 'HighTop'
        }
        /// <summary>
        /// Test the property 'Table'
        /// </summary>
        [Test]
        public void TableTest()
        {
            // TODO unit test for the property 'Table'
        }
        /// <summary>
        /// Test the property 'HandicapAccessible'
        /// </summary>
        [Test]
        public void HandicapAccessibleTest()
        {
            // TODO unit test for the property 'HandicapAccessible'
        }

    }

}


text:
using System;
using System.Collections.Generic;
using System.Linq;

namespace IronText.Reflection.Managed
{
    internal class CilSymbolRefResolver : ICilSymbolResolver
    {
        private readonly Dictionary<object, CilSymbol> ref2def = new Dictionary<object, CilSymbol>();

        public CilSymbolRefResolver()
        {
        }

        public IEnumerable<CilSymbol> Definitions
        {
            get { return ref2def.Values.Distinct(); }
        }

        public CilSymbol Resolve(CilSymbolRef symbolRef)
        {
            if (symbolRef == null)
            {
                return null;
            }

            var literalDef   = ResolveLiteral(symbolRef.Literal);
            var tokenTypeDef = ResolveTokenType(symbolRef.Type);

            if (literalDef != null && tokenTypeDef != null && literalDef != tokenTypeDef)
            {
                throw new InvalidOperationException("Unable to resolve conflicting token reference: " + symbolRef);
            }

            return literalDef ?? tokenTypeDef;
        }

        private CilSymbol ResolveLiteral(string literal)
        {
            if (literal == null)
            {
                return null;
            }

            CilSymbol result;
            ref2def.TryGetValue(literal, out result);
            return result;
        }

        private CilSymbol ResolveTokenType(Type type)
        {
            if (type == null)
            {
                return null;
            }

            CilSymbol result;
            ref2def.TryGetValue(type, out result);
            return result;
        }

        public bool Contains(CilSymbolRef symbolRef)
        {
            return symbolRef != null
                && (ResolveLiteral(symbolRef.Literal) != null
                    || ResolveTokenType(symbolRef.Type) != null);
        }

        public Symbol GetSymbol(CilSymbolRef tid)
        {
            CilSymbol def = Resolve(tid);
            return def == null ? null : def.Symbol;
        }

        private CilSymbol Ensure(CilSymbolRef symbolRef)
        {
            CilSymbol literalDef = ResolveLiteral(symbolRef.Literal);
            CilSymbol typeDef    = ResolveTokenType(symbolRef.Type);
            CilSymbol def        = MergeDefs(literalDef, typeDef);

            if (def == null)
            {
                 def = new CilSymbol();
            }
            else if (symbolRef.Type != null && def.Type != null && def.Type != symbolRef.Type)
            {
                throw new InvalidOperationException("Incompatible symbol constraints.");
            }

            // Add token to a defintion
            if (symbolRef.Type != null)
            {
                def.Type = symbolRef.Type;
            }

            if (symbolRef.HasLiteral)
            {
                def.Literals.Add(symbolRef.Literal);
            }

            // Update index
            foreach (var literal in def.Literals)
            {
                ref2def[literal] = def;
            }

            if (def.Type != null)
            {
                ref2def[def.Type] = def;
            }

            return def;
        }

        public void Link(CilSymbolRef first)
        {
            Ensure(first);
        }

        private CilSymbol MergeDefs(CilSymbol xDef, CilSymbol yDef)
        {
            if (xDef == null)
            {
                return yDef;
            }

            if (yDef == null)
            {
                return xDef;
            }

            if (xDef == yDef)
            {
                return xDef;
            }

            if (xDef.Type != null
                && yDef.Type != null
                && xDef.Type != yDef.Type)
            {
                var msg = string.Format(
                    "Internal error: attemt to identify single token by two types: '{0}' and '{1}'",
                    xDef.Type,
                    yDef.Type);
                throw new InvalidOperationException(msg);
            }

            if (xDef.Type == null)
            {
                xDef.Type = yDef.Type;
            }

            xDef.Literals.UnionWith(yDef.Literals);

            return xDef;
        }
    }
}


text:
<?php

namespace Msi\Bundle\CmfBundle\EntityManager;

use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Doctrine\ORM\QueryBuilder;

class Manager
{
    protected $em;
    protected $repository;
    protected $class;
    protected $appLocales;

    public function __construct($class)
    {
        $this->class = $class;
    }

    public function save($entity)
    {
        $this->em->persist($entity);
        $this->em->flush();
    }

    public function saveBatch($entity, $i)
    {
        $this->em->persist($entity);
        $batchSize = 20;
        if ($i % $batchSize === 0) {
            $this->em->flush();
            $this->em->clear();
        }
    }

    public function delete($entity)
    {
        $this->em->remove($entity);
        $this->em->flush();
    }

    public function change($entity, $request)
    {
        $field = $request->query->get('field');
        $locale = $request->query->get('locale');

        $getter = 'get'.ucfirst($field);
        $setter = 'set'.ucfirst($field);

        if ($locale) {
            $entity->getTranslation($locale)->$getter()
                ? $entity->getTranslation($locale)->$setter(false)
                : $entity->getTranslation($locale)->$setter(true);
        } else {
            $entity->$getter() ? $entity->$setter(false) : $entity->$setter(true);
        }

        $this->save($entity);
    }

    public function savePosition($objects, $disposition)
    {
        $i = 1;
        $l = 0;
        foreach ($objects as $object) {
            if (in_array($object->getId(), $disposition)) {
                $object->setPosition($i + array_search($object->getId(), $disposition) - $l);
                $l++;
            } else {
                $object->setPosition($i);
            }
            $i++;
            $this->save($object);
        }
    }

    public function moveUp($entity)
    {
        $this->repository->moveUp($entity, 1);
        $this->save($entity);
    }

    public function moveDown($entity)
    {
        $this->repository->moveDown($entity, 1);
        $this->save($entity);
    }

    public function create()
    {
        return new $this->class();
    }

    public function getClass()
    {
        return $this->class;
    }

    public function isTranslatable()
    {
        return is_subclass_of($this->class, 'Msi\Bundle\CmfBundle\Entity\Translatable');
    }

    public function getAppLocales()
    {
        return $this->appLocales;
    }

    public function setAppLocales($appLocales)
    {
        $this->appLocales = $appLocales;

        return $this;
    }

    public function getRepository()
    {
        return $this->repository;
    }

    public function setEntityManager(EntityManager $em)
    {
        $this->em = $em;
        $this->repository = $em->getRepository($this->class);

        return $this;
    }

    public function findAll()
    {
        return $this->repository->findBy(array());
    }

    public function findOneOrCreate($id = null)
    {
        if ($id) {
            $object = $this->getFindByQueryBuilder(array('a.id' => $id))->getQuery()->getOneOrNullResult();
            if (!$object) {
                throw new NotFoundHttpException();
            }
        } else {
            $object = $this->create();
        }

        if ($this->isTranslatable()) {
            $object->createTranslations($this->class.'Translation', $this->appLocales);
        }

        return $object;
    }

    public function getFindByQueryBuilder(array $where = array(), array $join = array(), array $orderBy = array(), $limit = null, $offset = null)
    {
        $qb = $this->repository->createQueryBuilder('a');

        $qb = $this->buildFindBy($qb, $where, $join, $orderBy);

        if (null !== $limit) {
            $qb->setMaxResults($limit);
        }

        if (null !== $offset) {
            $qb->setFirstResult($offset);
        }

        return $qb;
    }

    public function getSearchQueryBuilder($q, array $searchFields, array $where = array(), array $join = array(), array $orderBy = array())
    {
        $qb = $this->repository->createQueryBuilder('a');

        if (count($searchFields)) {
            $q = trim($q);
            $strings = explode(' ', $q);

            $orX = $qb->expr()->orX();
            $i = 1;
            foreach ($searchFields as $field) {
                foreach ($strings as $string) {
                    $token = 'likeMatch'.$i;
                    $orX->add($qb->expr()->like($field, ':'.$token));
                    $qb->setParameter($token, '%'.$string.'%');

                    $orX->add($qb->expr()->like('a.id', ':eqMatchForId'.$i));
                    $qb->setParameter('eqMatchForId'.$i, $string);
                    $i++;
                }
            }

            $qb->andWhere($orX);
        }

        $qb = $this->buildFindBy($qb, $where, $join, $orderBy);

        return $qb;
    }

    protected function buildFindBy(QueryBuilder $qb, array $where, array $join, array $orderBy)
    {
        $i = 1;
        foreach ($where as $k => $v) {
            $token = 'eqMatch'.$i;
            $qb->andWhere($qb->expr()->eq($k, ':'.$token))->setParameter($token, $v);
            $i++;
        }

        foreach ($join as $k => $v) {
            $qb->leftJoin($k, $v);
            $qb->addSelect($v);
        }

        foreach ($orderBy as $k => $v) {
            $qb->addOrderBy($k, $v);
        }

        return $qb;
    }
}


text:
/**
 * @author benvanik@google.com (Ben Vanik)
 */

goog.provide('blk.ui.screens.SplashScreen');

goog.require('blk.ui.PopupScreen');
goog.require('blk.ui.screens.splashscreen');

/**
 * Simple splash screen.
 * This screen could be nice for showing a fancy logo/credits/etc.
 * Ideally, it would hide the latency of loading all of the extra resources.
 * @constructor
 * @extends {blk.ui.PopupScreen}
 * @param {!goog.dom.DomHelper} dom DOM helper used to create DOM nodes.
 * @param {!Element} parentElement Parent DOM element to render into.
 */
blk.ui.screens.SplashScreen = function(dom, parentElement) {
  goog.base(this, '', dom, parentElement,
      blk.ui.screens.splashscreen.content, {
      });
};
goog.inherits(blk.ui.screens.SplashScreen, blk.ui.PopupScreen);


text:
package com.backendless.examples.endless.tagging;

import android.app.*;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.*;
import android.widget.*;
import com.backendless.Backendless;
import com.backendless.BackendlessCollection;
import com.backendless.async.callback.AsyncCallback;
import com.backendless.exceptions.BackendlessFault;
import com.backendless.geo.BackendlessGeoQuery;
import com.backendless.geo.GeoCategory;
import com.backendless.geo.GeoPoint;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.*;

import java.io.InputStream;
import java.util.*;

public class EndlessTaggingActivity extends Activity
{
  private GoogleMap googleMap;
  private LatLng myPosition;
  private List<String> selectedCategories = new ArrayList<String>();
  private List<String> categoriesNames = new ArrayList<String>();
  private List<String> pointCategories = new ArrayList<String>();
  private String categoryInLine = null;
  private String category = null;
  private String pointNameNew = null;
  private String photoUrl = null;
  private String filePath = null;
  private double latitude, longitude, NELat, NELon, SWLat, SWLon, latitudeOnMap, longitudeOnMap;
  private PointsAdapter adapter;
  private BackendlessGeoQuery backendlessGeoQuery;
  private ProgressDialog progressDialog;
  private Map<Marker, Bitmap> bitmapToMarkerMap = new HashMap<Marker, Bitmap>();
  private boolean onMapClick = false;

  @Override
  public void onCreate( Bundle savedInstanceState )
  {
    super.onCreate( savedInstanceState );
    setContentView( R.layout.act_endless_tagging );

    TextView textEndless = (TextView) findViewById( R.id.textEndless );
    Typeface typeface = Typeface.createFromAsset( getAssets(), "fonts/verdana.ttf" );
    textEndless.setTypeface( typeface );

    adapter = new PointsAdapter( EndlessTaggingActivity.this );
    MapFragment mapFragment = (MapFragment) getFragmentManager().findFragmentById( R.id.map );
    googleMap = mapFragment.getMap();
    googleMap.setMyLocationEnabled( true );

    LocationManager locationManager = (LocationManager) getSystemService( LOCATION_SERVICE );
    Criteria criteria = new Criteria();
    criteria.setAccuracy( Criteria.ACCURACY_FINE );
    criteria.setPowerRequirement( Criteria.POWER_LOW );
    criteria.setAltitudeRequired( false );
    criteria.setBearingRequired( false );
    criteria.setSpeedRequired( false );
    criteria.setCostAllowed( true );
    boolean enabledGPS = locationManager.isProviderEnabled( LocationManager.GPS_PROVIDER );
    if( !enabledGPS )
    {
      Toast.makeText( EndlessTaggingActivity.this, "No GPS signal", Toast.LENGTH_LONG ).show();
    }
    boolean networkIsEnabled = locationManager.isProviderEnabled( LocationManager.NETWORK_PROVIDER );
    if( !networkIsEnabled )
    {
      Toast.makeText( EndlessTaggingActivity.this, "No Network signal", Toast.LENGTH_LONG ).show();
    }
    String provider = locationManager.getBestProvider( criteria, true );
    LocationListener myLocationListener = new LocationListener()
    {
      @Override
      public void onLocationChanged( Location location )
      {
        //To change body of implemented methods use File | Settings | File Templates.
      }

      @Override
      public void onStatusChanged( String provider, int status, Bundle extras )
      {
        //To change body of implemented methods use File | Settings | File Templates.
      }

      @Override
      public void onProviderEnabled( String provider )
      {
        //To change body of implemented methods use File | Settings | File Templates.
      }

      @Override
      public void onProviderDisabled( String provider )
      {
        //To change body of implemented methods use File | Settings | File Templates.
      }
    };
    Location location = locationManager.getLastKnownLocation( provider );
    locationManager.requestSingleUpdate( criteria, myLocationListener, null );
    if( location != null )
    {
      latitude = location.getLatitude();
      longitude = location.getLongitude();
      myPosition = new LatLng( latitude, longitude );
      googleMap.addMarker( new MarkerOptions().position( myPosition ).icon( BitmapDescriptorFactory.fromResource( R.drawable.marker_red ) ) );
      googleMap.moveCamera( CameraUpdateFactory.newLatLngZoom( myPosition, 11 ) );
      googleMap.animateCamera( CameraUpdateFactory.zoomTo( 14 ), 2000, null );
      getCategory();
    }
    else
      Toast.makeText( EndlessTaggingActivity.this, "Can't determine your location. Check the settings your device and restart the application.", Toast.LENGTH_LONG ).show();

    googleMap.setOnMapLongClickListener( new GoogleMap.OnMapLongClickListener()
    {
      @Override
      public void onMapLongClick( LatLng latLng )
      {
        latitudeOnMap = latLng.latitude;
        longitudeOnMap = latLng.longitude;
        onMapClick = true;
        AlertDialog.Builder builder = new AlertDialog.Builder( EndlessTaggingActivity.this );
        builder.setMessage( R.string.new_point );
        builder.setPositiveButton( R.string.yes, new DialogInterface.OnClickListener()
        {
          @Override
          public void onClick( DialogInterface dialog, int which )
          {
            Intent intent = new Intent( EndlessTaggingActivity.this, MakeChoiceActivity.class );
            startActivityForResult( intent, Default.ADD_NEW_PHOTO_RESULT );
          }
        } );
        builder.setNegativeButton( R.string.no, new DialogInterface.OnClickListener()
        {
          @Override
          public void onClick( DialogInterface dialog, int which )
          {

          }
        } );
        AlertDialog dialog = builder.create();
        dialog.show();
      }
    } );
    googleMap.setOnMarkerClickListener( new GoogleMap.OnMarkerClickListener()
    {
      @Override
      public boolean onMarkerClick( Marker marker )
      {
        GeoPoint pointEndless = adapter.getPointByMarker( marker );

        if( pointEndless == null || !pointEndless.getMetadata().containsKey( "endlessTagging" ) )
        {
          return true;
        }

        if( !bitmapToMarkerMap.containsKey( marker ) )
        {
          String markerPhotoUrl = pointEndless.getMetadata( "photoUrl" );
          new DownloadImageTask( marker ).execute( markerPhotoUrl );
        }
        else
          marker.showInfoWindow();

        return true;
      }
    } );
    googleMap.setInfoWindowAdapter( new GoogleMap.InfoWindowAdapter()
    {
      @Override
      public View getInfoWindow( Marker marker )
      {
        return null;
      }

      @Override
      public View getInfoContents( Marker marker )
      {
        View view = getLayoutInflater().inflate( R.layout.custom_info_window, null );
        ImageView imageView = (ImageView) view.findViewById( R.id.imagePhoto );

        if( bitmapToMarkerMap.containsKey( marker ) )
          imageView.setImageBitmap( bitmapToMarkerMap.get( marker ) );

        TextView title = (TextView) view.findViewById( R.id.textTitlePoint );
        title.setText( marker.getTitle() );
        TextView sniped = (TextView) view.findViewById( R.id.textSnipedPoint );
        sniped.setText( marker.getSnippet() );

        return view;
      }
    } );

    googleMap.setOnCameraChangeListener( new GoogleMap.OnCameraChangeListener()
    {
      @Override
      public void onCameraChange( CameraPosition cameraPosition )
      {
        LatLngBounds visibleRegion = googleMap.getProjection().getVisibleRegion().latLngBounds;
        LatLng topRight = visibleRegion.northeast;
        LatLng botDown = visibleRegion.southwest;
        SWLat = botDown.latitude;
        SWLon = botDown.longitude;
        NELat = topRight.latitude;
        NELon = topRight.longitude;
        searchRectanglePoints( selectedCategories.isEmpty() ? categoriesNames : selectedCategories );
      }
    } );

    googleMap.setOnInfoWindowClickListener( new GoogleMap.OnInfoWindowClickListener()
    {
      @Override
      public void onInfoWindowClick( Marker marker )
      {
        for( int i = 0; i < adapter.pointsList.size(); i++ )
        {
          GeoPoint pointData = adapter.pointsList.get( i );
          Map<String, String> newMetaData = pointData.getMetadata();
          final String geoPointId = pointData.getObjectId();
          String name = null;
          String pointName = newMetaData.get( "pointName" );
          List<String> categoriesPoint = pointData.getCategories();
          for( String category : categoriesPoint )
          {
            name = category.toString();
          }
          if( pointName.equals( marker.getTitle() ) )
          {
            Intent intent = new Intent( EndlessTaggingActivity.this, PointCommentsActivity.class );
            intent.putExtra( Default.SEARCH_CATEGORY_NAME, name );
            intent.putExtra( Default.GEO_POINT_ID, geoPointId );
            intent.putExtra( Default.SEND_BITMAP, bitmapToMarkerMap.get( marker ) );
            startActivityForResult( intent, Default.ADD_NEW_COMMENT );
          }
        }
      }
    } );

    Button tagBtn = (Button) findViewById( R.id.tagBtn );
    tagBtn.setTypeface( typeface );
    tagBtn.setOnClickListener( new View.OnClickListener()
    {
      @Override
      public void onClick( View v )
      {
        Intent intent = new Intent( EndlessTaggingActivity.this, MakeChoiceActivity.class );
        startActivityForResult( intent, Default.ADD_NEW_PHOTO_RESULT );
      }
    } );

    Button filterBtn = (Button) findViewById( R.id.filterBtn );
    filterBtn.setTypeface( typeface );
    filterBtn.setOnClickListener( new View.OnClickListener()
    {
      @Override
      public void onClick( View v )
      {
        if( selectedCategories.size() == 0 )
        {
          Intent intent = new Intent( EndlessTaggingActivity.this, FilterActivity.class );
          startActivityForResult( intent, Default.CATEGORY_RESULT_SEARCH );
        }
        else
        {
          Intent intent = new Intent( EndlessTaggingActivity.this, FilterActivity.class );
          String[] array = new String[ selectedCategories.size() ];
          array = selectedCategories.toArray( array );
          intent.putExtra( Default.SEARCH_CHECK_BOX, array );
          startActivityForResult( intent, Default.CATEGORY_RESULT_SEARCH );
        }
      }
    } );
  }

  public static Bitmap scaleDownBitmap( Bitmap photo, int newHeight, Context context )
  {

    final float densityMultiplier = context.getResources().getDisplayMetrics().density;

    int h = (int) (newHeight * densityMultiplier);
    int w = (int) (h * photo.getWidth() / ((double) photo.getHeight()));

    photo = Bitmap.createScaledBitmap( photo, w, h, true );

    return photo;
  }

  public class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
  {
    Marker marker;

    public DownloadImageTask( Marker marker )
    {
      this.marker = marker;
    }

    protected Bitmap doInBackground( String... urls )
    {
      String urlDisplay = urls[ 0 ];
      Bitmap bitmap = null;
      try
      {
        InputStream inputStream = new java.net.URL( urlDisplay ).openStream();
        bitmap = BitmapFactory.decodeStream( inputStream );
      }
      catch( Exception e )
      {
        e.printStackTrace();
      }
      return bitmap;
    }

    protected void onPostExecute( Bitmap result )
    {
      if( result != null )
      {
        Bitmap sendBitmap = scaleDownBitmap( result, 200, EndlessTaggingActivity.this );
        bitmapToMarkerMap.put( marker, sendBitmap );
      }
      marker.showInfoWindow();
    }
  }

  @Override
  protected void onActivityResult( int requestCode, int resultCode, Intent data )
  {
    if( data == null )
      return;

    switch( requestCode )
    {
      case Default.CATEGORY_RESULT_SEARCH:
        googleMap.clear();
        googleMap.addMarker( new MarkerOptions().position( myPosition ).icon( BitmapDescriptorFactory.fromResource( R.drawable.marker_red ) ) );
        List<String> searchCategory = Arrays.asList( data.getStringArrayExtra( Default.SEARCH_CATEGORY_NAME ) );
        selectedCategories = new ArrayList<String>();
        selectedCategories = searchCategory;
        searchRectanglePoints( selectedCategories.isEmpty() ? categoriesNames : selectedCategories );
        break;
      case Default.ADD_NEW_CATEGORY_RESULT:
        String categoryTriger = data.getStringExtra( Default.CATEGORY_TRIGER );
        if( categoryTriger.equals( "1" ) )
          category = data.getStringExtra( Default.NEW_CATEGORY_NAME );
        if( categoryTriger.equals( "2" ) )
          pointCategories = Arrays.asList( data.getStringArrayExtra( Default.SEARCH_CATEGORY_NAME ) );
        if( categoryTriger.equals( "3" ) )
        {
          category = data.getStringExtra( Default.NEW_CATEGORY_NAME );
          pointCategories = Arrays.asList( data.getStringArrayExtra( Default.SEARCH_CATEGORY_NAME ) );
        }
        Intent intentComment = new Intent( EndlessTaggingActivity.this, AddCommentActivity.class );
        intentComment.putExtra( Default.FILE_PATH, filePath );
        startActivityForResult( intentComment, Default.ADD_NEW_POINT_RESULT );
        break;
      case Default.ADD_NEW_POINT_RESULT:
        pointNameNew = data.getStringExtra( Default.NEW_POINT_NAME );
        googleMap.clear();

        if( category != null && pointCategories.isEmpty() )
        {
          AlertDialog.Builder builder = new AlertDialog.Builder( EndlessTaggingActivity.this );
          builder.setMessage( R.string.save_data );
          builder.setPositiveButton( R.string.yes, new DialogInterface.OnClickListener()
          {
            @Override
            public void onClick( DialogInterface dialog, int which )
            {
              List<String> categories = new ArrayList<String>();
              categories.add( category );
              Map<String, String> metadata = new HashMap<String, String>();
              metadata.put( "pointName", pointNameNew );
              metadata.put( "pointDescription", category );
              metadata.put( "photoUrl", photoUrl );
              metadata.put( "endlessTagging", "true" );
              if( onMapClick )
              {
                latitude = latitudeOnMap;
                longitude = longitudeOnMap;
              }
              GeoPoint geoPoint = new GeoPoint( latitude, longitude, categories, metadata );
              progressDialog = ProgressDialog.show( EndlessTaggingActivity.this, "", "Loading", true );
              Backendless.Geo.savePoint( geoPoint, new AsyncCallback<GeoPoint>()
              {
                @Override
                public void handleResponse( GeoPoint geoPoint )
                {
                  adapter.add( geoPoint );
                  progressDialog.cancel();
                  selectedCategories = new ArrayList<String>();
                  selectedCategories.add( category );
                  searchRectanglePoints( selectedCategories.isEmpty() ? categoriesNames : selectedCategories );
                }

                @Override
                public void handleFault( BackendlessFault backendlessFault )
                {
                  progressDialog.cancel();
                  Toast.makeText( EndlessTaggingActivity.this, backendlessFault.getMessage(), Toast.LENGTH_LONG ).show();
                }
              } );
            }
          } );
          builder.setNegativeButton( R.string.no, new DialogInterface.OnClickListener()
          {
            @Override
            public void onClick( DialogInterface dialog, int which )
            {
              Intent intent = new Intent( EndlessTaggingActivity.this, AddCommentActivity.class );
              startActivity( intent );
              finish();
            }
          } );
          AlertDialog dialog = builder.create();
          dialog.show();
        }
        else if( category == null && !pointCategories.isEmpty() )
        {
          AlertDialog.Builder builder = new AlertDialog.Builder( EndlessTaggingActivity.this );
          builder.setMessage( R.string.save_data );
          builder.setPositiveButton( R.string.yes, new DialogInterface.OnClickListener()
          {
            @Override
            public void onClick( DialogInterface dialog, int which )
            {
              List<String> categories = new ArrayList<String>();
              categories.addAll( pointCategories );
              StringBuilder result = new StringBuilder();
              for( int i = 0; i < pointCategories.size(); i++ )
              {
                result.append( pointCategories.get( i ) );
                if( i < pointCategories.size() - 1 )
                  result.append( ", " );
              }
              categoryInLine = result.toString();
              Map<String, String> metadata = new HashMap<String, String>();
              metadata.put( "pointName", pointNameNew );
              metadata.put( "pointDescription", categoryInLine );
              metadata.put( "photoUrl", photoUrl );
              metadata.put( "endlessTagging", "true" );
              if( onMapClick )
              {
                latitude = latitudeOnMap;
                longitude = longitudeOnMap;
              }
              GeoPoint geoPoint = new GeoPoint( latitude, longitude, categories, metadata );
              progressDialog = ProgressDialog.show( EndlessTaggingActivity.this, "", "Loading", true );
              Backendless.Geo.savePoint( geoPoint, new AsyncCallback<GeoPoint>()
              {
                @Override
                public void handleResponse( GeoPoint geoPoint )
                {
                  adapter.add( geoPoint );
                  selectedCategories = new ArrayList<String>();
                  selectedCategories.addAll( pointCategories );
                  searchRectanglePoints( selectedCategories.isEmpty() ? categoriesNames : selectedCategories );
                  progressDialog.cancel();
                }

                @Override
                public void handleFault( BackendlessFault backendlessFault )
                {
                  progressDialog.cancel();
                  Toast.makeText( EndlessTaggingActivity.this, backendlessFault.getMessage(), Toast.LENGTH_LONG ).show();
                }
              } );
            }
          } );
          builder.setNegativeButton( R.string.no, new DialogInterface.OnClickListener()
          {
            @Override
            public void onClick( DialogInterface dialog, int which )
            {
              Intent intent = new Intent( EndlessTaggingActivity.this, AddCommentActivity.class );
              startActivity( intent );
              finish();
            }
          } );
          AlertDialog dialog = builder.create();
          dialog.show();
        }
        else if( category != null && !pointCategories.isEmpty() )
        {
          AlertDialog.Builder builder = new AlertDialog.Builder( EndlessTaggingActivity.this );
          builder.setMessage( R.string.save_data );
          builder.setPositiveButton( R.string.yes, new DialogInterface.OnClickListener()
          {
            @Override
            public void onClick( DialogInterface dialog, int which )
            {
              List<String> categories = new ArrayList<String>();
              categories.add( category );
              categories.addAll( pointCategories );
              StringBuilder result = new StringBuilder();
              for( int i = 0; i < categories.size(); i++ )
              {
                result.append( categories.get( i ) );
                if( i < categories.size() - 1 )
                  result.append( ", " );
              }
              categoryInLine = result.toString();
              Map<String, String> metadata = new HashMap<String, String>();
              metadata.put( "pointName", pointNameNew );
              metadata.put( "pointDescription", categoryInLine );
              metadata.put( "photoUrl", photoUrl );
              metadata.put( "endlessTagging", "true" );
              if( onMapClick )
              {
                latitude = latitudeOnMap;
                longitude = longitudeOnMap;
              }
              GeoPoint geoPoint = new GeoPoint( latitude, longitude, categories, metadata );
              progressDialog = ProgressDialog.show( EndlessTaggingActivity.this, "", "Loading", true );
              Backendless.Geo.savePoint( geoPoint, new AsyncCallback<GeoPoint>()
              {
                @Override
                public void handleResponse( GeoPoint geoPoint )
                {
                  adapter.add( geoPoint );
                  progressDialog.cancel();
                  selectedCategories = new ArrayList<String>();
                  selectedCategories.add( category );
                  selectedCategories.addAll( pointCategories );
                  searchRectanglePoints( selectedCategories.isEmpty() ? categoriesNames : selectedCategories );
                }

                @Override
                public void handleFault( BackendlessFault backendlessFault )
                {
                  progressDialog.cancel();
                  Toast.makeText( EndlessTaggingActivity.this, backendlessFault.getMessage(), Toast.LENGTH_LONG ).show();
                }
              } );
            }
          } );
          builder.setNegativeButton( R.string.no, new DialogInterface.OnClickListener()
          {
            @Override
            public void onClick( DialogInterface dialog, int which )
            {
              Intent intent = new Intent( EndlessTaggingActivity.this, AddCommentActivity.class );
              startActivity( intent );
              finish();
            }
          } );
          AlertDialog dialog = builder.create();
          dialog.show();
        }
        break;
      case Default.ADD_NEW_COMMENT:
        googleMap.clear();
        googleMap.addMarker( new MarkerOptions().position( myPosition ).icon( BitmapDescriptorFactory.fromResource( R.drawable.marker_red ) ) );
        String nameCategory = data.getStringExtra( Default.SEARCH_CATEGORY_NAME );
        selectedCategories = new ArrayList<String>();
        selectedCategories.add( nameCategory );
        searchRectanglePoints( selectedCategories.isEmpty() ? categoriesNames : selectedCategories );
        break;
      case Default.ADD_NEW_PHOTO_RESULT:
        photoUrl = data.getStringExtra( Default.PHOTO_CAMERA_URL );
        if( photoUrl == null )
          photoUrl = data.getStringExtra( Default.PHOTO_BROWSE_URL );
        filePath = data.getStringExtra( Default.FILE_PATH );
        Intent intent = new Intent( EndlessTaggingActivity.this, CreateNewCategoryActivity.class );
        startActivityForResult( intent, Default.ADD_NEW_CATEGORY_RESULT );
        break;
      default:
        break;
    }
  }

  private void searchRectanglePoints( List<String> categoriesNames )
  {
    final ProgressBar progressBar = (ProgressBar) findViewById( R.id.progressBar );
    final TextView textView = (TextView) findViewById( R.id.textLoading );
    textView.setVisibility( TextView.VISIBLE );
    progressBar.setVisibility( ProgressBar.VISIBLE );
    backendlessGeoQuery = new BackendlessGeoQuery();
    backendlessGeoQuery.setSearchRectangle( new double[] { NELat, SWLon, SWLat, NELon } );
    backendlessGeoQuery.setCategories( categoriesNames );

    Backendless.Geo.getPoints( backendlessGeoQuery, new AsyncCallback<BackendlessCollection<GeoPoint>>()
    {
      @Override
      public void handleResponse( BackendlessCollection<GeoPoint> geoPointBackendlessCollection )
      {
        List<GeoPoint> points = geoPointBackendlessCollection.getCurrentPage();
        double newLatitude, newLongitude;
        adapter.clear();

        if( !points.isEmpty() )
          adapter.addAll( points );

        adapter.notifyDataSetChanged();

        for( GeoPoint point : points )
        {
          newLatitude = point.getLatitude();
          newLongitude = point.getLongitude();
          Map<String, String> newMetaData = point.getMetadata();
          String endlessTagging = newMetaData.get( "endlessTagging" );
          if( endlessTagging != null )
          {
            String pointName = newMetaData.get( "pointName" );
            String pointDescription = newMetaData.get( "pointDescription" );
            LatLng newPosition = new LatLng( newLatitude, newLongitude );
            googleMap.addMarker( new MarkerOptions().position( newPosition ).title( pointName ).snippet( pointDescription ).icon( BitmapDescriptorFactory.fromResource( R.drawable.marker_blue ) ) );
          }
        }
        textView.setVisibility( TextView.INVISIBLE );
        progressBar.setVisibility( ProgressBar.INVISIBLE );
      }

      @Override
      public void handleFault( BackendlessFault backendlessFault )
      {
        textView.setVisibility( TextView.INVISIBLE );
        progressBar.setVisibility( ProgressBar.INVISIBLE );
        Toast.makeText( EndlessTaggingActivity.this, backendlessFault.getMessage(), Toast.LENGTH_LONG ).show();
      }
    } );
  }

  public void getCategory()
  {
    progressDialog = ProgressDialog.show( EndlessTaggingActivity.this, "", "Loading", true );
    Backendless.Geo.getCategories( new AsyncCallback<List<GeoCategory>>()
    {
      @Override
      public void handleResponse( List<GeoCategory> geoCategories )
      {
        List<GeoCategory> categories = geoCategories;

        for( GeoCategory category : categories )
          categoriesNames.add( category.getName() );
        searchRectanglePoints( selectedCategories.isEmpty() ? categoriesNames : selectedCategories );
        progressDialog.cancel();
      }

      @Override
      public void handleFault( BackendlessFault backendlessFault )
      {
        progressDialog.cancel();
        Toast.makeText( EndlessTaggingActivity.this, backendlessFault.getMessage(), Toast.LENGTH_LONG ).show();
      }
    } );
  }

  private static class PointsAdapter extends BaseAdapter
  {
    private LayoutInflater inflater;
    private List<GeoPoint> pointsList = new ArrayList<GeoPoint>();

    public PointsAdapter( Context context )
    {
      inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
    }

    public int getCount()
    {
      return pointsList.size();
    }

    public GeoPoint getItem( int position )
    {
      return pointsList.get( position );
    }

    public void add( GeoPoint geoPoint )
    {
      if( !pointsList.contains( geoPoint ) )
        pointsList.add( geoPoint );
    }

    public long getItemId( int position )
    {
      return position;
    }

    public View getView( int position, View convertView, ViewGroup parent )
    {
      return convertView;
    }

    public void addAll( Collection<? extends GeoPoint> geoPoints )
    {
      for( GeoPoint geoPoint : geoPoints )
        add( geoPoint );
    }

    public GeoPoint getPointByMarker( Marker marker )
    {
      for( int i = 0; i < pointsList.size(); i++ )
      {
        GeoPoint pointData = getItem( i );
        Map<String, String> newMetaData = pointData.getMetadata();
        String pointName = newMetaData.get( "pointName" );
        String markerName = marker.getTitle();
        if( pointName == null || markerName == null )
          return null;

        if( pointName.equals( markerName ) )
          return pointData;
      }

      return null;
    }

    public void clear()
    {
      pointsList.clear();
    }
  }
}