[dasher] * Made Children() read-only; nodes add selves to parent when created/SetParent'd



commit 564fc36774a1f548c96908050a451dab11671169
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Wed Dec 2 11:56:06 2009 +0000

    * Made Children() read-only; nodes add selves to parent when created/SetParent'd
    * Tidy up AlphabetManager and AlphIO:
    
      RecursiveIterateGroup adds children immediately rather than via
      array, and computes probabilities itself rather than duplicating
      code for symbols/groups; AlphIO puts SGroupInfo child lists into
      increasing (was decreasing) order and stores m_vGroups temporary
      (only used during parsing) in parser not result.

 ChangeLog                            |    7 ++-
 Src/DasherCore/Alphabet/AlphIO.cpp   |   29 ++++++--
 Src/DasherCore/Alphabet/AlphIO.h     |    2 +-
 Src/DasherCore/AlphabetManager.cpp   |  131 +++++++++++-----------------------
 Src/DasherCore/AlphabetManager.h     |    9 +--
 Src/DasherCore/ControlManager.cpp    |    2 +-
 Src/DasherCore/ConversionHelper.cpp  |    4 +-
 Src/DasherCore/ConversionManager.cpp |    2 +-
 Src/DasherCore/DasherModel.cpp       |    6 +-
 Src/DasherCore/DasherNode.cpp        |   10 +++-
 Src/DasherCore/DasherNode.h          |    3 +-
 11 files changed, 93 insertions(+), 112 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 72b5dd0..438e239 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2009-12-02  Alan Lawrence <acl33 inf phy cam ac uk>
+
+	* Made Children() read-only; nodes add selves to parent
+	  when created/SetParent'd
+	* Tidy up AlphabetManager and AlphIO
+
 2009-12-01  Alan Lawrence <acl33 inf phy cam ac uk>
 
 	* MacOSX: Set DEBUG for Development build;
@@ -6,7 +12,6 @@
 	  all you have written)
 	* (Partial) build fixes for Japanese (CannaConversionHelper)
 
-
 2009-12-01  Patrick Welche <prlw1 cam ac uk>
 
 	Update POTFILES.in
diff --git a/Src/DasherCore/Alphabet/AlphIO.cpp b/Src/DasherCore/Alphabet/AlphIO.cpp
index 5bf7318..130f0c1 100644
--- a/Src/DasherCore/Alphabet/AlphIO.cpp
+++ b/Src/DasherCore/Alphabet/AlphIO.cpp
@@ -433,7 +433,7 @@ void CAlphIO::XML_StartElement(void *userData, const XML_Char *name, const XML_C
     }
     Me->InputInfo.m_iConversionID = 0;
     Me->InputInfo.m_strDefaultContext = ". ";
-
+    Me->m_vGroups.clear();
     return;
   }
 
