1 // Implementation of TCollection methods
2 // Copyright © 2009 The University of Chicago
3 #ifndef COLLECTIONTEMPLATE_TCC
4 #define COLLECTIONTEMPLATE_TCC
6 #include "CollectionTemplate.h"
12 #include "MiniLexicon.h"
14 #include "linguisticamainwindow.h"
15 #include "ui/Status.h"
16 #include "CompareFunc.h"
19 TCollection<T>::TCollection()
22 m_DeletePointers = TRUE;
23 m_DeletionArray = NULL;
24 m_HashHasChangedFlag = FALSE;
25 m_MemberName = QString::null;
26 m_PointerArray = NULL;
29 m_pReverseTrie = NULL;
30 m_pTerminalNodeArray = NULL;
39 TCollection<T>::TCollection(CMiniLexicon* Mini, QString MemberName)
42 m_DeletePointers(true),
44 m_HashHasChangedFlag(false),
45 m_MemberName(MemberName),
47 m_pLexicon((Mini != 0) ? Mini->GetLexicon() : 0),
50 m_pTerminalNodeArray(),
52 m_SortValidFlag(false),
54 m_TotalUseCount(0.0) { }
57 TCollection<T>::TCollection( CLexicon* Lex, QString MemberName )
60 m_DeletePointers = TRUE;
61 m_DeletionArray = NULL;
62 m_HashHasChangedFlag = FALSE;
63 m_MemberName = MemberName;
64 m_PointerArray = NULL;
67 m_pReverseTrie = NULL;
68 m_pTerminalNodeArray = NULL;
78 TCollection<T>::~TCollection()
80 if ( m_PointerArray ) { delete [] m_PointerArray; m_PointerArray = NULL; }
81 if ( m_SortArray ) { delete [] m_SortArray; m_SortArray = NULL; }
82 if ( m_DeletionArray) { delete [] m_DeletionArray; m_DeletionArray = NULL; }
83 if ( m_pTerminalNodeArray ) { delete [] m_pTerminalNodeArray; m_pTerminalNodeArray = NULL; }
84 if ( m_pReverseTrie ) { delete m_pReverseTrie; m_pReverseTrie = NULL; }
88 T* TCollection<T>::operator<<( CParse* pParse )
89 // Good for everything except CSignatures, which need punctuation.
94 CStringSurrogate cssKey;
96 cssKey = pParse->GetKey();
97 pTerminal = Insert (cssKey, &Result);
99 std::auto_ptr<T> morpheme(new T(pParse));
100 morpheme->SetIndex(GetCount() - 1);
101 pTerminal->SetPointer(morpheme.release());
103 T* pT = static_cast<T*>(pTerminal->Get_T_Pointer());
105 // Add to the reverse trie also
108 cssKey.SetBackwards( TRUE );
109 pTerminal = m_pReverseTrie->Insert( cssKey, &Result );
112 pTerminal->SetPointer(pT);
116 IncrementCorpusCount(1);
117 pT->IncrementCorpusCount(1);
119 m_SortValidFlag = FALSE;
120 m_HashHasChangedFlag = TRUE;
128 T* TCollection<T>::operator<< (CStringSurrogate SS)
130 QChar* szWord = new QChar[SS.GetLength()];
134 LxStrCpy( &SS, szWord, SS.GetLength() );
136 pTerminal = Insert ( CStringSurrogate(szWord,0,SS.GetLength()), &Result );
139 std::auto_ptr<T> morpheme(new T(SS, m_pMiniLex));
140 morpheme->SetIndex(GetCount() - 1);
141 pTerminal->SetPointer(morpheme.release());
143 T* pT = static_cast<T*>(pTerminal->Get_T_Pointer());
145 // Add to the reverse trie also
148 pTerminal = m_pReverseTrie->Insert( CStringSurrogate(szWord,0,SS.GetLength(),TRUE), &Result );
151 pTerminal->SetPointer(pT);
155 IncrementCorpusCount(1);
156 pT->IncrementCorpusCount(1);
158 m_SortValidFlag = FALSE;
159 m_HashHasChangedFlag = TRUE;
168 T* TCollection<T>::operator<< (QString szWord)
173 if ( szWord.isEmpty() ) { return NULL; }
175 pKey = new QChar [ szWord.length()];
176 LxStrCpy( szWord, pKey, szWord.length() );
178 pTerminal = Insert (CStringSurrogate( pKey, 0, szWord.length() ), &Result);
181 std::auto_ptr<T> morpheme(new T(szWord, m_pMiniLex));
182 morpheme->SetIndex(GetCount() - 1);
183 pTerminal->SetPointer(morpheme.release());
185 T* pT = static_cast<T*>(pTerminal->Get_T_Pointer());
187 // Add to the reverse trie also
190 pTerminal = m_pReverseTrie->Insert(CStringSurrogate( pKey, 0, szWord.length() ), &Result);
193 pTerminal->SetPointer(pT);
197 IncrementCorpusCount(1);
198 pT->IncrementCorpusCount(1);
200 m_SortValidFlag = FALSE;
201 m_HashHasChangedFlag = TRUE;
202 if (m_ReverseFlag) { delete pKey; }
208 template<class T> T* TCollection<T>::operator^=(CParse* string)
210 if (string->GetKeyLength() < 1)
212 Q_ASSERT(string->Size() >= 1);
213 if (CNode* node = Find1(m_ReverseFlag ?
214 CStringSurrogate(string->GetReverse()) :
215 CStringSurrogate(string)))
216 return static_cast<T*>(node->Get_T_Pointer());
220 template<class T> T* TCollection<T>::operator^=(CStringSurrogate string)
222 if (string.GetLength() < 1)
224 if (CNode* node = Find1(string))
225 return static_cast<T*>(node->Get_T_Pointer());
229 template<class T> T* TCollection<T>::operator^=(QString string)
231 if (string.isEmpty())
233 if (CNode* node = Find1(CStringSurrogate(string)))
234 return static_cast<T*>(node->Get_T_Pointer());
240 T* TCollection<T>::operator[] ( uint n)
242 if ( m_PointerArray == NULL || m_HashHasChangedFlag == TRUE )
244 CreatePointerArray();
247 Q_ASSERT(GetCount() >= 0);
248 Q_ASSERT(n < static_cast<unsigned int>(GetCount()));
250 return m_PointerArray[n];
255 void TCollection<T>::AddPointer(T* pT)
258 CStringSurrogate ssWord = pT->GetKey();
260 pTerminal = CTrie::operator<< (ssWord);
261 pTerminal->SetPointer ( pT );
263 // Add to the reverse trie also
266 ssWord.SetBackwards(TRUE);
267 pTerminal = m_pReverseTrie->Insert( ssWord );
268 pTerminal->SetPointer(pT);
271 pT->SetIndex ( GetCount() - 1);
272 IncrementCorpusCount(pT->GetCorpusCount());
277 T* TCollection<T>::AddToCollection (const CParse& Word)
279 m_HashHasChangedFlag = TRUE;
287 T* TCollection<T>::AddToCollection (const CStringSurrogate& Word)
290 m_HashHasChangedFlag = TRUE;
297 bool TCollection<T>::Contains ( CStringSurrogate& string )
299 T* pT = *this ^= string;
312 void TCollection<T>::CreatePointerArray()
314 Q_ASSERT(m_DeletionArray == 0);
315 const int size = GetCount();
317 delete[] m_PointerArray;
318 m_PointerArray = new T*[size];
319 delete[] m_pTerminalNodeArray;
320 m_pTerminalNodeArray = new CNode*[size];
322 MakeATerminalPointerArray(m_pTerminalNodeArray);
324 for (int i = 0; i < size; ++i) {
325 m_PointerArray[i] = static_cast<T*>(
326 m_pTerminalNodeArray[i]->Get_T_Pointer());
327 m_PointerArray[i]->SetIndex(i);
330 m_HashHasChangedFlag = false;
335 void TCollection<T>::CreateReverseTrie()
338 CStringSurrogate ssKey;
340 if( m_pReverseTrie ) delete m_pReverseTrie;
341 m_pReverseTrie = new CTrie( TRUE );
342 m_pReverseTrie->SetAutoDelete( FALSE );
344 // Build the reverse trie from the
346 for (int i = 0; i < GetCount(); ++i)
347 if (T* pT = GetAt(i)) {
348 ssKey = pT->GetKey();
349 ssKey.SetBackwards();
350 pTerminal = m_pReverseTrie->Insert(ssKey);
351 pTerminal->SetPointer(pT);
357 void TCollection<T>::DeleteMarkedMembers()
359 // we want to do this without tripping the "trie has changed" flag along the way.
361 if ( m_DeletionArray == NULL ) { return; }
363 int count = GetCount();
364 for (int i = 0; i < count; i++)
366 if ( m_DeletionArray[i] == 1 )
368 CStringSurrogate key = m_PointerArray[i]->GetKey();
369 Q_ASSERT( RemoveFromTrie( key ) );
373 Q_ASSERT( "void TCollection<T>::DeleteMarkedMembers() : Type existed in forward trie but not reverse trie." && m_pReverseTrie->RemoveFromTrie( key ) );
378 // m_LockState = Unlocked;
379 m_HashHasChangedFlag = TRUE;
380 delete m_DeletionArray;
381 m_DeletionArray = NULL;
386 void TCollection<T>::DoNotDeletePointers()
388 m_DeletePointers = FALSE;
393 void TCollection<T>::DumpVisibleToLogFile()
395 if (! m_pLexicon->GetLogFileStream()) { return; }
396 GetRoot1()->DumpVisibleToLogFile( m_pLexicon->GetLogFileStream(), m_ReverseFlag );
401 void TCollection<T>::DumpVisibleWords(CWordCollection* Words)
403 GetRoot1()->DumpVisibleWords(Words, FALSE);
408 void TCollection<T>::Empty()
411 if( m_pReverseTrie ) m_pReverseTrie->ResetToEmpty();
412 if (m_SortArray) { delete [] m_SortArray; m_SortArray = NULL; }
413 if (m_PointerArray) { delete [] m_PointerArray; m_PointerArray = NULL; }
416 m_SortValidFlag = FALSE;
417 m_HashHasChangedFlag = TRUE;
422 CStringSurrogate TCollection<T>::FindMaximalMajorityPrefix()
424 CNode* pNode = FindLowestMajorityNode();
426 if (pNode) return pNode->GetKey();
427 else return CStringSurrogate();
432 void TCollection<T>::FindMemberExtensions (int SmallestStemSize, TCollection<CLParse>& Output )
436 CLParse* pChain = NULL;
440 for (int i = 0; i < GetCount(); i++)
442 pMorpheme = GetAtSort(i);
443 if (pMorpheme->GetLength() < SmallestStemSize + 1) { continue; }
444 pChain = SeekChain (pMorpheme, pChain, SmallestStemSize);
447 Output.AddPointer (pChain);
448 m_pLexicon->AddToScreen ( pChain->Display() );
456 CStringSurrogate TCollection<T>::FSAGetAtSS(uint n)
458 Q_ASSERT ( n < (uint) GetCount() );
460 if ( m_PointerArray == NULL || m_HashHasChangedFlag == TRUE )
462 CreatePointerArray();
466 return m_pTerminalNodeArray[n]->GetKey();
471 T* TCollection<T>::GetAt ( uint n )
473 Q_ASSERT(GetCount() >= 0);
474 Q_ASSERT(n < static_cast<unsigned int>(GetCount()));
476 if ( m_PointerArray == NULL || m_HashHasChangedFlag == TRUE )
478 CreatePointerArray();
482 return m_PointerArray[n];
487 CStringSurrogate TCollection<T>::GetAt_SS( int n )
489 CStringSurrogate Return ( GetAt(n)->GetKeyPointer(), 0, GetAt(n)->GetKeyLength() );
496 T* TCollection<T>::GetAtSort( int n )
498 if( !m_SortArray || !m_SortValidFlag ) Sort( m_SortStyle );
499 return m_SortArray[n];
503 template<class T> CStringSurrogate TCollection<T>::GetAtSort_SS(int n)
505 if (m_SortArray == 0)
507 return CStringSurrogate(m_SortArray[n]->GetKeyPointer(),
508 m_SortArray[n]->GetKeyLength());
511 template<class T> int TCollection<T>::GetCorpusCount() const
512 { return m_CorpusCount; }
515 CLexicon* TCollection<T>::GetLexicon()
522 CMiniLexicon* TCollection<T>::GetMiniLexicon()
529 CTrie* TCollection<T>::GetReverseTrie()
531 if( !m_pReverseTrie ) CreateReverseTrie();
532 return m_pReverseTrie;
537 int TCollection<T>::GetSortValidFlag()
539 return m_SortValidFlag;
544 CTrie* TCollection<T>::GetTrie()
550 int TCollection<T>::GetWidth( CStringSurrogate string )// i.e., from Trie
552 CNode *pNode = Find1 ( string );
553 return pNode->GetWidth();
558 void TCollection<T>::IncrementCorpusCount( int n )
565 void TCollection<T>::MarkForDeletion(int n)
567 if ( m_DeletionArray == NULL)
569 m_DeletionArray = new int [ GetCount() ];
570 for (int i = 0; i < GetCount(); i++)
572 m_DeletionArray[i] = 0;
575 m_DeletionArray [n] = 1;
580 void TCollection<T>::RecomputeCorpusCount()
583 for (int i = 0; i < GetCount(); i++)
585 m_CorpusCount += GetAt(i)->GetCorpusCount();
592 bool TCollection<T>::Remove (T* pT) // this doesn't delete pT
593 { bool bResult (FALSE);
594 QString display = pT->Display();
595 CStringSurrogate ssDisplay( display );
597 bResult = RemoveFromTrie ( ssDisplay, FALSE );
601 m_SortValidFlag = FALSE;
602 m_HashHasChangedFlag = TRUE;
604 if( m_pReverseTrie ) {
605 ssDisplay.SetBackwards();
606 Q_ASSERT( "bool TCollection<T>::Remove (T* pT) : Type existed in forward trie but not reverse trie." && m_pReverseTrie->RemoveFromTrie( ssDisplay ) );
618 void TCollection<T>::RemoveAll()
620 if ( m_SortArray ) { delete [] m_SortArray; m_SortArray = NULL; }
621 if ( m_PointerArray ) { delete [] m_PointerArray; m_PointerArray = NULL; }
624 if( m_pReverseTrie ) m_pReverseTrie->ResetToEmpty();
629 bool TCollection<T>::RemoveMember ( T* pT ) // this deletes pT
631 bool ReturnValue = Remove (pT);
637 bool TCollection<T>::RemoveMember ( const CStringSurrogate& ssT ) // this deletes pT
641 bResult = RemoveFromTrie ( ssT );
645 m_SortValidFlag = FALSE;
646 m_HashHasChangedFlag = TRUE;
649 // XXX. remove from reverse trie
661 bool TCollection<T>::RemoveMember ( const CStringSurrogate& ssT, bool DeleteFlag ) // this can choose whether or not to delete pT
666 bResult = RemoveFromTrie ( ssT, DeleteFlag );
670 m_SortValidFlag = FALSE;
671 m_HashHasChangedFlag = TRUE;
674 CStringSurrogate ss_copy = ssT;
675 ss_copy.SetBackwards();
676 if (!m_pReverseTrie->RemoveFromTrie(ss_copy))
677 Q_ASSERT( !"bool TCollection<T>::RemoveMember ( CStringSurrogate& ssT ) : Type existed in forward trie but not reverse trie." );
678 ss_copy.SetBackwards(false);
692 void TCollection<T>::SetKey( T* pT, CParse& NewKey )
695 CStringSurrogate ssKey = pT->GetKey();
697 RemoveFromTrie ( ssKey, FALSE);
698 pTerminal = Insert ( NewKey.GetKey() );
699 pTerminal->SetPointer (pT);
703 ssKey.SetBackwards();
704 Q_ASSERT( m_pReverseTrie->RemoveFromTrie( ssKey ) && "bool TCollection<T>::RemoveMember ( CStringSurrogate& ssT ) : Type existed in forward trie but not reverse trie." );
705 ssKey = NewKey.GetKey();
706 ssKey.SetBackwards();
707 pTerminal = m_pReverseTrie->Insert( ssKey );
708 pTerminal->SetPointer(pT);
714 void TCollection<T>::SetKey( T* pT, QString NewKey )
717 CStringSurrogate ssKey = pT->GetKey();
719 RemoveFromTrie ( ssKey );
720 pTerminal = Insert ( NewKey );
721 pTerminal->SetPointer (pT);
725 ssKey.SetBackwards();
726 Q_ASSERT( m_pReverseTrie->RemoveFromTrie( ssKey ) && "bool TCollection<T>::RemoveMember ( CStringSurrogate& ssT ) : Type existed in forward trie but not reverse trie." );
728 ssKey.SetBackwards();
729 pTerminal = m_pReverseTrie->Insert( ssKey );
730 pTerminal->SetPointer(pT);
736 void TCollection<T>::SetSortStyle(eSortStyle SS)
743 void TCollection<T>::SetSortValidFlag(bool value)
745 m_SortValidFlag = value;
750 void TCollection<T>::Sort( eSortStyle SS )
754 if (m_SortStyle == SS && m_SortValidFlag == TRUE)
757 int Size = GetCount();
759 if (m_SortArray) { delete [] m_SortArray; }
760 m_SortArray = new T*[ Size ];
762 for (i = 0; i < Size; i++)
764 m_SortArray[i] = GetAt( i );
771 qsort(m_SortArray, Size, sizeof(T*), CompareAlphabetically);
777 qsort(m_SortArray, Size, sizeof(T*), CompareCorpusCount);
782 case MORPHEME_GOODNESS:
784 qsort(m_SortArray, Size, sizeof(T*), CompareSortingQuantity);
789 qsort(m_SortArray, Size, sizeof(T*), CompareSortingQuantity);
794 qsort(m_SortArray, Size, sizeof(T*), CompareSize);
797 case SIGS_NUMBER_OF_STEMS:
799 qsort(m_SortArray, Size, sizeof(T*), CompareNumberOfStems);
804 qsort(m_SortArray, Size, sizeof(T*), CompareSortingString);
809 qsort(m_SortArray, Size, sizeof(T*), CompareReverseAlphabetically);
814 qsort(m_SortArray, Size, sizeof(T*), CompareLength);
819 qsort(m_SortArray, Size, sizeof(T*), CompareDLSavings);
824 qsort(m_SortArray, Size, sizeof(T*), CompareFrequency);
829 qsort(m_SortArray, Size, sizeof(T*), CompareMorphemeCount);
834 qsort(m_SortArray, Size, sizeof(T*), CompareUseCount);
839 qsort(m_SortArray, Size, sizeof(T*), CompareSigRemark);
844 qsort(m_SortArray, Size, sizeof(T*), CompareStemSource);
851 for ( i = 0; i < Size; i++)
853 m_SortArray[i]->SetSortIndex(i);
857 m_SortValidFlag = TRUE;
862 void TCollection<T>::T_PredecessorFrequency(
863 enum eSuccessorFrequencyMode PFM,
864 CStemCollection* Stems, CPrefixCollection* Prefixes,
865 int MaxNeighborPredecessorCount,
866 int MaximumPrefixLength,
867 int MinimumStemLength,
868 int RightMargin, int LeftMargin)
871 CStringSurrogate ssStem, ssWord, ssPrefix;
872 int WordLength, nRightMargin, nLeftMargin, j;
878 linguistica::ui::status_user_agent& status = m_pLexicon->status_display();
880 status.progress.clear();
881 status.progress.set_denominator(GetCount());
882 for (int i = 0; i < GetCount(); i++) {
887 ssWord.SetBackwards();
888 WordLength = pWord->GetKeyLength();
890 if (pWord->MayBeParsed() == FALSE)
891 // if it's a compound, or parsed on the PF1 pass through this function.
895 nLeftMargin = WordLength - LeftMargin;
897 nLeftMargin = WordLength - 1;
899 if (RightMargin >= 0)
900 nRightMargin = RightMargin;
902 nRightMargin = nLeftMargin - MaximumPrefixLength;
904 CNode* pNode = m_pReverseTrie->GetRoot1();
906 int* SFArray = new int[WordLength + 2];
908 // This array keeps track in position i of
909 // how many alternatives to character i there are ( + 1) in the data.
910 for (j = 0; j < WordLength + 2; j++)
914 while (loc <= WordLength) {
915 for (; loc < pNode->m_BreakPoint && loc < WordLength; loc++) {
917 Q_ASSERT (loc < WordLength);
919 if (loc == WordLength) {
920 SFArray[loc] = pNode->GetWidth();
923 SFArray[loc] = pNode->GetWidth();
924 qNode = pNode->FindLetter(ssWord[loc]);
926 Q_ASSERT(pNode->GetKey() == ssWord);
927 for ( ; loc <= WordLength; loc++)
936 for (int w = nLeftMargin; w > nRightMargin && w >= MinimumStemLength ; w--) {
938 if (SFArray[w-1] <= MaxNeighborPredecessorCount &&
940 SFArray[w+1] <= MaxNeighborPredecessorCount) {
941 // use only the smallest prefix --
942 // this is wrong in some cases
943 // (assist-an-t, assist-an-ce, for example).
945 pWord->CutRightBeforeHere(WordLength - w);
950 // Here the condition is that the first piece must already exist in Stems,
951 // and the second piece must already exist in Prefixes;
952 // and the "peak" need only be not LOWER than the neighbors are.
954 if (SFArray[w-1] <= SFArray[w] &&
956 SFArray[w+1] <= SFArray[w]) {
957 ssStem = ssWord.Left(w - 1);
958 if (!(*Stems ^= ssStem))
960 ssPrefix = ssWord.Right(ssWord.GetLength() - w);
961 // ssPrefix = ssPrefix.Left(ssPrefix.GetLength()1);
962 if (!(*Prefixes ^= ssPrefix))
964 // use only the smallest prefix --
965 // this is wrong in some cases
966 // (assist-an-t, assist-an-ce, for example).
968 pWord->CutRightBeforeHere(w);
975 status.progress.clear();
979 void TCollection<T>::T_SuccessorFrequency(
980 enum eSuccessorFrequencyMode SFM,
981 CStemCollection* Stems, CSuffixCollection* Suffixes,
982 int MaxNeighborSuccessorCount,
983 int MaximumSuffixLength,
984 int MinimumStemLength,
985 int LeftMargin, int RightMargin)
988 CStringSurrogate ssStem, ssWord, ssSuffix;
989 int WordLength, nLeftMargin, nRightMargin, j;
992 linguistica::ui::status_user_agent& status = m_pLexicon->status_display();
993 status.progress.clear();
994 status.progress.set_denominator(GetCount());
995 for (int i = 0; i < GetCount(); i++) {
1000 WordLength = pWord->GetKeyLength();
1002 if (pWord->MayBeParsed() == FALSE)
1003 // if it's a compound, or
1004 // parsed on the SF1 pass through this function.
1007 if (RightMargin >= 0)
1008 nRightMargin = WordLength - RightMargin;
1010 nRightMargin = WordLength - 1;
1012 if (LeftMargin >= 0)
1013 nLeftMargin = LeftMargin;
1015 nLeftMargin = nRightMargin - MaximumSuffixLength;
1017 CNode* pNode = GetRoot1();
1019 int* SFArray = new int[WordLength + 2];
1021 // This array keeps track in position i of
1022 // how many alternatives to character i there are ( + 1) in the data.
1023 for (j = 0; j < WordLength + 2; j++)
1027 while (loc <= WordLength) {
1028 for (; loc < pNode->m_BreakPoint && loc < WordLength; loc++) {
1030 Q_ASSERT (loc < WordLength);
1032 if (loc == WordLength) {
1033 SFArray[loc] = pNode->GetWidth();
1036 SFArray[loc] = pNode->GetWidth();
1037 qNode = pNode->FindLetter(ssWord[loc]);
1038 if (qNode == NULL) {
1039 Q_ASSERT(pNode->GetKey() == ssWord);
1040 for ( ; loc <= WordLength; loc++)
1049 for (int w = nRightMargin ; w > 1 && w > nLeftMargin && w >= MinimumStemLength ; w--) {
1051 if (SFArray[w-1] <= MaxNeighborSuccessorCount &&
1053 SFArray[w+1] <= MaxNeighborSuccessorCount) {
1054 // use only the smallest suffix --
1055 // this is wrong in some cases
1056 // (assist-an-t, assist-an-ce, for example).
1057 pWord->CutRightBeforeHere(w);
1058 m_pLexicon->UpdateWord(pWord);
1063 // Here the condition is that the first piece must already exist in Stems,
1064 // and the second piece must already exist in Suffixes;
1065 // and the "peak" need only be not LOWER than the neighbors are.
1067 if (SFArray[w-1] <= SFArray[w] &&
1069 SFArray[w+1] <= SFArray[w]) {
1070 ssStem = ssWord.Left(w - 1);
1071 if (!(*Stems ^= ssStem))
1073 ssSuffix = ssWord.Right(ssWord.GetLength() - w);
1074 // ssSuffix = ssSuffix.Left(ssSuffix.GetLength() 1);
1075 if (!(*Suffixes ^= ssSuffix))
1078 // use only the smallest suffix --
1079 // this is wrong in some cases
1080 // (assist-an-t, assist-an-ce, for example).
1081 pWord->CutRightBeforeHere(w);
1082 m_pLexicon->UpdateWord(pWord);
1089 status.progress.clear();
1092 #endif // COLLECTIONTEMPLATE_TCC