Mac binaries
[jabaws.git] / website / archive / binaries / mac / src / muscle / clust.cpp
diff --git a/website/archive/binaries/mac/src/muscle/clust.cpp b/website/archive/binaries/mac/src/muscle/clust.cpp
new file mode 100644 (file)
index 0000000..97e9d7c
--- /dev/null
@@ -0,0 +1,666 @@
+#include "muscle.h"\r
+#include "clust.h"\r
+#include "clustset.h"\r
+#include <stdio.h>\r
+\r
+#define TRACE          0\r
+\r
+Clust::Clust()\r
+       {\r
+       m_Nodes = 0;\r
+       m_uNodeCount = 0;\r
+       m_uLeafCount = 0;\r
+       m_uClusterCount = 0;\r
+       m_JoinStyle = JOIN_Undefined;\r
+       m_dDist = 0;\r
+       m_uLeafCount = 0;\r
+       m_ptrSet = 0;\r
+       }\r
+\r
+Clust::~Clust()\r
+       {\r
+       delete[] m_Nodes;\r
+       delete[] m_dDist;\r
+       delete[] m_ClusterIndexToNodeIndex;\r
+       }\r
+\r
+void Clust::Create(ClustSet &Set, CLUSTER Method)\r
+       {\r
+       m_ptrSet = &Set;\r
+\r
+       SetLeafCount(Set.GetLeafCount());\r
+\r
+       switch (Method)\r
+               {\r
+       case CLUSTER_UPGMA:\r
+               m_JoinStyle = JOIN_NearestNeighbor;\r
+               m_CentroidStyle = LINKAGE_Avg;\r
+               break;\r
+\r
+       case CLUSTER_UPGMAMax:\r
+               m_JoinStyle = JOIN_NearestNeighbor;\r
+               m_CentroidStyle = LINKAGE_Max;\r
+               break;\r
+\r
+       case CLUSTER_UPGMAMin:\r
+               m_JoinStyle = JOIN_NearestNeighbor;\r
+               m_CentroidStyle = LINKAGE_Min;\r
+               break;\r
+\r
+       case CLUSTER_UPGMB:\r
+               m_JoinStyle = JOIN_NearestNeighbor;\r
+               m_CentroidStyle = LINKAGE_Biased;\r
+               break;\r
+\r
+       case CLUSTER_NeighborJoining:\r
+               m_JoinStyle = JOIN_NeighborJoining;\r
+               m_CentroidStyle = LINKAGE_NeighborJoining;\r
+               break;\r
+\r
+       default:\r
+               Quit("Clust::Create, invalid method %d", Method);\r
+               }\r
+\r
+       if (m_uLeafCount <= 1)\r
+               Quit("Clust::Create: no leaves");\r
+\r
+       m_uNodeCount = 2*m_uLeafCount - 1;\r
+       m_Nodes = new ClustNode[m_uNodeCount];\r
+       m_ClusterIndexToNodeIndex = new unsigned[m_uLeafCount];\r
+\r
+       m_ptrClusterList = 0;\r
+       for (unsigned uNodeIndex = 0; uNodeIndex < m_uNodeCount; ++uNodeIndex)\r
+               {\r
+               ClustNode &Node = m_Nodes[uNodeIndex];\r
+               Node.m_uIndex = uNodeIndex;\r
+               if (uNodeIndex < m_uLeafCount)\r
+                       {\r
+                       Node.m_uSize = 1;\r
+                       Node.m_uLeafIndexes = new unsigned[1];\r
+                       Node.m_uLeafIndexes[0] = uNodeIndex;\r
+                       AddToClusterList(uNodeIndex);\r
+                       }\r
+               else\r
+                       Node.m_uSize = 0;\r
+               }\r
+\r
+// Compute initial distance matrix between leaves\r
+       SetProgressDesc("Build dist matrix");\r
+       unsigned uPairIndex = 0;\r
+       const unsigned uPairCount = (m_uLeafCount*(m_uLeafCount - 1))/2;\r
+       for (unsigned i = 0; i < m_uLeafCount; ++i)\r
+               for (unsigned j = 0; j < i; ++j)\r
+                       {\r
+                       const float dDist = (float) m_ptrSet->ComputeDist(*this, i, j);\r
+                       SetDist(i, j, dDist);\r
+                       if (0 == uPairIndex%10000)\r
+                               Progress(uPairIndex, uPairCount);\r
+                       ++uPairIndex;\r
+                       }\r
+       ProgressStepsDone();\r
+\r
+// Call CreateCluster once for each internal node in the tree\r
+       SetProgressDesc("Build guide tree");\r
+       m_uClusterCount = m_uLeafCount;\r
+       const unsigned uInternalNodeCount = m_uNodeCount - m_uLeafCount;\r
+       for (unsigned uNodeIndex = m_uLeafCount; uNodeIndex < m_uNodeCount; ++uNodeIndex)\r
+               {\r
+               unsigned i = uNodeIndex + 1 - m_uLeafCount;\r
+               Progress(i, uInternalNodeCount);\r
+               CreateCluster();\r
+               }\r
+       ProgressStepsDone();\r
+       }\r
+\r
+void Clust::CreateCluster()\r
+       {\r
+       unsigned uLeftNodeIndex;\r
+       unsigned uRightNodeIndex;\r
+       float dLeftLength;\r
+       float dRightLength;\r
+       ChooseJoin(&uLeftNodeIndex, &uRightNodeIndex, &dLeftLength, &dRightLength);\r
+\r
+       const unsigned uNewNodeIndex = m_uNodeCount - m_uClusterCount + 1;\r
+\r
+       JoinNodes(uLeftNodeIndex, uRightNodeIndex, dLeftLength, dRightLength,\r
+         uNewNodeIndex);\r
+\r
+#if    TRACE\r
+       Log("Merge New=%u L=%u R=%u Ld=%7.2g Rd=%7.2g\n",\r
+         uNewNodeIndex, uLeftNodeIndex, uRightNodeIndex, dLeftLength, dRightLength);\r
+#endif\r
+\r
+// Compute distances to other clusters\r
+       --m_uClusterCount;\r
+       for (unsigned uNodeIndex = GetFirstCluster(); uNodeIndex != uInsane;\r
+         uNodeIndex = GetNextCluster(uNodeIndex))\r
+               {\r
+               if (uNodeIndex == uLeftNodeIndex || uNodeIndex == uRightNodeIndex)\r
+                       continue;\r
+\r
+               if (uNewNodeIndex == uNodeIndex)\r
+                       continue;\r
+\r
+               const float dDist = ComputeDist(uNewNodeIndex, uNodeIndex);\r
+               SetDist(uNewNodeIndex, uNodeIndex, dDist);\r
+               }\r
+\r
+       for (unsigned uNodeIndex = GetFirstCluster(); uNodeIndex != uInsane;\r
+         uNodeIndex = GetNextCluster(uNodeIndex))\r
+               {\r
+               if (uNodeIndex == uLeftNodeIndex || uNodeIndex == uRightNodeIndex)\r
+                       continue;\r
+\r
+               if (uNewNodeIndex == uNodeIndex)\r
+                       continue;\r
+\r
+#if    REDLACK\r
+               const float dMetric = ComputeMetric(uNewNodeIndex, uNodeIndex);\r
+               InsertMetric(uNewNodeIndex, uNodeIndex, dMetric);\r
+#endif\r
+               }\r
+       }\r
+\r
+void Clust::ChooseJoin(unsigned *ptruLeftIndex, unsigned *ptruRightIndex,\r
+  float *ptrdLeftLength, float *ptrdRightLength)\r
+       {\r
+       switch (m_JoinStyle)\r
+               {\r
+       case JOIN_NearestNeighbor:\r
+               ChooseJoinNearestNeighbor(ptruLeftIndex, ptruRightIndex, ptrdLeftLength,\r
+                 ptrdRightLength);\r
+               return;\r
+       case JOIN_NeighborJoining:\r
+               ChooseJoinNeighborJoining(ptruLeftIndex, ptruRightIndex, ptrdLeftLength,\r
+                 ptrdRightLength);\r
+               return;\r
+               }\r
+       Quit("Clust::ChooseJoin, Invalid join style %u", m_JoinStyle);\r
+       }\r
+\r
+void Clust::ChooseJoinNearestNeighbor(unsigned *ptruLeftIndex,\r
+  unsigned *ptruRightIndex, float *ptrdLeftLength, float *ptrdRightLength)\r
+       {\r
+       const unsigned uClusterCount = GetClusterCount();\r
+\r
+       unsigned uMinLeftNodeIndex;\r
+       unsigned uMinRightNodeIndex;\r
+       GetMinMetric(&uMinLeftNodeIndex, &uMinRightNodeIndex);\r
+\r
+       float dMinDist = GetDist(uMinLeftNodeIndex, uMinRightNodeIndex);\r
+\r
+       const float dLeftHeight = GetHeight(uMinLeftNodeIndex);\r
+       const float dRightHeight = GetHeight(uMinRightNodeIndex);\r
+\r
+       *ptruLeftIndex = uMinLeftNodeIndex;\r
+       *ptruRightIndex = uMinRightNodeIndex;\r
+       *ptrdLeftLength = dMinDist/2 - dLeftHeight;\r
+       *ptrdRightLength = dMinDist/2 - dRightHeight;\r
+       }\r
+\r
+void Clust::ChooseJoinNeighborJoining(unsigned *ptruLeftIndex,\r
+  unsigned *ptruRightIndex, float *ptrdLeftLength, float *ptrdRightLength)\r
+       {\r
+       const unsigned uClusterCount = GetClusterCount();\r
+\r
+       //unsigned uMinLeftNodeIndex = uInsane;\r
+       //unsigned uMinRightNodeIndex = uInsane;\r
+       //float dMinD = PLUS_INFINITY;\r
+       //for (unsigned i = GetFirstCluster(); i != uInsane; i = GetNextCluster(i))\r
+       //      {\r
+       //      const float ri = Calc_r(i);\r
+       //      for (unsigned j = GetNextCluster(i); j != uInsane; j = GetNextCluster(j))\r
+       //              {\r
+       //              const float rj = Calc_r(j);\r
+       //              const float dij = GetDist(i, j);\r
+       //              const float Dij = dij - (ri + rj);\r
+       //              if (Dij < dMinD)\r
+       //                      {\r
+       //                      dMinD = Dij;\r
+       //                      uMinLeftNodeIndex = i;\r
+       //                      uMinRightNodeIndex = j;\r
+       //                      }\r
+       //              }\r
+       //      }\r
+\r
+       unsigned uMinLeftNodeIndex;\r
+       unsigned uMinRightNodeIndex;\r
+       GetMinMetric(&uMinLeftNodeIndex, &uMinRightNodeIndex);\r
+\r
+       const float dDistLR = GetDist(uMinLeftNodeIndex, uMinRightNodeIndex);\r
+       const float rL = Calc_r(uMinLeftNodeIndex);\r
+       const float rR = Calc_r(uMinRightNodeIndex);\r
+\r
+       const float dLeftLength = (dDistLR + rL - rR)/2;\r
+       const float dRightLength = (dDistLR - rL + rR)/2;\r
+\r
+       *ptruLeftIndex = uMinLeftNodeIndex;\r
+       *ptruRightIndex = uMinRightNodeIndex;\r
+       *ptrdLeftLength = dLeftLength;\r
+       *ptrdRightLength = dRightLength;\r
+       }\r
+\r
+void Clust::JoinNodes(unsigned uLeftIndex, unsigned uRightIndex, float dLeftLength,\r
+  float dRightLength, unsigned uNodeIndex)\r
+       {\r
+       ClustNode &Parent = m_Nodes[uNodeIndex];\r
+       ClustNode &Left = m_Nodes[uLeftIndex];\r
+       ClustNode &Right = m_Nodes[uRightIndex];\r
+\r
+       Left.m_dLength = dLeftLength;\r
+       Right.m_dLength = dRightLength;\r
+\r
+       Parent.m_ptrLeft = &Left;\r
+       Parent.m_ptrRight = &Right;\r
+\r
+       Left.m_ptrParent = &Parent;\r
+       Right.m_ptrParent = &Parent;\r
+\r
+       const unsigned uLeftSize = Left.m_uSize;\r
+       const unsigned uRightSize = Right.m_uSize;\r
+       const unsigned uParentSize = uLeftSize + uRightSize;\r
+       Parent.m_uSize = uParentSize;\r
+\r
+       assert(0 == Parent.m_uLeafIndexes);\r
+       Parent.m_uLeafIndexes = new unsigned[uParentSize];\r
+\r
+       const unsigned uLeftBytes = uLeftSize*sizeof(unsigned);\r
+       const unsigned uRightBytes = uRightSize*sizeof(unsigned);\r
+       memcpy(Parent.m_uLeafIndexes, Left.m_uLeafIndexes, uLeftBytes);\r
+       memcpy(Parent.m_uLeafIndexes + uLeftSize, Right.m_uLeafIndexes, uRightBytes);\r
+\r
+       DeleteFromClusterList(uLeftIndex);\r
+       DeleteFromClusterList(uRightIndex);\r
+       AddToClusterList(uNodeIndex);\r
+       }\r
+\r
+float Clust::Calc_r(unsigned uNodeIndex) const\r
+       {\r
+       const unsigned uClusterCount = GetClusterCount();\r
+       if (2 == uClusterCount)\r
+               return 0;\r
+\r
+       float dSum = 0;\r
+       for (unsigned i = GetFirstCluster(); i != uInsane; i = GetNextCluster(i))\r
+               {\r
+               if (i == uNodeIndex)\r
+                       continue;\r
+               dSum += GetDist(uNodeIndex, i);\r
+               }\r
+       return dSum/(uClusterCount - 2);\r
+       }\r
+\r
+float Clust::ComputeDist(unsigned uNewNodeIndex, unsigned uNodeIndex)\r
+       {\r
+       switch (m_CentroidStyle)\r
+               {\r
+       case LINKAGE_Avg:\r
+               return ComputeDistAverageLinkage(uNewNodeIndex, uNodeIndex);\r
+\r
+       case LINKAGE_Min:\r
+               return ComputeDistMinLinkage(uNewNodeIndex, uNodeIndex);\r
+\r
+       case LINKAGE_Max:\r
+               return ComputeDistMaxLinkage(uNewNodeIndex, uNodeIndex);\r
+\r
+       case LINKAGE_Biased:\r
+               return ComputeDistMAFFT(uNewNodeIndex, uNodeIndex);\r
+\r
+       case LINKAGE_NeighborJoining:\r
+               return ComputeDistNeighborJoining(uNewNodeIndex, uNodeIndex);\r
+               }\r
+       Quit("Clust::ComputeDist, invalid centroid style %u", m_CentroidStyle);\r
+       return (float) g_dNAN;\r
+       }\r
+\r
+float Clust::ComputeDistMinLinkage(unsigned uNewNodeIndex, unsigned uNodeIndex)\r
+       {\r
+       const unsigned uLeftNodeIndex = GetLeftIndex(uNewNodeIndex);\r
+       const unsigned uRightNodeIndex = GetRightIndex(uNewNodeIndex);\r
+       const float dDistL = GetDist(uLeftNodeIndex, uNodeIndex);\r
+       const float dDistR = GetDist(uRightNodeIndex, uNodeIndex);\r
+       return (dDistL < dDistR ? dDistL : dDistR);\r
+       }\r
+\r
+float Clust::ComputeDistMaxLinkage(unsigned uNewNodeIndex, unsigned uNodeIndex)\r
+       {\r
+       const unsigned uLeftNodeIndex = GetLeftIndex(uNewNodeIndex);\r
+       const unsigned uRightNodeIndex = GetRightIndex(uNewNodeIndex);\r
+       const float dDistL = GetDist(uLeftNodeIndex, uNodeIndex);\r
+       const float dDistR = GetDist(uRightNodeIndex, uNodeIndex);\r
+       return (dDistL > dDistR ? dDistL : dDistR);\r
+       }\r
+\r
+float Clust::ComputeDistAverageLinkage(unsigned uNewNodeIndex, unsigned uNodeIndex)\r
+       {\r
+       const unsigned uLeftNodeIndex = GetLeftIndex(uNewNodeIndex);\r
+       const unsigned uRightNodeIndex = GetRightIndex(uNewNodeIndex);\r
+       const float dDistL = GetDist(uLeftNodeIndex, uNodeIndex);\r
+       const float dDistR = GetDist(uRightNodeIndex, uNodeIndex);\r
+       return (dDistL + dDistR)/2;\r
+       }\r
+\r
+float Clust::ComputeDistNeighborJoining(unsigned uNewNodeIndex, unsigned uNodeIndex)\r
+       {\r
+       const unsigned uLeftNodeIndex = GetLeftIndex(uNewNodeIndex);\r
+       const unsigned uRightNodeIndex = GetRightIndex(uNewNodeIndex);\r
+       const float dDistLR = GetDist(uLeftNodeIndex, uRightNodeIndex);\r
+       const float dDistL = GetDist(uLeftNodeIndex, uNodeIndex);\r
+       const float dDistR = GetDist(uRightNodeIndex, uNodeIndex);\r
+       const float dDist = (dDistL + dDistR - dDistLR)/2;\r
+       return dDist;\r
+       }\r
+\r
+// This is a mysterious variant of UPGMA reverse-engineered from MAFFT source.\r
+float Clust::ComputeDistMAFFT(unsigned uNewNodeIndex, unsigned uNodeIndex)\r
+       {\r
+       const unsigned uLeftNodeIndex = GetLeftIndex(uNewNodeIndex);\r
+       const unsigned uRightNodeIndex = GetRightIndex(uNewNodeIndex);\r
+\r
+       const float dDistLR = GetDist(uLeftNodeIndex, uRightNodeIndex);\r
+       const float dDistL = GetDist(uLeftNodeIndex, uNodeIndex);\r
+       const float dDistR = GetDist(uRightNodeIndex, uNodeIndex);\r
+       const float dMinDistLR = (dDistL < dDistR ? dDistL : dDistR);\r
+       const float dSumDistLR = dDistL + dDistR;\r
+       const float dDist = dMinDistLR*(1 - g_dSUEFF) + dSumDistLR*g_dSUEFF/2;\r
+       return dDist;\r
+       }\r
+\r
+unsigned Clust::GetClusterCount() const\r
+       {\r
+       return m_uClusterCount;\r
+       }\r
+\r
+void Clust::LogMe() const\r
+       {\r
+       Log("Clust %u leaves, %u nodes, %u clusters.\n",\r
+         m_uLeafCount, m_uNodeCount, m_uClusterCount);\r
+\r
+       Log("Distance matrix\n");\r
+       const unsigned uNodeCount = GetNodeCount();\r
+       Log("       ");\r
+       for (unsigned i = 0; i < uNodeCount - 1; ++i)\r
+               Log(" %7u", i);\r
+       Log("\n");\r
+\r
+       Log("       ");\r
+       for (unsigned i = 0; i < uNodeCount - 1; ++i)\r
+               Log("  ------");\r
+       Log("\n");\r
+\r
+       for (unsigned i = 0; i < uNodeCount - 1; ++i)\r
+               {\r
+               Log("%4u:  ", i);\r
+               for (unsigned j = 0; j < i; ++j)\r
+                       Log(" %7.2g", GetDist(i, j));\r
+               Log("\n");\r
+               }\r
+\r
+       Log("\n");\r
+       Log("Node  Size  Prnt  Left  Rght   Length  Name\n");\r
+       Log("----  ----  ----  ----  ----   ------  ----\n");\r
+       for (unsigned uNodeIndex = 0; uNodeIndex < m_uNodeCount; ++uNodeIndex)\r
+               {\r
+               const ClustNode &Node = m_Nodes[uNodeIndex];\r
+               Log("%4u  %4u", uNodeIndex, Node.m_uSize);\r
+               if (0 != Node.m_ptrParent)\r
+                       Log("  %4u", Node.m_ptrParent->m_uIndex);\r
+               else\r
+                       Log("      ");\r
+\r
+               if (0 != Node.m_ptrLeft)\r
+                       Log("  %4u", Node.m_ptrLeft->m_uIndex);\r
+               else\r
+                       Log("      ");\r
+\r
+               if (0 != Node.m_ptrRight)\r
+                       Log("  %4u", Node.m_ptrRight->m_uIndex);\r
+               else\r
+                       Log("      ");\r
+\r
+               if (uNodeIndex != m_uNodeCount - 1)\r
+                       Log("  %7.3g", Node.m_dLength);\r
+               if (IsLeaf(uNodeIndex))\r
+                       {\r
+                       const char *ptrName = GetNodeName(uNodeIndex);\r
+                       if (0 != ptrName)\r
+                               Log("  %s", ptrName);\r
+                       }\r
+               if (GetRootNodeIndex() == uNodeIndex)\r
+                       Log("    [ROOT]");\r
+               Log("\n");\r
+               }\r
+       }\r
+\r
+const ClustNode &Clust::GetNode(unsigned uNodeIndex) const\r
+       {\r
+       if (uNodeIndex >= m_uNodeCount)\r
+               Quit("ClustNode::GetNode(%u) %u", uNodeIndex, m_uNodeCount);\r
+       return m_Nodes[uNodeIndex];\r
+       }\r
+\r
+bool Clust::IsLeaf(unsigned uNodeIndex) const\r
+       {\r
+       return uNodeIndex < m_uLeafCount;\r
+       }\r
+\r
+unsigned Clust::GetClusterSize(unsigned uNodeIndex) const\r
+       {\r
+       const ClustNode &Node = GetNode(uNodeIndex);\r
+       return Node.m_uSize;\r
+       }\r
+\r
+unsigned Clust::GetLeftIndex(unsigned uNodeIndex) const\r
+       {\r
+       const ClustNode &Node = GetNode(uNodeIndex);\r
+       if (0 == Node.m_ptrLeft)\r
+               Quit("Clust::GetLeftIndex: leaf");\r
+       return Node.m_ptrLeft->m_uIndex;\r
+       }\r
+\r
+unsigned Clust::GetRightIndex(unsigned uNodeIndex) const\r
+       {\r
+       const ClustNode &Node = GetNode(uNodeIndex);\r
+       if (0 == Node.m_ptrRight)\r
+               Quit("Clust::GetRightIndex: leaf");\r
+       return Node.m_ptrRight->m_uIndex;\r
+       }\r
+\r
+float Clust::GetLength(unsigned uNodeIndex) const\r
+       {\r
+       const ClustNode &Node = GetNode(uNodeIndex);\r
+       return Node.m_dLength;\r
+       }\r
+\r
+void Clust::SetLeafCount(unsigned uLeafCount)\r
+       {\r
+       if (uLeafCount <= 1)\r
+               Quit("Clust::SetLeafCount(%u)", uLeafCount);\r
+\r
+       m_uLeafCount = uLeafCount;\r
+       const unsigned uNodeCount = GetNodeCount();\r
+\r
+// Triangular matrix size excluding diagonal (all zeros in our case).\r
+       m_uTriangularMatrixSize = (uNodeCount*(uNodeCount - 1))/2;\r
+       m_dDist = new float[m_uTriangularMatrixSize];\r
+       }\r
+\r
+unsigned Clust::GetLeafCount() const\r
+       {\r
+       return m_uLeafCount;\r
+       }\r
+\r
+unsigned Clust::VectorIndex(unsigned uIndex1, unsigned uIndex2) const\r
+       {\r
+       const unsigned uNodeCount = GetNodeCount();\r
+       if (uIndex1 >= uNodeCount || uIndex2 >= uNodeCount)\r
+               Quit("DistVectorIndex(%u,%u) %u", uIndex1, uIndex2, uNodeCount);\r
+       unsigned v;\r
+       if (uIndex1 >= uIndex2)\r
+               v = uIndex2 + (uIndex1*(uIndex1 - 1))/2;\r
+       else\r
+               v = uIndex1 + (uIndex2*(uIndex2 - 1))/2;\r
+       assert(v < m_uTriangularMatrixSize);\r
+       return v;\r
+       }\r
+\r
+float Clust::GetDist(unsigned uIndex1, unsigned uIndex2) const\r
+       {\r
+       unsigned v = VectorIndex(uIndex1, uIndex2);\r
+       return m_dDist[v];\r
+       }\r
+\r
+void Clust::SetDist(unsigned uIndex1, unsigned uIndex2, float dDist)\r
+       {\r
+       unsigned v = VectorIndex(uIndex1, uIndex2);\r
+       m_dDist[v] = dDist;\r
+       }\r
+\r
+float Clust::GetHeight(unsigned uNodeIndex) const\r
+       {\r
+       if (IsLeaf(uNodeIndex))\r
+               return 0;\r
+\r
+       const unsigned uLeftIndex = GetLeftIndex(uNodeIndex);\r
+       const unsigned uRightIndex = GetRightIndex(uNodeIndex);\r
+       const float dLeftLength = GetLength(uLeftIndex);\r
+       const float dRightLength = GetLength(uRightIndex);\r
+       const float dLeftHeight = dLeftLength + GetHeight(uLeftIndex);\r
+       const float dRightHeight = dRightLength + GetHeight(uRightIndex);\r
+       return (dLeftHeight + dRightHeight)/2;\r
+       }\r
+\r
+const char *Clust::GetNodeName(unsigned uNodeIndex) const\r
+       {\r
+       if (!IsLeaf(uNodeIndex))\r
+               Quit("Clust::GetNodeName, is not leaf");\r
+       return m_ptrSet->GetLeafName(uNodeIndex);\r
+       }\r
+\r
+unsigned Clust::GetNodeId(unsigned uNodeIndex) const\r
+       {\r
+       if (uNodeIndex >= GetLeafCount())\r
+               return 0;\r
+       return m_ptrSet->GetLeafId(uNodeIndex);\r
+       }\r
+\r
+unsigned Clust::GetLeaf(unsigned uNodeIndex, unsigned uLeafIndex) const\r
+       {\r
+       const ClustNode &Node = GetNode(uNodeIndex);\r
+       const unsigned uLeafCount = Node.m_uSize;\r
+       if (uLeafIndex >= uLeafCount)\r
+               Quit("Clust::GetLeaf, invalid index");\r
+       const unsigned uIndex = Node.m_uLeafIndexes[uLeafIndex];\r
+       if (uIndex >= m_uNodeCount)\r
+               Quit("Clust::GetLeaf, index out of range");\r
+       return uIndex;\r
+       }\r
+\r
+unsigned Clust::GetFirstCluster() const\r
+       {\r
+       if (0 == m_ptrClusterList)\r
+               return uInsane;\r
+       return m_ptrClusterList->m_uIndex;\r
+       }\r
+\r
+unsigned Clust::GetNextCluster(unsigned uIndex) const\r
+       {\r
+       ClustNode *ptrNode = &m_Nodes[uIndex];\r
+       if (0 == ptrNode->m_ptrNextCluster)\r
+               return uInsane;\r
+       return ptrNode->m_ptrNextCluster->m_uIndex;\r
+       }\r
+\r
+void Clust::DeleteFromClusterList(unsigned uNodeIndex)\r
+       {\r
+       assert(uNodeIndex < m_uNodeCount);\r
+       ClustNode *ptrNode = &m_Nodes[uNodeIndex];\r
+       ClustNode *ptrPrev = ptrNode->m_ptrPrevCluster;\r
+       ClustNode *ptrNext = ptrNode->m_ptrNextCluster;\r
+\r
+       if (0 != ptrNext)\r
+               ptrNext->m_ptrPrevCluster = ptrPrev;\r
+       if (0 == ptrPrev)\r
+               {\r
+               assert(m_ptrClusterList == ptrNode);\r
+               m_ptrClusterList = ptrNext;\r
+               }\r
+       else\r
+               ptrPrev->m_ptrNextCluster = ptrNext;\r
+\r
+       ptrNode->m_ptrNextCluster = 0;\r
+       ptrNode->m_ptrPrevCluster = 0;\r
+       }\r
+\r
+void Clust::AddToClusterList(unsigned uNodeIndex)\r
+       {\r
+       assert(uNodeIndex < m_uNodeCount);\r
+       ClustNode *ptrNode = &m_Nodes[uNodeIndex];\r
+\r
+       if (0 != m_ptrClusterList)\r
+               m_ptrClusterList->m_ptrPrevCluster = ptrNode;\r
+\r
+       ptrNode->m_ptrNextCluster = m_ptrClusterList;\r
+       ptrNode->m_ptrPrevCluster = 0;\r
+\r
+       m_ptrClusterList = ptrNode;\r
+       }\r
+\r
+float Clust::ComputeMetric(unsigned uIndex1, unsigned uIndex2) const\r
+       {\r
+       switch (m_JoinStyle)\r
+               {\r
+       case JOIN_NearestNeighbor:\r
+               return ComputeMetricNearestNeighbor(uIndex1, uIndex2);\r
+\r
+       case JOIN_NeighborJoining:\r
+               return ComputeMetricNeighborJoining(uIndex1, uIndex2);\r
+               }\r
+       Quit("Clust::ComputeMetric");\r
+       return 0;\r
+       }\r
+\r
+float Clust::ComputeMetricNeighborJoining(unsigned i, unsigned j) const\r
+       {\r
+       float ri = Calc_r(i);\r
+       float rj = Calc_r(j);\r
+       float dij = GetDist(i, j);\r
+       float dMetric = dij - (ri + rj);\r
+       return (float) dMetric;\r
+       }\r
+\r
+float Clust::ComputeMetricNearestNeighbor(unsigned i, unsigned j) const\r
+       {\r
+       return (float) GetDist(i, j);\r
+       }\r
+\r
+float Clust::GetMinMetricBruteForce(unsigned *ptruIndex1, unsigned *ptruIndex2) const\r
+       {\r
+       unsigned uMinLeftNodeIndex = uInsane;\r
+       unsigned uMinRightNodeIndex = uInsane;\r
+       float dMinMetric = PLUS_INFINITY;\r
+       for (unsigned uLeftNodeIndex = GetFirstCluster(); uLeftNodeIndex != uInsane;\r
+         uLeftNodeIndex = GetNextCluster(uLeftNodeIndex))\r
+               {\r
+               for (unsigned uRightNodeIndex = GetNextCluster(uLeftNodeIndex);\r
+                 uRightNodeIndex != uInsane;\r
+                 uRightNodeIndex = GetNextCluster(uRightNodeIndex))\r
+                       {\r
+                       float dMetric = ComputeMetric(uLeftNodeIndex, uRightNodeIndex);\r
+                       if (dMetric < dMinMetric)\r
+                               {\r
+                               dMinMetric = dMetric;\r
+                               uMinLeftNodeIndex = uLeftNodeIndex;\r
+                               uMinRightNodeIndex = uRightNodeIndex;\r
+                               }\r
+                       }\r
+               }\r
+       *ptruIndex1 = uMinLeftNodeIndex;\r
+       *ptruIndex2 = uMinRightNodeIndex;\r
+       return dMinMetric;\r
+       }\r
+\r
+float Clust::GetMinMetric(unsigned *ptruIndex1, unsigned *ptruIndex2) const\r
+       {\r
+       return GetMinMetricBruteForce(ptruIndex1, ptruIndex2);\r
+       }\r