1*449ab281SAndrew Rist /**************************************************************
2*449ab281SAndrew Rist *
3*449ab281SAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one
4*449ab281SAndrew Rist * or more contributor license agreements. See the NOTICE file
5*449ab281SAndrew Rist * distributed with this work for additional information
6*449ab281SAndrew Rist * regarding copyright ownership. The ASF licenses this file
7*449ab281SAndrew Rist * to you under the Apache License, Version 2.0 (the
8*449ab281SAndrew Rist * "License"); you may not use this file except in compliance
9*449ab281SAndrew Rist * with the License. You may obtain a copy of the License at
10*449ab281SAndrew Rist *
11*449ab281SAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0
12cdf0e10cSrcweir *
13*449ab281SAndrew Rist * Unless required by applicable law or agreed to in writing,
14*449ab281SAndrew Rist * software distributed under the License is distributed on an
15*449ab281SAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*449ab281SAndrew Rist * KIND, either express or implied. See the License for the
17*449ab281SAndrew Rist * specific language governing permissions and limitations
18*449ab281SAndrew Rist * under the License.
19*449ab281SAndrew Rist *
20*449ab281SAndrew Rist *************************************************************/
21*449ab281SAndrew Rist
22*449ab281SAndrew Rist
23cdf0e10cSrcweir
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_i18npool.hxx"
26cdf0e10cSrcweir /*************************************************************************
27cdf0e10cSrcweir
28cdf0e10cSrcweir Weighted Levenshtein Distance
29cdf0e10cSrcweir including wildcards
30cdf0e10cSrcweir '*' for any number (0 or more) of arbitrary characters
31cdf0e10cSrcweir '?' for exactly one arbitrary character
32cdf0e10cSrcweir escapeable with backslash, "\*" or "\?"
33cdf0e10cSrcweir
34cdf0e10cSrcweir Return:
35cdf0e10cSrcweir WLD if WLD <= nLimit, else nLimit+1
36cdf0e10cSrcweir
37cdf0e10cSrcweir or, if bSplitCount:
38cdf0e10cSrcweir WLD if WLD <= nLimit
39cdf0e10cSrcweir -WLD if Replace and Insert and Delete <= nLimit
40cdf0e10cSrcweir else nLimit+1
41cdf0e10cSrcweir
42cdf0e10cSrcweir Recursive definition of WLD:
43cdf0e10cSrcweir
44cdf0e10cSrcweir WLD( X(i), Y(j) ) = min( WLD( X(i-1), Y(j-1) ) + p(i,j) ,
45cdf0e10cSrcweir WLD( X(i) , Y(j-1) ) + q ,
46cdf0e10cSrcweir WLD( X(i-1), Y(j) ) + r )
47cdf0e10cSrcweir
48cdf0e10cSrcweir X(i) := the first i characters of the word X
49cdf0e10cSrcweir Y(j) := the first j characters of the word Y
50cdf0e10cSrcweir p(i,j) := 0 if i-th character of X == j-th character of Y,
51cdf0e10cSrcweir p else
52cdf0e10cSrcweir
53cdf0e10cSrcweir Boundary conditions:
54cdf0e10cSrcweir WLD( X(0), Y(j) ) := j*q (Y created by j inserts)
55cdf0e10cSrcweir WLD( X(i), Y(0) ) := i*r (Y created by i deletes)
56cdf0e10cSrcweir WLD( X(0), Y(0) ) := 0
57cdf0e10cSrcweir
58cdf0e10cSrcweir Instead of recursions a dynamic algorithm is used.
59cdf0e10cSrcweir
60cdf0e10cSrcweir See also: German computer magazine
61cdf0e10cSrcweir c't 07/89 pages 192-208 and c't 03/94 pages 230-239
62cdf0e10cSrcweir
63cdf0e10cSrcweir *************************************************************************/
64cdf0e10cSrcweir
65cdf0e10cSrcweir
66cdf0e10cSrcweir #include <string.h> // strlen()
67cdf0e10cSrcweir
68cdf0e10cSrcweir #if defined( _MSC_VER )
69cdf0e10cSrcweir #pragma warning(once: 4068)
70cdf0e10cSrcweir #endif
71cdf0e10cSrcweir
72cdf0e10cSrcweir #include "levdis.hxx"
73cdf0e10cSrcweir
74cdf0e10cSrcweir
75cdf0e10cSrcweir #ifdef erTEST
76cdf0e10cSrcweir #include <stdlib.h>
77cdf0e10cSrcweir #include <stdio.h>
78cdf0e10cSrcweir #include <iostream.h>
79cdf0e10cSrcweir #endif
80cdf0e10cSrcweir
81cdf0e10cSrcweir #ifdef SOLARIS
82cdf0e10cSrcweir #undef min
83cdf0e10cSrcweir #endif
84cdf0e10cSrcweir
85cdf0e10cSrcweir #define LEVDISBIG (nLimit + 1) // Returnwert wenn Distanz > nLimit
86cdf0e10cSrcweir #define LEVDISDOUBLEBUF 2048 // dadrueber wird nicht mehr gedoppelt
87cdf0e10cSrcweir
88cdf0e10cSrcweir // Balance, aus Geschwindigkeitsgruenden ist dieses keine Funktion
89cdf0e10cSrcweir // c == cpPattern[jj] == cString[ii]
90cdf0e10cSrcweir // erst wird bis Fundstelle gesucht, wenn dort die Balance gleich ist, wird
91cdf0e10cSrcweir // auch nach der Fundstelle verglichen
92cdf0e10cSrcweir #define LEVDISBALANCE(jj,ii) \
93cdf0e10cSrcweir { \
94cdf0e10cSrcweir if ( jj != ii ) \
95cdf0e10cSrcweir { \
96cdf0e10cSrcweir register sal_Int32 k; \
97cdf0e10cSrcweir if ( jj > 0 ) \
98cdf0e10cSrcweir for ( k=0; k < jj; k++ ) \
99cdf0e10cSrcweir if ( cpPattern[k] == c ) \
100cdf0e10cSrcweir nBalance++; \
101cdf0e10cSrcweir if ( ii > 0 ) \
102cdf0e10cSrcweir for ( k=0; k < ii; k++ ) \
103cdf0e10cSrcweir if ( cString[k] == c ) \
104cdf0e10cSrcweir nBalance--; \
105cdf0e10cSrcweir if ( !nBalance ) \
106cdf0e10cSrcweir { \
107cdf0e10cSrcweir for ( k=jj+1; k < nPatternLen; k++ ) \
108cdf0e10cSrcweir if ( cpPattern[k] == c ) \
109cdf0e10cSrcweir nBalance++; \
110cdf0e10cSrcweir for ( k=ii+1; k < nStringLen; k++ ) \
111cdf0e10cSrcweir if ( cString[k] == c ) \
112cdf0e10cSrcweir nBalance--; \
113cdf0e10cSrcweir } \
114cdf0e10cSrcweir } \
115cdf0e10cSrcweir }
116cdf0e10cSrcweir
Impl_WLD_StringLen(const sal_Unicode * pStr)117cdf0e10cSrcweir static sal_Int32 Impl_WLD_StringLen( const sal_Unicode* pStr )
118cdf0e10cSrcweir {
119cdf0e10cSrcweir const sal_Unicode* pTempStr = pStr;
120cdf0e10cSrcweir while( *pTempStr )
121cdf0e10cSrcweir pTempStr++;
122cdf0e10cSrcweir return (sal_Int32)(pTempStr-pStr);
123cdf0e10cSrcweir }
124cdf0e10cSrcweir
125cdf0e10cSrcweir #ifdef erTESTMAT
126cdf0e10cSrcweir #define erTESTMATMAX 180
127cdf0e10cSrcweir static int npMatrix[erTESTMATMAX][erTESTMATMAX]; // nearly 64K
128cdf0e10cSrcweir #endif
129cdf0e10cSrcweir
130cdf0e10cSrcweir // Distanz von String zu Pattern
WLD(const sal_Unicode * cString,sal_Int32 nStringLen)131cdf0e10cSrcweir int WLevDistance::WLD( const sal_Unicode* cString, sal_Int32 nStringLen )
132cdf0e10cSrcweir {
133cdf0e10cSrcweir int nSPMin = 0; // StrafPunkteMinimum
134cdf0e10cSrcweir int nRepS = 0; // fuer SplitCount
135cdf0e10cSrcweir
136cdf0e10cSrcweir #ifdef erTESTMAT
137cdf0e10cSrcweir {
138cdf0e10cSrcweir for ( sal_Int32 r=0; r<=nStringLen && r < erTESTMATMAX; r++ )
139cdf0e10cSrcweir for ( sal_Int32 c=0; c<=nPatternLen && c < erTESTMATMAX; c++ )
140cdf0e10cSrcweir npMatrix[r][c] = 99; // Matrix initialisieren, nur visuell
141cdf0e10cSrcweir }
142cdf0e10cSrcweir #endif
143cdf0e10cSrcweir
144cdf0e10cSrcweir // Laengendifferenz von Pattern und String
145cdf0e10cSrcweir int nLenDiff = nPatternLen - nStars - nStringLen;
146cdf0e10cSrcweir // mehr Einfuegungen oder Loeschungen noetig als Limit? => raus hier
147cdf0e10cSrcweir if ( (nLenDiff * nInsQ0 > nLimit)
148cdf0e10cSrcweir || ((nStars == 0) && (nLenDiff * nDelR0 < -nLimit)) )
149cdf0e10cSrcweir return(LEVDISBIG);
150cdf0e10cSrcweir
151cdf0e10cSrcweir // wenn der zu vergleichende String groesser ist als das bisherige Array
152cdf0e10cSrcweir // muss dieses angepasst werden
153cdf0e10cSrcweir if ( nStringLen >= nArrayLen )
154cdf0e10cSrcweir {
155cdf0e10cSrcweir // gib ihm moeglichst mehr, damit nicht gleich naechstesmal
156cdf0e10cSrcweir // wieder realloziert werden muss
157cdf0e10cSrcweir if ( nStringLen < LEVDISDOUBLEBUF )
158cdf0e10cSrcweir nArrayLen = 2 * nStringLen;
159cdf0e10cSrcweir else
160cdf0e10cSrcweir nArrayLen = nStringLen + 1;
161cdf0e10cSrcweir npDistance = aDisMem.NewMem( nArrayLen );
162cdf0e10cSrcweir #ifdef erTEST
163cdf0e10cSrcweir if ( !npDistance )
164cdf0e10cSrcweir {
165cdf0e10cSrcweir cerr << "DOOM! (Damned, Out Of Memory)" << endl;
166cdf0e10cSrcweir exit(1);
167cdf0e10cSrcweir }
168cdf0e10cSrcweir #endif
169cdf0e10cSrcweir }
170cdf0e10cSrcweir
171cdf0e10cSrcweir // Anfangswerte der zweiten Spalte (erstes Pattern-Zeichen) berechnen
172cdf0e10cSrcweir // die erste Spalte (0-Len Pattern) ist immer 0 .. nStringLen * nInsQ0,
173cdf0e10cSrcweir // deren Minimum also 0
174cdf0e10cSrcweir if ( nPatternLen == 0 )
175cdf0e10cSrcweir {
176cdf0e10cSrcweir // Anzahl der Loeschungen, um auf Pattern zu kommen
177cdf0e10cSrcweir for ( sal_Int32 i=0; i <= nStringLen; i++ )
178cdf0e10cSrcweir npDistance[i] = i * nDelR0;
179cdf0e10cSrcweir }
180cdf0e10cSrcweir else if ( cpPattern[0] == '*' && bpPatIsWild[0] )
181cdf0e10cSrcweir {
182cdf0e10cSrcweir // statt einem '*' ist alles einsetzbar
183cdf0e10cSrcweir for ( sal_Int32 i=0; i <= nStringLen; i++ )
184cdf0e10cSrcweir npDistance[i] = 0;
185cdf0e10cSrcweir }
186cdf0e10cSrcweir else
187cdf0e10cSrcweir {
188cdf0e10cSrcweir sal_Unicode c;
189cdf0e10cSrcweir int nP;
190cdf0e10cSrcweir c = cpPattern[0];
191cdf0e10cSrcweir if ( c == '?' && bpPatIsWild[0] )
192cdf0e10cSrcweir nP = 0; // ein '?' kann jedes Zeichen sein
193cdf0e10cSrcweir else
194cdf0e10cSrcweir // Minimum von Ersetzen und Loeschen+Einfuegen Gewichtung
195cdf0e10cSrcweir nP = Min3( nRepP0, nRepP0, nDelR0 + nInsQ0 );
196cdf0e10cSrcweir npDistance[0] = nInsQ0; // mit einfachem Einfuegen geht's los
197cdf0e10cSrcweir npDistance[1] = nInsQ0;
198cdf0e10cSrcweir npDistance[2] = nInsQ0;
199cdf0e10cSrcweir int nReplacePos = -1; // tristate Flag
200cdf0e10cSrcweir int nDelCnt = 0;
201cdf0e10cSrcweir for ( sal_Int32 i=1; i <= nStringLen; i++, nDelCnt += nDelR0 )
202cdf0e10cSrcweir {
203cdf0e10cSrcweir if ( cString[i-1] == c )
204cdf0e10cSrcweir nP = 0; // Replace ab dieser Stelle ist 0
205cdf0e10cSrcweir // Loeschungen um auf Pattern zu kommen + Replace
206cdf0e10cSrcweir npDistance[i] = nDelCnt + nP;
207cdf0e10cSrcweir if ( bSplitCount )
208cdf0e10cSrcweir {
209cdf0e10cSrcweir if ( nReplacePos < 0 && nP )
210cdf0e10cSrcweir { // diese Stelle wird ersetzt
211cdf0e10cSrcweir nRepS++;
212cdf0e10cSrcweir nReplacePos = i;
213cdf0e10cSrcweir #ifdef erTESTMAT
214cdf0e10cSrcweir npMatrix[i][1] = -npDistance[i];
215cdf0e10cSrcweir #endif
216cdf0e10cSrcweir }
217cdf0e10cSrcweir else if ( nReplacePos > 0 && !nP )
218cdf0e10cSrcweir {
219cdf0e10cSrcweir int nBalance = 0; // gleiche Anzahl c
220cdf0e10cSrcweir LEVDISBALANCE( 0, i-1 );
221cdf0e10cSrcweir if ( !nBalance )
222cdf0e10cSrcweir { // einer wurde ersetzt, der ein Insert war
223cdf0e10cSrcweir nRepS--;
224cdf0e10cSrcweir #ifdef erTESTMAT
225cdf0e10cSrcweir npMatrix[nReplacePos][1] = npDistance[nReplacePos];
226cdf0e10cSrcweir #endif
227cdf0e10cSrcweir nReplacePos = 0;
228cdf0e10cSrcweir }
229cdf0e10cSrcweir }
230cdf0e10cSrcweir }
231cdf0e10cSrcweir }
232cdf0e10cSrcweir nSPMin = Min3( npDistance[0], npDistance[1], npDistance[2] );
233cdf0e10cSrcweir }
234cdf0e10cSrcweir #ifdef erTESTMAT
235cdf0e10cSrcweir {
236cdf0e10cSrcweir for ( sal_Int32 r=0; r<=nStringLen && r < erTESTMATMAX; r++ )
237cdf0e10cSrcweir {
238cdf0e10cSrcweir npMatrix[r][0] = r * nInsQ0;
239cdf0e10cSrcweir if ( npMatrix[r][1] >= 0)
240cdf0e10cSrcweir npMatrix[r][1] = npDistance[r];
241cdf0e10cSrcweir }
242cdf0e10cSrcweir }
243cdf0e10cSrcweir #endif
244cdf0e10cSrcweir
245cdf0e10cSrcweir // Distanzmatrix berechnen
246cdf0e10cSrcweir sal_Int32 j = 0; // fuer alle Spalten des Pattern, solange nicht Limit
247cdf0e10cSrcweir while ( (j < nPatternLen-1)
248cdf0e10cSrcweir && nSPMin <= (bSplitCount ? 2 * nLimit : nLimit) )
249cdf0e10cSrcweir {
250cdf0e10cSrcweir sal_Unicode c;
251cdf0e10cSrcweir int nP, nQ, nR, nPij, d1, d2;
252cdf0e10cSrcweir
253cdf0e10cSrcweir j++;
254cdf0e10cSrcweir c = cpPattern[j];
255cdf0e10cSrcweir if ( bpPatIsWild[j] ) // '*' oder '?' nicht escaped
256cdf0e10cSrcweir nP = 0; // kann ohne Strafpunkte ersetzt werden
257cdf0e10cSrcweir else
258cdf0e10cSrcweir nP = nRepP0;
259cdf0e10cSrcweir if ( c == '*' && bpPatIsWild[j] )
260cdf0e10cSrcweir {
261cdf0e10cSrcweir nQ = 0; // Einfuegen und Loeschen ohne Strafpunkte
262cdf0e10cSrcweir nR = 0;
263cdf0e10cSrcweir }
264cdf0e10cSrcweir else
265cdf0e10cSrcweir {
266cdf0e10cSrcweir nQ = nInsQ0; // normale Gewichtung
267cdf0e10cSrcweir nR = nDelR0;
268cdf0e10cSrcweir }
269cdf0e10cSrcweir d2 = npDistance[0];
270cdf0e10cSrcweir // Anzahl Einfuegungen um von Null-String auf Pattern zu kommen erhoehen
271cdf0e10cSrcweir npDistance[0] = npDistance[0] + nQ;
272cdf0e10cSrcweir nSPMin = npDistance[0];
273cdf0e10cSrcweir int nReplacePos = -1; // tristate Flag
274cdf0e10cSrcweir // fuer jede Patternspalte den String durchgehen
275cdf0e10cSrcweir for ( register sal_Int32 i=1; i <= nStringLen; i++ )
276cdf0e10cSrcweir {
277cdf0e10cSrcweir d1 = d2; // WLD( X(i-1), Y(j-1) )
278cdf0e10cSrcweir d2 = npDistance[i]; // WLD( X(i) , Y(j-1) )
279cdf0e10cSrcweir if ( cString[i-1] == c )
280cdf0e10cSrcweir {
281cdf0e10cSrcweir nPij = 0; // p(i,j)
282cdf0e10cSrcweir if ( nReplacePos < 0 )
283cdf0e10cSrcweir {
284cdf0e10cSrcweir int nBalance = 0; // gleiche Anzahl c
285cdf0e10cSrcweir LEVDISBALANCE( j, i-1 );
286cdf0e10cSrcweir if ( !nBalance )
287cdf0e10cSrcweir nReplacePos = 0; // keine Ersetzung mehr
288cdf0e10cSrcweir }
289cdf0e10cSrcweir }
290cdf0e10cSrcweir else
291cdf0e10cSrcweir nPij = nP;
292cdf0e10cSrcweir // WLD( X(i), Y(j) ) = min( WLD( X(i-1), Y(j-1) ) + p(i,j) ,
293cdf0e10cSrcweir // WLD( X(i) , Y(j-1) ) + q ,
294cdf0e10cSrcweir // WLD( X(i-1), Y(j) ) + r )
295cdf0e10cSrcweir npDistance[i] = Min3( d1 + nPij, d2 + nQ, npDistance[i-1] + nR );
296cdf0e10cSrcweir if ( npDistance[i] < nSPMin )
297cdf0e10cSrcweir nSPMin = npDistance[i];
298cdf0e10cSrcweir if ( bSplitCount )
299cdf0e10cSrcweir {
300cdf0e10cSrcweir if ( nReplacePos < 0 && nPij && npDistance[i] == d1 + nPij )
301cdf0e10cSrcweir { // diese Stelle wird ersetzt
302cdf0e10cSrcweir nRepS++;
303cdf0e10cSrcweir nReplacePos = i;
304cdf0e10cSrcweir #ifdef erTESTMAT
305cdf0e10cSrcweir npMatrix[i][j+1] = -npDistance[i];
306cdf0e10cSrcweir #endif
307cdf0e10cSrcweir }
308cdf0e10cSrcweir else if ( nReplacePos > 0 && !nPij )
309cdf0e10cSrcweir { // Zeichen in String und Pattern gleich.
310cdf0e10cSrcweir // wenn ab hier die gleiche Anzahl dieses Zeichens
311cdf0e10cSrcweir // sowohl in Pattern als auch in String ist, und vor
312cdf0e10cSrcweir // dieser Stelle das Zeichen gleich oft vorkommt, war das
313cdf0e10cSrcweir // Replace keins. Buchstabendreher werden hier erfasst
314cdf0e10cSrcweir // und der ReplaceS zurueckgenommen, wodurch das doppelte
315cdf0e10cSrcweir // Limit zum Tragen kommt.
316cdf0e10cSrcweir int nBalance = 0; // gleiche Anzahl c
317cdf0e10cSrcweir LEVDISBALANCE( j, i-1 );
318cdf0e10cSrcweir if ( !nBalance )
319cdf0e10cSrcweir { // einer wurde ersetzt, der ein Insert war
320cdf0e10cSrcweir nRepS--;
321cdf0e10cSrcweir #ifdef erTESTMAT
322cdf0e10cSrcweir npMatrix[nReplacePos][j+1] = npDistance[nReplacePos];
323cdf0e10cSrcweir #endif
324cdf0e10cSrcweir nReplacePos = 0;
325cdf0e10cSrcweir }
326cdf0e10cSrcweir }
327cdf0e10cSrcweir }
328cdf0e10cSrcweir }
329cdf0e10cSrcweir #ifdef erTESTMAT
330cdf0e10cSrcweir {
331cdf0e10cSrcweir for ( sal_Int32 r=0; r<=nStringLen && r < erTESTMATMAX; r++ )
332cdf0e10cSrcweir if ( npMatrix[r][j+1] >= 0)
333cdf0e10cSrcweir npMatrix[r][j+1] = npDistance[r];
334cdf0e10cSrcweir }
335cdf0e10cSrcweir #endif
336cdf0e10cSrcweir }
337cdf0e10cSrcweir #ifdef erTESTSPLIT
338cdf0e10cSrcweir printf(" nRepS: %d ", nRepS );
339cdf0e10cSrcweir #endif
340cdf0e10cSrcweir if ( (nSPMin <= nLimit) && (npDistance[nStringLen] <= nLimit) )
341cdf0e10cSrcweir return(npDistance[nStringLen]);
342cdf0e10cSrcweir else
343cdf0e10cSrcweir {
344cdf0e10cSrcweir if ( bSplitCount )
345cdf0e10cSrcweir {
346cdf0e10cSrcweir if ( nRepS && nLenDiff > 0 )
347cdf0e10cSrcweir nRepS -= nLenDiff; // Inserts wurden mitgezaehlt
348cdf0e10cSrcweir #ifdef erTESTSPLIT
349cdf0e10cSrcweir printf(" nRepSdiff: %d ", nRepS );
350cdf0e10cSrcweir #endif
351cdf0e10cSrcweir if ( (nSPMin <= 2 * nLimit)
352cdf0e10cSrcweir && (npDistance[nStringLen] <= 2 * nLimit)
353cdf0e10cSrcweir && (nRepS * nRepP0 <= nLimit) )
354cdf0e10cSrcweir return( -npDistance[nStringLen] );
355cdf0e10cSrcweir return(LEVDISBIG);
356cdf0e10cSrcweir }
357cdf0e10cSrcweir return(LEVDISBIG);
358cdf0e10cSrcweir }
359cdf0e10cSrcweir }
360cdf0e10cSrcweir
361cdf0e10cSrcweir
362cdf0e10cSrcweir
363cdf0e10cSrcweir // Berechnung von nLimit, nReplP0, nInsQ0, nDelR0, bSplitCount
364cdf0e10cSrcweir // aus Userwerten nOtherX, nShorterY, nLongerZ, bRelaxed
CalcLPQR(int nX,int nY,int nZ,bool bRelaxed)365cdf0e10cSrcweir int WLevDistance::CalcLPQR( int nX, int nY, int nZ, bool bRelaxed )
366cdf0e10cSrcweir {
367cdf0e10cSrcweir int nMin, nMid, nMax;
368cdf0e10cSrcweir if ( nX < 0 ) nX = 0; // nur positive Werte
369cdf0e10cSrcweir if ( nY < 0 ) nY = 0;
370cdf0e10cSrcweir if ( nZ < 0 ) nZ = 0;
371cdf0e10cSrcweir if ( 0 == (nMin = Min3( nX, nY, nZ )) ) // mindestens einer 0
372cdf0e10cSrcweir {
373cdf0e10cSrcweir nMax = Max3( nX, nY, nZ ); // entweder 0 bei drei 0 oder Max
374cdf0e10cSrcweir if ( 0 == (nMid = Mid3( nX, nY, nZ )) ) // sogar zwei 0
375cdf0e10cSrcweir nLimit = nMax; // entweder 0 oder einziger >0
376cdf0e10cSrcweir else // einer 0
377cdf0e10cSrcweir nLimit = KGV( nMid, nMax );
378cdf0e10cSrcweir }
379cdf0e10cSrcweir else // alle drei nicht 0
380cdf0e10cSrcweir nLimit = KGV( KGV( nX, nY ), nZ );
381cdf0e10cSrcweir nRepP0 = ( nX ? nLimit / nX : nLimit + 1 );
382cdf0e10cSrcweir nInsQ0 = ( nY ? nLimit / nY : nLimit + 1 );
383cdf0e10cSrcweir nDelR0 = ( nZ ? nLimit / nZ : nLimit + 1 );
384cdf0e10cSrcweir bSplitCount = bRelaxed;
385cdf0e10cSrcweir return( nLimit );
386cdf0e10cSrcweir }
387cdf0e10cSrcweir
388cdf0e10cSrcweir
389cdf0e10cSrcweir
390cdf0e10cSrcweir // Groesster Gemeinsamer Teiler nach Euklid (Kettendivision)
391cdf0e10cSrcweir // Sonderfall: 0 und irgendwas geben 1
GGT(int a,int b)392cdf0e10cSrcweir int WLevDistance::GGT( int a, int b )
393cdf0e10cSrcweir {
394cdf0e10cSrcweir if ( !a || !b )
395cdf0e10cSrcweir return 1;
396cdf0e10cSrcweir if ( a < 0 ) a = -a;
397cdf0e10cSrcweir if ( b < 0 ) b = -b;
398cdf0e10cSrcweir do
399cdf0e10cSrcweir {
400cdf0e10cSrcweir if ( a > b )
401cdf0e10cSrcweir a -= int(a / b) * b;
402cdf0e10cSrcweir else
403cdf0e10cSrcweir b -= int(b / a) * a;
404cdf0e10cSrcweir } while ( a && b );
405cdf0e10cSrcweir return( a ? a : b);
406cdf0e10cSrcweir }
407cdf0e10cSrcweir
408cdf0e10cSrcweir
409cdf0e10cSrcweir
410cdf0e10cSrcweir // Kleinstes Gemeinsames Vielfaches: a * b / GGT(a,b)
KGV(int a,int b)411cdf0e10cSrcweir int WLevDistance::KGV( int a, int b )
412cdf0e10cSrcweir {
413cdf0e10cSrcweir if ( a > b ) // Ueberlauf unwahrscheinlicher machen
414cdf0e10cSrcweir return( (a / GGT(a,b)) * b );
415cdf0e10cSrcweir else
416cdf0e10cSrcweir return( (b / GGT(a,b)) * a );
417cdf0e10cSrcweir }
418cdf0e10cSrcweir
419cdf0e10cSrcweir
420cdf0e10cSrcweir // Minimum von drei Werten
Min3(int x,int y,int z)421cdf0e10cSrcweir inline int WLevDistance::Min3( int x, int y, int z )
422cdf0e10cSrcweir {
423cdf0e10cSrcweir if ( x < y )
424cdf0e10cSrcweir return( x < z ? x : z );
425cdf0e10cSrcweir else
426cdf0e10cSrcweir return( y < z ? y : z );
427cdf0e10cSrcweir }
428cdf0e10cSrcweir
429cdf0e10cSrcweir
430cdf0e10cSrcweir
431cdf0e10cSrcweir // mittlerer von drei Werten
Mid3(int x,int y,int z)432cdf0e10cSrcweir int WLevDistance::Mid3( int x, int y, int z )
433cdf0e10cSrcweir {
434cdf0e10cSrcweir int min = Min3(x,y,z);
435cdf0e10cSrcweir if ( x == min )
436cdf0e10cSrcweir return( y < z ? y : z);
437cdf0e10cSrcweir else if ( y == min )
438cdf0e10cSrcweir return( x < z ? x : z);
439cdf0e10cSrcweir else // z == min
440cdf0e10cSrcweir return( x < y ? x : y);
441cdf0e10cSrcweir }
442cdf0e10cSrcweir
443cdf0e10cSrcweir
444cdf0e10cSrcweir
445cdf0e10cSrcweir // Maximum von drei Werten
Max3(int x,int y,int z)446cdf0e10cSrcweir int WLevDistance::Max3( int x, int y, int z )
447cdf0e10cSrcweir {
448cdf0e10cSrcweir if ( x > y )
449cdf0e10cSrcweir return( x > z ? x : z );
450cdf0e10cSrcweir else
451cdf0e10cSrcweir return( y > z ? y : z );
452cdf0e10cSrcweir }
453cdf0e10cSrcweir
454cdf0e10cSrcweir
455cdf0e10cSrcweir
456cdf0e10cSrcweir // Daten aus CTor initialisieren
InitData(const sal_Unicode * cPattern)457cdf0e10cSrcweir void WLevDistance::InitData( const sal_Unicode* cPattern )
458cdf0e10cSrcweir {
459cdf0e10cSrcweir cpPattern = aPatMem.GetcPtr();
460cdf0e10cSrcweir bpPatIsWild = aPatMem.GetbPtr();
461cdf0e10cSrcweir npDistance = aDisMem.GetPtr();
462cdf0e10cSrcweir nStars = 0;
463cdf0e10cSrcweir const sal_Unicode* cp1 = cPattern;
464cdf0e10cSrcweir sal_Unicode* cp2 = cpPattern;
465cdf0e10cSrcweir bool* bp = bpPatIsWild;
466cdf0e10cSrcweir // Pattern kopieren, Sternchen zaehlen, escaped Jokers
467cdf0e10cSrcweir while ( *cp1 )
468cdf0e10cSrcweir {
469cdf0e10cSrcweir if ( *cp1 == '\\' ) // maybe escaped
470cdf0e10cSrcweir {
471cdf0e10cSrcweir if ( *(cp1+1) == '*' || *(cp1+1) == '?' ) // naechstes Joker?
472cdf0e10cSrcweir {
473cdf0e10cSrcweir cp1++; // skip '\\'
474cdf0e10cSrcweir nPatternLen--;
475cdf0e10cSrcweir }
476cdf0e10cSrcweir *bp++ = false;
477cdf0e10cSrcweir }
478cdf0e10cSrcweir else if ( *cp1 == '*' || *cp1 == '?' ) // Joker
479cdf0e10cSrcweir {
480cdf0e10cSrcweir if ( *cp1 == '*' )
481cdf0e10cSrcweir nStars++; // Sternchenzaehler erhoehen
482cdf0e10cSrcweir *bp++ = true;
483cdf0e10cSrcweir }
484cdf0e10cSrcweir else
485cdf0e10cSrcweir *bp++ = false;
486cdf0e10cSrcweir *cp2++ = *cp1++;
487cdf0e10cSrcweir }
488cdf0e10cSrcweir *cp2 = '\0';
489cdf0e10cSrcweir }
490cdf0e10cSrcweir
491cdf0e10cSrcweir
492cdf0e10cSrcweir // CTor
493cdf0e10cSrcweir
494cdf0e10cSrcweir #ifdef erTEST
495cdf0e10cSrcweir
WLevDistance(const::rtl::OUString & rPattern)496cdf0e10cSrcweir WLevDistance::WLevDistance( const ::rtl::OUString& rPattern ) :
497cdf0e10cSrcweir nPatternLen( rPattern.getLength() ),
498cdf0e10cSrcweir aPatMem( nPatternLen + 1 ),
499cdf0e10cSrcweir nArrayLen( nPatternLen + 1 ),
500cdf0e10cSrcweir aDisMem( nArrayLen ),
501cdf0e10cSrcweir nLimit( LEVDISDEFAULTLIMIT ),
502cdf0e10cSrcweir nRepP0( LEVDISDEFAULT_P0 ),
503cdf0e10cSrcweir nInsQ0( LEVDISDEFAULT_Q0 ),
504cdf0e10cSrcweir nDelR0( LEVDISDEFAULT_R0 ),
505cdf0e10cSrcweir bSplitCount( false )
506cdf0e10cSrcweir {
507cdf0e10cSrcweir InitData( rPattern.getStr() );
508cdf0e10cSrcweir }
509cdf0e10cSrcweir
510cdf0e10cSrcweir #endif // erTEST
511cdf0e10cSrcweir
512cdf0e10cSrcweir
WLevDistance(const sal_Unicode * cPattern,int nOtherX,int nShorterY,int nLongerZ,bool bRelaxed)513cdf0e10cSrcweir WLevDistance::WLevDistance( const sal_Unicode* cPattern,
514cdf0e10cSrcweir int nOtherX, int nShorterY, int nLongerZ,
515cdf0e10cSrcweir bool bRelaxed ) :
516cdf0e10cSrcweir nPatternLen( Impl_WLD_StringLen(cPattern) ),
517cdf0e10cSrcweir aPatMem( nPatternLen + 1 ),
518cdf0e10cSrcweir nArrayLen( nPatternLen + 1 ),
519cdf0e10cSrcweir aDisMem( nArrayLen )
520cdf0e10cSrcweir {
521cdf0e10cSrcweir InitData( cPattern );
522cdf0e10cSrcweir CalcLPQR( nOtherX, nShorterY, nLongerZ, bRelaxed );
523cdf0e10cSrcweir }
524cdf0e10cSrcweir
525cdf0e10cSrcweir
526cdf0e10cSrcweir // CopyCTor
WLevDistance(const WLevDistance & rWLD)527cdf0e10cSrcweir WLevDistance::WLevDistance( const WLevDistance& rWLD ) :
528cdf0e10cSrcweir nPatternLen( rWLD.nPatternLen ),
529cdf0e10cSrcweir aPatMem( nPatternLen + 1 ),
530cdf0e10cSrcweir nArrayLen( nPatternLen + 1 ),
531cdf0e10cSrcweir aDisMem( nArrayLen ),
532cdf0e10cSrcweir nLimit( rWLD.nLimit ),
533cdf0e10cSrcweir nRepP0( rWLD.nRepP0 ),
534cdf0e10cSrcweir nInsQ0( rWLD.nInsQ0 ),
535cdf0e10cSrcweir nDelR0( rWLD.nDelR0 ),
536cdf0e10cSrcweir nStars( rWLD.nStars ),
537cdf0e10cSrcweir bSplitCount( rWLD.bSplitCount )
538cdf0e10cSrcweir {
539cdf0e10cSrcweir cpPattern = aPatMem.GetcPtr();
540cdf0e10cSrcweir bpPatIsWild = aPatMem.GetbPtr();
541cdf0e10cSrcweir npDistance = aDisMem.GetPtr();
542cdf0e10cSrcweir sal_Int32 i;
543cdf0e10cSrcweir for ( i=0; i<nPatternLen; i++ )
544cdf0e10cSrcweir {
545cdf0e10cSrcweir cpPattern[i] = rWLD.cpPattern[i];
546cdf0e10cSrcweir bpPatIsWild[i] = rWLD.bpPatIsWild[i];
547cdf0e10cSrcweir }
548cdf0e10cSrcweir cpPattern[i] = '\0';
549cdf0e10cSrcweir }
550cdf0e10cSrcweir
551cdf0e10cSrcweir
552cdf0e10cSrcweir // DTor
~WLevDistance()553cdf0e10cSrcweir WLevDistance::~WLevDistance()
554cdf0e10cSrcweir {
555cdf0e10cSrcweir }
556cdf0e10cSrcweir
557cdf0e10cSrcweir /*************************************************************************
558cdf0e10cSrcweir * Test
559cdf0e10cSrcweir *************************************************************************/
560cdf0e10cSrcweir
561cdf0e10cSrcweir #ifdef erTEST
562cdf0e10cSrcweir
563cdf0e10cSrcweir #define LINESIZE 1000
564cdf0e10cSrcweir typedef char MAXSTRING [LINESIZE+1];
565cdf0e10cSrcweir
566cdf0e10cSrcweir #ifdef erTESTMAT
567cdf0e10cSrcweir
ShowMatrix(const sal_Unicode * cString)568cdf0e10cSrcweir void WLevDistance::ShowMatrix( const sal_Unicode* cString )
569cdf0e10cSrcweir {
570cdf0e10cSrcweir sal_Int32 r, c, l = Impl_WLD_StringLen(cString);
571cdf0e10cSrcweir printf(" | ");
572cdf0e10cSrcweir for ( c=0; c<nPatternLen; c++ )
573cdf0e10cSrcweir #error Error: conversion from sal_Unicode to char needed!
574cdf0e10cSrcweir printf( " %c ", cpPattern[c] );
575cdf0e10cSrcweir printf("\n---+---");
576cdf0e10cSrcweir for ( c=0; c<nPatternLen; c++ )
577cdf0e10cSrcweir printf( "---" );
578cdf0e10cSrcweir for ( r=0; r<=l && r < erTESTMATMAX; r++ )
579cdf0e10cSrcweir {
580cdf0e10cSrcweir #error Error: conversion from sal_Unicode to char needed!
581cdf0e10cSrcweir printf( "\n %c |", ( r==0 ? ' ' : cString[r-1] ) );
582cdf0e10cSrcweir for ( c=0; c<=nPatternLen && c < erTESTMATMAX; c++ )
583cdf0e10cSrcweir printf( "%2d ", npMatrix[r][c] );
584cdf0e10cSrcweir }
585cdf0e10cSrcweir printf("\n\n");
586cdf0e10cSrcweir }
587cdf0e10cSrcweir
588cdf0e10cSrcweir #endif // erTESTMAT
589cdf0e10cSrcweir
590cdf0e10cSrcweir // Delimiter fuer Token, \t Tab bleibt fuer immer an der ersten Stelle
591cdf0e10cSrcweir MAXSTRING cDelim = "\t, ;(){}[]<>&=+-/%!|.\\'\"~";
592cdf0e10cSrcweir
ShowTest()593cdf0e10cSrcweir void WLevDistance::ShowTest()
594cdf0e10cSrcweir {
595cdf0e10cSrcweir printf(" \n");
596cdf0e10cSrcweir #error Error: conversion from sal_Unicode to char needed!
597cdf0e10cSrcweir printf(" a *cpPattern . . . . : %s\n", cpPattern);
598cdf0e10cSrcweir printf(" b *bpPatIsWild . . . : ");
599cdf0e10cSrcweir for ( sal_Int32 i=0; i<nPatternLen; i++ )
600cdf0e10cSrcweir printf("%d", bpPatIsWild[i]);
601cdf0e10cSrcweir printf("\n");
602cdf0e10cSrcweir printf(" c nPatternLen . . . : %d\n", (int)nPatternLen);
603cdf0e10cSrcweir printf(" d nStars . . . . . . : %d\n", nStars);
604cdf0e10cSrcweir printf(" e nLimit . . . . . . : %d\n", nLimit);
605cdf0e10cSrcweir printf(" f nRepP0 (Ersetzen) : %d\n", nRepP0);
606cdf0e10cSrcweir printf(" g nInsQ0 (Einfuegen) : %d\n", nInsQ0);
607cdf0e10cSrcweir printf(" h nDelR0 (Loeschen) : %d\n", nDelR0);
608cdf0e10cSrcweir printf(" i bSplitCount . . . : %d\n", bSplitCount);
609cdf0e10cSrcweir #error Error: conversion from sal_Unicode to char needed!
610cdf0e10cSrcweir printf(" j cDelim . . . . . . : '%s'\n", cDelim);
611cdf0e10cSrcweir printf(" ~\n");
612cdf0e10cSrcweir }
613cdf0e10cSrcweir
IsDelim(char c)614cdf0e10cSrcweir inline bool IsDelim( char c )
615cdf0e10cSrcweir {
616cdf0e10cSrcweir char* cp = cDelim;
617cdf0e10cSrcweir while ( *cp )
618cdf0e10cSrcweir if ( *cp++ == c )
619cdf0e10cSrcweir return( true );
620cdf0e10cSrcweir return( false );
621cdf0e10cSrcweir }
622cdf0e10cSrcweir
623cdf0e10cSrcweir MAXSTRING cString, cLine, cIgString;
624cdf0e10cSrcweir
main(int argc,char ** argv)625cdf0e10cSrcweir int main( int argc, char **argv )
626cdf0e10cSrcweir {
627cdf0e10cSrcweir int nLim, nP0, nQ0, nR0, nX, nY, nZ;
628cdf0e10cSrcweir int args = 0;
629cdf0e10cSrcweir bool IgnoreCase = false, Direct = false, bStrict = false;
630cdf0e10cSrcweir WLevDistance* pTest;
631cdf0e10cSrcweir if ( argc < 2 )
632cdf0e10cSrcweir {
633cdf0e10cSrcweir printf("%s Syntax:\n"
634cdf0e10cSrcweir " ... [-i] cPattern [nOtherX, nShorterY, nLongerZ [bStrict [cDelim]]] [<stdin] [>stdout]\n"
635cdf0e10cSrcweir " ... -d cPattern [nLimit [nRepP0 nInsQ0 nDelR0 [cDelim]]] [<stdin] [>stdout]\n"
636cdf0e10cSrcweir , argv[0]);
637cdf0e10cSrcweir exit(1);
638cdf0e10cSrcweir }
639cdf0e10cSrcweir if ( *argv[1] == '-' )
640cdf0e10cSrcweir {
641cdf0e10cSrcweir args++;
642cdf0e10cSrcweir argc--;
643cdf0e10cSrcweir switch ( *(argv[1]+1) )
644cdf0e10cSrcweir {
645cdf0e10cSrcweir case 'i':
646cdf0e10cSrcweir {
647cdf0e10cSrcweir IgnoreCase = true;
648cdf0e10cSrcweir char* cp = argv[args+1];
649cdf0e10cSrcweir while ( (*cp = tolower( *cp )) != 0 )
650cdf0e10cSrcweir cp++;
651cdf0e10cSrcweir break;
652cdf0e10cSrcweir }
653cdf0e10cSrcweir case 'd':
654cdf0e10cSrcweir Direct = true;
655cdf0e10cSrcweir break;
656cdf0e10cSrcweir }
657cdf0e10cSrcweir }
658cdf0e10cSrcweir if ( Direct )
659cdf0e10cSrcweir {
660cdf0e10cSrcweir if ( argc > 2 )
661cdf0e10cSrcweir nLim = atoi(argv[args+2]);
662cdf0e10cSrcweir else
663cdf0e10cSrcweir nLim = LEVDISDEFAULTLIMIT;
664cdf0e10cSrcweir if ( argc > 3 )
665cdf0e10cSrcweir {
666cdf0e10cSrcweir nP0 = atoi(argv[args+3]);
667cdf0e10cSrcweir nQ0 = atoi(argv[args+4]);
668cdf0e10cSrcweir nR0 = atoi(argv[args+5]);
669cdf0e10cSrcweir }
670cdf0e10cSrcweir else
671cdf0e10cSrcweir {
672cdf0e10cSrcweir nP0 = LEVDISDEFAULT_P0;
673cdf0e10cSrcweir nQ0 = LEVDISDEFAULT_Q0;
674cdf0e10cSrcweir nR0 = LEVDISDEFAULT_R0;
675cdf0e10cSrcweir }
676cdf0e10cSrcweir if ( argc > 6 )
677cdf0e10cSrcweir {
678cdf0e10cSrcweir strncpy( cDelim+1, argv[args+6], LINESIZE ); // \t Tab always remains
679cdf0e10cSrcweir cDelim[LINESIZE-1] = 0;
680cdf0e10cSrcweir }
681cdf0e10cSrcweir }
682cdf0e10cSrcweir else
683cdf0e10cSrcweir {
684cdf0e10cSrcweir if ( argc > 2 )
685cdf0e10cSrcweir {
686cdf0e10cSrcweir nX = atoi(argv[args+2]);
687cdf0e10cSrcweir nY = atoi(argv[args+3]);
688cdf0e10cSrcweir nZ = atoi(argv[args+4]);
689cdf0e10cSrcweir }
690cdf0e10cSrcweir else
691cdf0e10cSrcweir {
692cdf0e10cSrcweir nX = LEVDISDEFAULT_XOTHER;
693cdf0e10cSrcweir nY = LEVDISDEFAULT_YSHORTER;
694cdf0e10cSrcweir nZ = LEVDISDEFAULT_ZLONGER;
695cdf0e10cSrcweir }
696cdf0e10cSrcweir if ( argc > 5 )
697cdf0e10cSrcweir bStrict = atoi(argv[args+5]);
698cdf0e10cSrcweir if ( argc > 6 )
699cdf0e10cSrcweir {
700cdf0e10cSrcweir strncpy( cDelim+1, argv[args+6], LINESIZE ); // \t Tab always remains
701cdf0e10cSrcweir cDelim[LINESIZE-1] = 0;
702cdf0e10cSrcweir }
703cdf0e10cSrcweir }
704cdf0e10cSrcweir if ( Direct )
705cdf0e10cSrcweir {
706cdf0e10cSrcweir #error Error: conversion from char to OUString needed!
707cdf0e10cSrcweir pTest = new WLevDistance( argv[args+1] );
708cdf0e10cSrcweir #ifdef erTESTDEFAULT
709cdf0e10cSrcweir pTest->ShowTest();
710cdf0e10cSrcweir #endif
711cdf0e10cSrcweir pTest->SetLimit( nLim );
712cdf0e10cSrcweir pTest->SetReplaceP0( nP0 );
713cdf0e10cSrcweir pTest->SetInsertQ0( nQ0 );
714cdf0e10cSrcweir pTest->SetDeleteR0( nR0 );
715cdf0e10cSrcweir }
716cdf0e10cSrcweir else
717cdf0e10cSrcweir {
718cdf0e10cSrcweir #error Error: conversion from char to sal_Unicode needed!
719cdf0e10cSrcweir pTest = new WLevDistance( argv[args+1], nX, nY, nZ, !bStrict );
720cdf0e10cSrcweir #ifdef erTESTCCTOR
721cdf0e10cSrcweir WLevDistance aTmp( *pTest );
722cdf0e10cSrcweir aTmp.ShowTest();
723cdf0e10cSrcweir #endif
724cdf0e10cSrcweir nLim = pTest->GetLimit();
725cdf0e10cSrcweir }
726cdf0e10cSrcweir pTest->ShowTest();
727cdf0e10cSrcweir do
728cdf0e10cSrcweir {
729cdf0e10cSrcweir char* cp1, *cp2;
730cdf0e10cSrcweir static long unsigned int nLine = 0;
731cdf0e10cSrcweir cp1 = cLine;
732cdf0e10cSrcweir cin.getline( cLine, LINESIZE ) ;
733cdf0e10cSrcweir nLine++;
734cdf0e10cSrcweir while ( *cp1 )
735cdf0e10cSrcweir {
736cdf0e10cSrcweir while ( *cp1 && IsDelim(*cp1) )
737cdf0e10cSrcweir cp1++;
738cdf0e10cSrcweir cp2 = cString;
739cdf0e10cSrcweir while ( *cp1 && !IsDelim(*cp1) )
740cdf0e10cSrcweir *cp2++ = *cp1++;
741cdf0e10cSrcweir *cp2 = '\0';
742cdf0e10cSrcweir while ( *cp1 && IsDelim(*cp1) )
743cdf0e10cSrcweir cp1++;
744cdf0e10cSrcweir if ( *cString )
745cdf0e10cSrcweir {
746cdf0e10cSrcweir int ret;
747cdf0e10cSrcweir if ( IgnoreCase )
748cdf0e10cSrcweir {
749cdf0e10cSrcweir char* cpi1 = cString;
750cdf0e10cSrcweir char* cpi2 = cIgString;
751cdf0e10cSrcweir while ( *cpi1 )
752cdf0e10cSrcweir *cpi2++ = tolower( *cpi1++ );
753cdf0e10cSrcweir *cpi2 = '\0';
754cdf0e10cSrcweir #error Error: conversion from char to OUString / sal_Unicode,length needed!
755cdf0e10cSrcweir ret = pTest->WLD( cIgString );
756cdf0e10cSrcweir }
757cdf0e10cSrcweir else
758cdf0e10cSrcweir #error Error: conversion from char to OUString / sal_Unicode,length needed!
759cdf0e10cSrcweir ret = pTest->WLD( cString );
760cdf0e10cSrcweir #ifdef erTESTMAT
761cdf0e10cSrcweir printf("\n# %3d : %s\n", ret, cString);
762cdf0e10cSrcweir #error Error: conversion from char to sal_Unicode needed!
763cdf0e10cSrcweir pTest->ShowMatrix( cString );
764cdf0e10cSrcweir #else
765cdf0e10cSrcweir if ( ret <= nLim )
766cdf0e10cSrcweir printf("# %3d : %s\t(line %lu)\t%s\n", ret, cString, nLine, cLine);
767cdf0e10cSrcweir #endif
768cdf0e10cSrcweir }
769cdf0e10cSrcweir }
770cdf0e10cSrcweir } while ( !cin.eof() );
771cdf0e10cSrcweir return 0;
772cdf0e10cSrcweir }
773cdf0e10cSrcweir
774cdf0e10cSrcweir #endif // erTEST
775cdf0e10cSrcweir
776