@@ -596,9 +596,9 @@ void CAlphIO::XML_StartElement(void *userData, const XML_Char *name, const XML_C
 
     pNewGroup->pChild = NULL;
 
-    if(Me->InputInfo.m_vGroups.size() > 0) {
-      pNewGroup->pNext = Me->InputInfo.m_vGroups.back()->pChild;
-      Me->InputInfo.m_vGroups.back()->pChild = pNewGroup;
+    if(Me->m_vGroups.size() > 0) {
+      pNewGroup->pNext = Me->m_vGroups.back()->pChild;
+      Me->m_vGroups.back()->pChild = pNewGroup;
     }
     else {
       pNewGroup->pNext = Me->InputInfo.m_pBaseGroup;
@@ -606,7 +606,7 @@ void CAlphIO::XML_StartElement(void *userData, const XML_Char *name, const XML_C
     }
 
 
-    Me->InputInfo.m_vGroups.push_back(pNewGroup);
+    Me->m_vGroups.push_back(pNewGroup);
 
     return;
   }
@@ -734,10 +734,23 @@ void CAlphIO::XML_StartElement(void *userData, const XML_Char *name, const XML_C
   }
 }
 
+void Reverse(SGroupInfo *&pList) {
+  SGroupInfo *pFirst = pList;  
+  SGroupInfo *pPrev = NULL;
+  while (pFirst) {
+    SGroupInfo *pNext = pFirst->pNext;
+    pFirst->pNext = pPrev;
+    pPrev = pFirst;
+    pFirst = pNext;
+  }
+  pList=pPrev;
+}
+
 void CAlphIO::XML_EndElement(void *userData, const XML_Char *name) {
   CAlphIO *Me = (CAlphIO *) userData;
 
   if(strcmp(name, "alphabet") == 0) {
+    Reverse(Me->InputInfo.m_pBaseGroup);
     Me->Alphabets[Me->InputInfo.AlphID] = Me->InputInfo;
     return;
   }
@@ -758,8 +771,10 @@ void CAlphIO::XML_EndElement(void *userData, const XML_Char *name) {
   }
 
   if(!strcmp(name, "group")) {
-    Me->InputInfo.m_vGroups.back()->iEnd = Me->InputInfo.m_iCharacters;
-    Me->InputInfo.m_vGroups.pop_back();
+    Me->m_vGroups.back()->iEnd = Me->InputInfo.m_iCharacters;
+    //child groups were added (to linked list) in reverse order. Put them in (iStart/iEnd) order...
+    Reverse(Me->m_vGroups.back()->pChild);
+    Me->m_vGroups.pop_back();
     return;
   }
 }
diff --git a/Src/DasherCore/Alphabet/AlphIO.h b/Src/DasherCore/Alphabet/AlphIO.h
index 8a6a4be..e9ae602 100644
--- a/Src/DasherCore/Alphabet/AlphIO.h
+++ b/Src/DasherCore/Alphabet/AlphIO.h
@@ -75,7 +75,6 @@ public:
 /*     // --- */
 
     int m_iCharacters;
-    std::vector<SGroupInfo *> m_vGroups;
     SGroupInfo *m_pBaseGroup;
 
     std::vector<character> m_vCharacters;
@@ -98,6 +97,7 @@ public:
   void SetInfo(const AlphInfo & NewInfo);
   void Delete(const std::string & AlphID);
 private:
+  std::vector<SGroupInfo *> m_vGroups;
   AlphInfo BlankInfo;
   std::string SystemLocation;
   std::string UserLocation;
diff --git a/Src/DasherCore/AlphabetManager.cpp b/Src/DasherCore/AlphabetManager.cpp
index 9ba3483..f6629b8 100644
--- a/Src/DasherCore/AlphabetManager.cpp
+++ b/Src/DasherCore/AlphabetManager.cpp
@@ -152,23 +152,7 @@ void CAlphabetManager::CAlphNode::PopulateChildren() {
   m_pMgr->PopulateChildrenWithSymbol( this, -2, 0 );
 }
 
-CAlphabetManager::CAlphNode *CAlphabetManager::CreateGroupNode(CAlphNode *pParent, SGroupInfo *pInfo, std::vector<unsigned int> *pCProb, unsigned int iStart, unsigned int iEnd, unsigned int iMin, unsigned int iMax) {
-
-#ifdef WIN32
-  unsigned int iLbnd = (((*pCProb)[iStart-1] - (*pCProb)[iMin-1]) *
-	  (unsigned __int64)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
-	  ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
-  unsigned int iHbnd = (((*pCProb)[iEnd-1] - (*pCProb)[iMin-1]) *
-	  (unsigned __int64)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
-	  ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
-#else
-  unsigned int iLbnd = (((*pCProb)[iStart-1] - (*pCProb)[iMin-1]) *
-	  (unsigned long long int)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
-	  ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
-  unsigned int iHbnd = (((*pCProb)[iEnd-1] - (*pCProb)[iMin-1]) *
-	  (unsigned long long int)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
-	  ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
-#endif
+CAlphabetManager::CAlphNode *CAlphabetManager::CreateGroupNode(CAlphNode *pParent, SGroupInfo *pInfo, unsigned int iLbnd, unsigned int iHbnd) {
 
   // TODO: More sensible structure in group data to map directly to this
   CDasherNode::SDisplayInfo *pDisplayInfo = new CDasherNode::SDisplayInfo;
@@ -192,30 +176,6 @@ CAlphabetManager::CAlphNode *CAlphabetManager::CreateGroupNode(CAlphNode *pParen
   return pNewNode;
 }
 
-// TODO: use these functions elsewhere in the file
-CDasherNode *CAlphabetManager::CreateSymbolNode(CAlphNode *pParent, symbol iSymbol, std::vector<unsigned int> *pCProb, unsigned int iStart, unsigned int iEnd, unsigned int iMin, unsigned int iMax, symbol iExistingSymbol, CDasherNode *pExistingChild) {
-  // TODO: Node deletion etc.
-  //std::cout << "isymbol: "<<iSymbol << " " << m_pNCManager->GetStartConversionSymbol() << std::endl;
-
-#ifdef WIN32
-  unsigned int iLbnd = (((*pCProb)[iStart-1] - (*pCProb)[iMin-1]) *
-	  (unsigned __int64)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
-	  ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
-  unsigned int iHbnd = (((*pCProb)[iEnd-1] - (*pCProb)[iMin-1]) *
-	  (unsigned __int64)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
-	  ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
-#else
-  unsigned int iLbnd = (((*pCProb)[iStart-1] - (*pCProb)[iMin-1]) *
-	  (unsigned long long int)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
-	  ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
-  unsigned int iHbnd = (((*pCProb)[iEnd-1] - (*pCProb)[iMin-1]) *
-	  (unsigned long long int)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
-	  ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
-#endif
-
-  return CreateSymbolNode(pParent, iSymbol, iLbnd, iHbnd, iExistingSymbol, pExistingChild);
-}
-
 CLanguageModel::Context CAlphabetManager::CreateSymbolContext(CAlphNode *pParent, symbol iSymbol)
 {
   CLanguageModel::Context iContext = m_pLanguageModel->CloneContext(pParent->iContext);
@@ -294,60 +254,53 @@ CDasherNode *CAlphabetManager::CreateSymbolNode(CAlphNode *pParent, symbol iSymb
 void CAlphabetManager::RecursiveIterateGroup(CAlphNode *pParent, SGroupInfo *pInfo, std::vector<symbol> *pSymbols, std::vector<unsigned int> *pCProb, int iMin, int iMax, symbol iExistingSymbol, CDasherNode *pExistingChild) {
   // TODO: Think through alphabet file formats etc. to make this class easier.
   // TODO: Throw a warning if parent node already has children
-
-  CDasherNode **pChildNodes = new CDasherNode *[iMax - iMin];
-
-  for(int i(iMin); i < iMax; ++i) {
-    pChildNodes[i - iMin] = NULL;
-  }
-
-  // Create child nodes and cache them for later
+  
+  // Create child nodes and add them
+  
+  int i(iMin); //lowest index of child which we haven't yet added
+  SGroupInfo *pCurrentNode(pInfo);
   // The SGroupInfo structure has something like linked list behaviour
   // Each SGroupInfo contains a pNext, a pointer to a sibling group info
-  SGroupInfo *pCurrentNode(pInfo);
-  while(pCurrentNode) {
-    CAlphNode *pNewChild = CreateGroupNode(pParent,
-					     pCurrentNode,
-					     pCProb,
-					     pCurrentNode->iStart,
-					     pCurrentNode->iEnd,
-					     iMin, iMax);
-
-    RecursiveIterateGroup(pNewChild,
-			  pCurrentNode->pChild,
-			  pSymbols,
-			  pCProb,
-			  pCurrentNode->iStart,
-			  pCurrentNode->iEnd,
-			  iExistingSymbol, pExistingChild);
-
-    for(int i(pCurrentNode->iStart); i < pCurrentNode->iEnd; ++i) {
-      pChildNodes[i - iMin] = pNewChild;
-    }
-
-    pCurrentNode = pCurrentNode->pNext; // Move along the group
-  }
-
-  CDasherNode *pLastChild = NULL;
-
-  // Now actually populate the children
-  for(int i(iMin); i < iMax; ++i) {
-    if(!pChildNodes[i-iMin]) {
-      CDasherNode *pNewChild = CreateSymbolNode(pParent, (*pSymbols)[i], pCProb, i, i+1, iMin, iMax, iExistingSymbol, pExistingChild);
-      pParent->Children().push_back(pNewChild);
-      pLastChild = pNewChild;
-    }
-    else if (pChildNodes[i-iMin] != pLastChild) {
-      pParent->Children().push_back(pChildNodes[i-iMin]);
-      pLastChild = pChildNodes[i-iMin];
+  while (i < iMax) {
+    CDasherNode *pNewChild;
+    bool bSymbol = !pCurrentNode //gone past last subgroup
+                  || i < pCurrentNode->iStart; //not reached next subgroup
+    const int iStart=i, iEnd = (bSymbol) ? i+1 : pCurrentNode->iEnd;
+#ifdef WIN32
+    typedef unsigned __int64 temptype;
+#else
+    typedef unsigned long long int temptype;
+#endif
+    unsigned int iLbnd = (((*pCProb)[iStart-1] - (*pCProb)[iMin-1]) *
+                          (temptype)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
+                         ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
+    unsigned int iHbnd = (((*pCProb)[iEnd-1] - (*pCProb)[iMin-1]) *
+                          (temptype)(m_pNCManager->GetLongParameter(LP_NORMALIZATION))) /
+                         ((*pCProb)[iMax-1] - (*pCProb)[iMin-1]);
+    
+    if (bSymbol) {
+      pNewChild = CreateSymbolNode(pParent, (*pSymbols)[i], iLbnd, iHbnd, iExistingSymbol, pExistingChild);
+      i++; //make one symbol at a time - move onto next in next iteration
+    } else { //in/reached subgroup - do entire group in one go:
+      CAlphNode *pNew;
+      pNewChild= pNew= CreateGroupNode(pParent,
+                                       pCurrentNode, iLbnd, iHbnd);
+
+      RecursiveIterateGroup(pNew,
+          pCurrentNode->pChild,
+          pSymbols,
+          pCProb,
+          pCurrentNode->iStart,
+          pCurrentNode->iEnd,
+          iExistingSymbol, pExistingChild);
+
+      i = pCurrentNode->iEnd; //make one group at a time - so move past entire group...
+      pCurrentNode = pCurrentNode->pNext;
     }
+    DASHER_ASSERT(pParent->GetChildren().back()==pNewChild);
   }
 
-  //  std::cout << pParent << std::endl;
-
   pParent->SetFlag(NF_ALLCHILDREN, true);
-
-  delete[] pChildNodes;
 }
 
 void CAlphabetManager::PopulateChildrenWithSymbol( CAlphNode *pNode, int iExistingSymbol, CDasherNode *pExistingChild ) {
diff --git a/Src/DasherCore/AlphabetManager.h b/Src/DasherCore/AlphabetManager.h
index cea602b..a4199d3 100644
--- a/Src/DasherCore/AlphabetManager.h
+++ b/Src/DasherCore/AlphabetManager.h
@@ -100,13 +100,13 @@ namespace Dasher {
     virtual CAlphNode *makeNode(CDasherNode *pParent, int iLbnd, int iHbnd, CDasherNode::SDisplayInfo *pDispInfo);    
 
     
-  void PopulateChildrenWithSymbol( CAlphNode *pNode, int iExistingSymbol, CDasherNode *pExistingChild );
+    void PopulateChildrenWithSymbol( CAlphNode *pNode, int iExistingSymbol, CDasherNode *pExistingChild );
 
-	virtual CDasherNode *CreateSymbolNode(CAlphNode *pParent, symbol iSymbol, unsigned int iLbnd, unsigned int iHbnd, symbol iExistingSymbol, CDasherNode *pExistingChild);
+    virtual CDasherNode *CreateSymbolNode(CAlphNode *pParent, symbol iSymbol, unsigned int iLbnd, unsigned int iHbnd, symbol iExistingSymbol, CDasherNode *pExistingChild);
     virtual CLanguageModel::Context CreateSymbolContext(CAlphNode *pParent, symbol iSymbol);
 
     CLanguageModel *m_pLanguageModel;
-	CNodeCreationManager *m_pNCManager;
+    CNodeCreationManager *m_pNCManager;
 
   private:
     
@@ -114,8 +114,7 @@ namespace Dasher {
 
     void RecursiveIterateGroup(CAlphNode *pParent, SGroupInfo *pInfo, std::vector<symbol> *pSymbols, std::vector<unsigned int> *pCProb, int iMin, int iMax, symbol iExistingSymbol, CDasherNode *pExistingChild);
 
-    CAlphNode *CreateGroupNode(CAlphNode *pParent, SGroupInfo *pInfo, std::vector<unsigned int> *pCProb, unsigned int iStart, unsigned int iEnd, unsigned int iMin, unsigned int iMax);
-    CDasherNode *CreateSymbolNode(CAlphNode *pParent, symbol iSymbol, std::vector<unsigned int> *pCProb, unsigned int iStart, unsigned int iEnd, unsigned int iMin, unsigned int iMax, symbol iExistingSymbol, CDasherNode *pExistingChild);
+    CAlphNode *CreateGroupNode(CAlphNode *pParent, SGroupInfo *pInfo, unsigned int iLbnd, unsigned int iHbnd);
 
     CLanguageModel::Context m_iLearnContext;
     CDasherInterfaceBase *m_pInterface;
diff --git a/Src/DasherCore/ControlManager.cpp b/Src/DasherCore/ControlManager.cpp
index be6d408..a5f9bb0 100644
--- a/Src/DasherCore/ControlManager.cpp
+++ b/Src/DasherCore/ControlManager.cpp
@@ -335,7 +335,7 @@ void CControlManager::CContNode::PopulateChildren() {
 
        pNewNode->m_iOffset = m_iOffset;
      }
-     Children().push_back(pNewNode);
+     DASHER_ASSERT(GetChildren().back()==pNewNode);
      ++iIdx;
    }
 }
diff --git a/Src/DasherCore/ConversionHelper.cpp b/Src/DasherCore/ConversionHelper.cpp
index 3e14591..3720be0 100644
--- a/Src/DasherCore/ConversionHelper.cpp
+++ b/Src/DasherCore/ConversionHelper.cpp
@@ -273,7 +273,7 @@ void CConversionHelper::CConvHNode::PopulateChildren() {
 	pNewNode->iContext = iContext;
       }
 
-      Children().push_back(pNewNode);
+      DASHER_ASSERT(GetChildren().back()==pNewNode);
 
       pCurrentSCEChild = pCurrentSCEChild->GetNext();
       ++iIdx;
@@ -292,7 +292,7 @@ void CConversionHelper::CConvHNode::PopulateChildren() {
       CDasherNode *pNewNode = mgr()->m_pNCManager->GetAlphRoot(this, iLbnd, iHbnd, NULL, m_iOffset);
       pNewNode->SetFlag(NF_SEEN, false);
 
-      Children().push_back(pNewNode);
+      DASHER_ASSERT(GetChildren().back()==pNewNode);
       //    pNode->SetHasAllChildren(false);
       //}
     /* What do the following code do?
diff --git a/Src/DasherCore/ConversionManager.cpp b/Src/DasherCore/ConversionManager.cpp
index c562354..13c5f8a 100644
--- a/Src/DasherCore/ConversionManager.cpp
+++ b/Src/DasherCore/ConversionManager.cpp
@@ -105,7 +105,7 @@ void CConversionManager::CConvNode::PopulateChildren() {
   CDasherNode *pNewNode = m_pMgr->m_pNCManager->GetAlphRoot(this, iLbnd, iHbnd, NULL, m_iOffset + 1);
   pNewNode->SetFlag(NF_SEEN, false);
 
-  Children().push_back(pNewNode);
+  DASHER_ASSERT(GetChildren().back()==pNewNode);
 }
 
 CConversionManager::CConvNode::~CConvNode() {
diff --git a/Src/DasherCore/DasherModel.cpp b/Src/DasherCore/DasherModel.cpp
index 3102659..7ed652f 100644
--- a/Src/DasherCore/DasherModel.cpp
+++ b/Src/DasherCore/DasherModel.cpp
@@ -672,7 +672,7 @@ void CDasherModel::Push_Node(CDasherNode *pNode) {
   
 
   if(pNode->GetFlag(NF_ALLCHILDREN)) {
-    DASHER_ASSERT(pNode->Children().size() > 0);
+    DASHER_ASSERT(pNode->GetChildren().size() > 0);
     return;
   }
 
@@ -789,9 +789,9 @@ bool CDasherModel::RecursiveCheckRoot(CDasherNode *pNode, CDasherNode **pNewNode
   DASHER_ASSERT(pNode != NULL);
   DASHER_ASSERT(pNewNode != NULL);
 
-  CDasherNode::ChildMap & children = pNode->Children();
+  const CDasherNode::ChildMap & children = pNode->GetChildren();
   
-  for(CDasherNode::ChildMap::iterator it(children.begin()); it != children.end(); ++it) {
+  for(CDasherNode::ChildMap::const_iterator it(children.begin()); it != children.end(); ++it) {
     if((*it)->GetFlag(NF_SUBNODE)) {
       if(!RecursiveCheckRoot(*it, pNewNode, bFound))
 	return false;
diff --git a/Src/DasherCore/DasherNode.cpp b/Src/DasherCore/DasherNode.cpp
index 855755e..57535bb 100644
--- a/Src/DasherCore/DasherNode.cpp
+++ b/Src/DasherCore/DasherNode.cpp
@@ -48,6 +48,11 @@ CDasherNode::CDasherNode(CDasherNode *pParent, int iLbnd, int iHbnd, SDisplayInf
   DASHER_ASSERT(pDisplayInfo != NULL);
 	
   m_pParent = pParent;  
+  if (pParent) {
+    DASHER_ASSERT(!pParent->GetFlag(NF_ALLCHILDREN));
+    pParent->Children().push_back(this);
+  }
+
   m_iLbnd = iLbnd;
   m_iHbnd = iHbnd;
   m_pDisplayInfo = pDisplayInfo;
@@ -130,7 +135,7 @@ void CDasherNode::OrphanChild(CDasherNode *pChild) {
     }
   }
   
-  pChild->SetParent(NULL);
+  pChild->m_pParent=NULL;
 
   Children().clear();
   SetFlag(NF_ALLCHILDREN, false);
@@ -187,7 +192,10 @@ void CDasherNode::SetFlag(int iFlag, bool bValue) {
 }
  
 void CDasherNode::SetParent(CDasherNode *pNewParent) {
+  DASHER_ASSERT(pNewParent);
+  DASHER_ASSERT(!pNewParent->GetFlag(NF_ALLCHILDREN));
   m_pParent = pNewParent;
+  pNewParent->Children().push_back(this);
 }
 
 int CDasherNode::MostProbableChild() {
diff --git a/Src/DasherCore/DasherNode.h b/Src/DasherCore/DasherNode.h
index e38b94b..af0e591 100644
--- a/Src/DasherCore/DasherNode.h
+++ b/Src/DasherCore/DasherNode.h
@@ -183,7 +183,6 @@ class Dasher::CDasherNode:private NoClones {
   /// @name Routines for manipulating relatives
   /// @{
 
-  inline ChildMap & Children();
   inline const ChildMap & GetChildren() const;
   inline unsigned int ChildCount() const;
   inline CDasherNode *Parent() const;
@@ -269,6 +268,8 @@ class Dasher::CDasherNode:private NoClones {
   int m_iNumSymbols;
   
  private:
+  inline ChildMap &Children();
+
   SDisplayInfo *m_pDisplayInfo;
 
   int m_iLbnd;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]