1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - PRC -
3   File:     prc_algo_fine.c
4 
5   Copyright 2003-2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2008-09-18#$
14   $Rev: 8573 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 #include <nitro.h>
19 #include <nitro/prc/algo_fine.h>
20 
21 /*===========================================================================*
22   Prototype Declarations
23  *===========================================================================*/
24 
25 static inline u32 PRCi_GetPatternLocalBufferSize_Fine(void);
26 
27 static void
28 PRCi_CalcStrokeDistance_Fine(fx32 *score,
29                              fx32 *weight,
30                              const PRCiPatternData_Common *inputData,
31                              const PRCiPatternData_Common *protoData,
32                              int normalizeSize,
33                              int lengthFilterThreshold,
34                              int lengthFilterRatio, int iStroke, void *buffer);
35 
36 /*===========================================================================*
37   Variable Definitions
38  *===========================================================================*/
39 
40 /*===========================================================================*
41   Functions
42  *===========================================================================*/
43 
44 /*---------------------------------------------------------------------------*
45   Name:         PRC_GetRecognitionBufferSizeEx_Fine
46 
47   Description:  Calculates the work area size required for recognition algorithm.
48 
49 
50   Arguments:    maxPointCount: Upper limit of number of input points (includes pen up marker)
51                 maxStrokeCount: Upper limit of stroke count
52                 protoDB: Sample DB
53                 param: Parameters for recognition processing
54 
55   Returns:      The memory capacity required for the recognition algorithm.
56  *---------------------------------------------------------------------------*/
57 u32
PRC_GetRecognitionBufferSizeEx_Fine(int maxPointCount,int maxStrokeCount,const PRCPrototypeDB_Fine * protoDB,const PRCRecognizeParam_Fine * param)58 PRC_GetRecognitionBufferSizeEx_Fine(int maxPointCount,
59                                     int maxStrokeCount,
60                                     const PRCPrototypeDB_Fine *protoDB,
61                                     const PRCRecognizeParam_Fine *param)
62 {
63     u32     size = 0;
64 
65     (void)maxStrokeCount;
66     (void)param;
67 
68     if (maxPointCount < protoDB->maxPointCount)
69     {
70         maxPointCount = protoDB->maxPointCount;
71     }
72 
73     size += PRCi_ARRAY_SIZE(int, maxPointCount * maxPointCount,);
74     size += PRCi_ARRAY_SIZE(int, maxPointCount * maxPointCount,);
75     size += PRCi_ARRAY_SIZE(int, maxPointCount * maxPointCount,);
76     size += PRCi_ARRAY_SIZE(int, ((maxPointCount + 1) * (maxPointCount + 1)),);
77     return size;
78 }
79 
80 /*---------------------------------------------------------------------------*
81   Name:         PRC_GetRecognizedEntriesEx_Fine
82 
83   Description:  Compares and recognizes a specific kind entry and input pattern to return the top numRanking ranking of the result.
84 
85 
86   Arguments:    resultEntries   Pointer to array that holds pointer to recognition result.
87                                 If less than the requested number could be recognized, fill in remainder with NULL.
88 
89                 resultScores: Pointer to recognition result score array
90                 numRanking: Number of returns to result*
91                 buffer: Pointer to memory region used by recognition algorithm
92                                 (Region size >= return value of PRC_GetRecognitionBufferSize)
93                 input: Input pattern
94                 protoDB: Sample DB
95                 kindMask: Takes the logical AND of the 'kind' values for all the sample DB entries. Considered valid if it is non-zero.
96 
97                 param: Parameters for recognition processing
98 
99   Returns:      Number of patterns in the compared sample DBs.
100  *---------------------------------------------------------------------------*/
101 int
PRC_GetRecognizedEntriesEx_Fine(PRCPrototypeEntry ** resultEntries,fx32 * resultScores,int numRanking,void * buffer,const PRCInputPattern_Fine * input,const PRCPrototypeDB_Fine * protoDB,u32 kindMask,const PRCRecognizeParam_Fine * param)102 PRC_GetRecognizedEntriesEx_Fine(PRCPrototypeEntry **resultEntries,
103                                 fx32 *resultScores,
104                                 int numRanking,
105                                 void *buffer,
106                                 const PRCInputPattern_Fine *input,
107                                 const PRCPrototypeDB_Fine *protoDB,
108                                 u32 kindMask, const PRCRecognizeParam_Fine *param)
109 {
110     int     i;
111     const PRCiPatternData_Common *inputData;
112     int     numCompared;
113     int     normalizeSize;
114     int     lengthFilterThreshold, lengthFilterRatio;
115 
116     SDK_ASSERT(resultEntries);
117     SDK_ASSERT(resultScores);
118     SDK_ASSERT(input);
119     SDK_ASSERT(protoDB);
120     SDK_ASSERT(numRanking > 0);
121 
122     for (i = 0; i < numRanking; i++)
123     {
124         resultEntries[i] = NULL;
125         resultScores[i] = 0;
126     }
127 
128     normalizeSize = protoDB->normalizeSize;
129     if (normalizeSize < input->normalizeSize)
130     {
131         normalizeSize = input->normalizeSize;
132     }
133 
134     if (param == NULL)
135     {
136         lengthFilterThreshold = PRC_LENGTH_FILTER_DEFAULT_THRESHOLD_FINE;
137         lengthFilterRatio = PRC_LENGTH_FILTER_DEFAULT_RATIO_FINE;
138     }
139     else
140     {
141         lengthFilterThreshold = param->lengthFilterThreshold;
142         lengthFilterRatio = param->lengthFilterRatio;
143     }
144 
145     inputData = &input->data;
146     numCompared = 0;
147 
148     {
149         const PRCiPrototypeEntry_Common *proto;
150         int     iPattern;
151 
152         proto = protoDB->patterns;
153 
154         for (iPattern = 0; iPattern < protoDB->patternCount; iPattern++, proto++)
155         {
156             const PRCiPatternData_Common *protoData;
157             int     iStroke;
158             fx32    wholeScore, wholeWeight;
159 
160             if (!proto->entry->enabled || !(proto->entry->kind & kindMask))
161                 continue;
162 
163             protoData = &proto->data;
164 
165             if (inputData->strokeCount != protoData->strokeCount)
166                 continue;
167 
168             numCompared++;
169             wholeScore = 0;
170             wholeWeight = 0;
171 
172             for (iStroke = 0; iStroke < inputData->strokeCount; iStroke++)
173             {
174                 fx32    score, weight;
175 
176                 score = 0;
177 
178                 PRCi_CalcStrokeDistance_Fine(&score, &weight, inputData, protoData, normalizeSize,
179                                              lengthFilterThreshold, lengthFilterRatio, iStroke,
180                                              buffer);
181 
182                 wholeScore += score;
183                 wholeWeight += weight;
184             }
185 
186             wholeScore = FX_Div(wholeScore * 4, wholeWeight * normalizeSize);   // 4 == (1<<FX32_SHIFT)/(512*2) , about 512 and 2, see below.
187 //            wholeScore = FX_Div(wholeScore<<FX32_SHIFT, wholeWeight*normalizeSize*2*512); // normalizeSize*2 == doubleWidth, 512 = max of angleScore * 2
188 
189             if (wholeScore < 0)
190                 wholeScore = 0;
191             if (wholeScore >= FX32_ONE)
192                 wholeScore = FX32_ONE;
193             if (proto->entry->correction != 0)
194             {
195                 wholeScore = FX_Mul(wholeScore, FX32_ONE - proto->entry->correction)
196                     + proto->entry->correction;
197             }
198 
199             if (resultScores[numRanking - 1] < wholeScore)
200             {
201                 resultScores[numRanking - 1] = wholeScore;
202                 resultEntries[numRanking - 1] = (PRCPrototypeEntry *)proto->entry;
203                 for (i = numRanking - 2; i >= 0; i--)
204                 {
205                     if (resultScores[i] < resultScores[i + 1])
206                     {
207                         fx32    tmpScore;
208                         PRCPrototypeEntry *tmpEntry;
209                         tmpScore = resultScores[i];
210                         resultScores[i] = resultScores[i + 1];
211                         resultScores[i + 1] = tmpScore;
212                         tmpEntry = resultEntries[i];
213                         resultEntries[i] = resultEntries[i + 1];
214                         resultEntries[i + 1] = tmpEntry;
215                     }
216                 }
217             }
218         }
219     }
220 
221     return numCompared;
222 }
223 
224 /*===========================================================================*
225   Static Functions
226  *===========================================================================*/
227 #define PRCi_SINGLE_ANGLE_SCORE 128
228 
CityBlockDistance(const PRCPoint * p1,const PRCPoint * p2)229 static inline int CityBlockDistance(const PRCPoint *p1, const PRCPoint *p2)
230 {
231     int     x = p1->x - p2->x;
232     int     y = p1->y - p2->y;
233     if (x < 0)
234         x = -x;
235     if (y < 0)
236         y = -y;
237     return (x + y);
238 }
239 
240 #define PRCi_ABS(x) (((x)>=0)?(x):-(x))
241 
242 #define PRCi_ANGLE_SCORE(input, proto) ((32768-PRCi_ABS((s16)(protoAngles[(proto)]-inputAngles[(input)])))/128)
243 
244 static void
PRCi_CalcStrokeDistance_Fine(fx32 * score,fx32 * weight,const PRCiPatternData_Common * inputData,const PRCiPatternData_Common * protoData,int normalizeSize,int lengthFilterThreshold,int lengthFilterRatio,int iStroke,void * buffer)245 PRCi_CalcStrokeDistance_Fine(fx32 *score,
246                              fx32 *weight,
247                              const PRCiPatternData_Common *inputData,
248                              const PRCiPatternData_Common *protoData,
249                              int normalizeSize,
250                              int lengthFilterThreshold,
251                              int lengthFilterRatio, int iStroke, void *buffer)
252 {
253 #define nMatches_(x,y) (*(nMatches + (x) * maxPointCount + (y)))
254 #define sumScore_(x,y) (*(sumScore + (x) * maxPointCount + (y)))
255 #define direction_(x,y) (*(direction + (x) * maxPointCount + (y)))
256 #define angleScores_(x,y) (*(angleScores + (x) * (maxPointCount+1) + (y)))
257 
258     // Calculate the degree of similarity between strokes, with DP matching
259     int     iInput, iProto;
260     int    *nMatches;                  //[STROKE_PACKED_POINT_MAX][STROKE_PACKED_POINT_MAX];
261     int    *sumScore;                  //[STROKE_PACKED_POINT_MAX][STROKE_PACKED_POINT_MAX];
262     int    *direction;                 //[STROKE_PACKED_POINT_MAX][STROKE_PACKED_POINT_MAX];
263     int    *angleScores;               //[STROKE_PACKED_POINT_MAX+1][STROKE_PACKED_POINT_MAX+1];
264 
265     int     protoStrokeIndex, inputStrokeIndex;
266     int     protoSize, inputSize;
267     const PRCPoint *protoPoints;
268     const PRCPoint *inputPoints;
269     const u16 *protoAngles;
270     const u16 *inputAngles;
271     const fx16 *protoRatios;
272     const fx16 *inputRatios;
273     const fx32 *protoLengths;
274     const fx32 *inputLengths;
275     fx32    protoStrokeLength, inputStrokeLength;
276     int     maxPointCount;
277     int     doubleWidth;
278 
279     maxPointCount = inputData->pointCount;
280     if (maxPointCount < protoData->pointCount)
281         maxPointCount = protoData->pointCount;
282 
283 #ifdef SDK_DEBUG
284     {
285         int     width, tmp;
286 
287         width = inputData->wholeBoundingBox.x2 - inputData->wholeBoundingBox.x1;
288         tmp = inputData->wholeBoundingBox.y2 - inputData->wholeBoundingBox.y1;
289         if (tmp > width)
290             width = tmp;
291         tmp = protoData->wholeBoundingBox.x2 - protoData->wholeBoundingBox.x1;
292         if (tmp > width)
293             width = tmp;
294         tmp = protoData->wholeBoundingBox.y2 - protoData->wholeBoundingBox.y1;
295         if (tmp > width)
296             width = tmp;
297 
298         width++;
299         if (width > normalizeSize)
300         {
301             OS_Warning
302                 ("too small normalizeSize. PRCPrototypeList.normalizeSize seems smaller than actual data.");
303         }
304     }
305 #endif
306     doubleWidth = normalizeSize * 2;
307 
308     // Allocate the buffer
309     {
310         int     addr;
311         addr = 0;
312         PRCi_ALLOC_ARRAY(nMatches, int, maxPointCount * maxPointCount, buffer, addr);
313         PRCi_ALLOC_ARRAY(sumScore, int, maxPointCount * maxPointCount, buffer, addr);
314         PRCi_ALLOC_ARRAY(direction, int, maxPointCount * maxPointCount, buffer, addr);
315         PRCi_ALLOC_ARRAY(angleScores,
316                          int, ((maxPointCount + 1) * (maxPointCount + 1)), buffer, addr);
317     }
318 
319     protoStrokeIndex = protoData->strokes[iStroke];
320     inputStrokeIndex = inputData->strokes[iStroke];
321     protoSize = protoData->strokeSizes[iStroke];
322     inputSize = inputData->strokeSizes[iStroke];
323     protoPoints = &protoData->pointArray[protoStrokeIndex];
324     inputPoints = &inputData->pointArray[inputStrokeIndex];
325     protoAngles = &protoData->lineSegmentAngleArray[protoStrokeIndex];
326     inputAngles = &inputData->lineSegmentAngleArray[inputStrokeIndex];
327     protoRatios = &protoData->lineSegmentRatioToStrokeArray[protoStrokeIndex];
328     inputRatios = &inputData->lineSegmentRatioToStrokeArray[inputStrokeIndex];
329     protoLengths = &protoData->lineSegmentLengthArray[protoStrokeIndex];
330     inputLengths = &inputData->lineSegmentLengthArray[inputStrokeIndex];
331     protoStrokeLength = protoData->strokeLengths[iStroke];
332     inputStrokeLength = inputData->strokeLengths[iStroke];
333 
334     *weight = FX32_ONE;
335     *score = 0;
336 
337     if (protoSize == 0 || inputSize == 0)
338         return;
339 
340     // Line segments whose length are too different will have their similarity set at 0 and no further calculation will be done.
341     if (inputStrokeLength > lengthFilterThreshold || protoStrokeLength > lengthFilterThreshold)
342     {
343         if (inputStrokeLength * lengthFilterRatio < protoStrokeLength
344             || protoStrokeLength * lengthFilterRatio < inputStrokeLength)
345         {
346 #ifdef PRC_DEBUG_RECOGNIZE_DEEPLY
347             OS_Printf("Skipped because of length filter %d <=> %d\n", FX_Whole(inputStrokeLength),
348                       FX_Whole(protoStrokeLength));
349 #endif
350             return;
351         }
352     }
353 
354     // Calculates angleScores[][] in advance, which is scores relating to angles
355     if (protoSize == 1 || inputSize == 1)
356     {
357         for (iInput = 0; iInput < inputSize; iInput++)
358         {
359             for (iProto = 0; iProto < protoSize; iProto++)
360             {
361                 angleScores_(iInput, iProto) = PRCi_SINGLE_ANGLE_SCORE;
362             }
363         }
364     }
365     else
366     {
367         angleScores_(0, 0) = PRCi_ANGLE_SCORE(1, 1);
368         angleScores_(inputSize, 0) = PRCi_ANGLE_SCORE(inputSize - 1, 1);
369         angleScores_(0, protoSize) = PRCi_ANGLE_SCORE(1, protoSize - 1);
370         angleScores_(inputSize, protoSize) = PRCi_ANGLE_SCORE(inputSize - 1, protoSize - 1);
371 
372         for (iInput = 1; iInput < inputSize; iInput++)
373         {
374             angleScores_(iInput, 0) = PRCi_ANGLE_SCORE(iInput, 1);
375             angleScores_(iInput, protoSize) = PRCi_ANGLE_SCORE(iInput, protoSize - 1);
376         }
377         for (iProto = 1; iProto < protoSize; iProto++)
378         {
379             angleScores_(0, iProto) = PRCi_ANGLE_SCORE(1, iProto);
380             angleScores_(inputSize, iProto) = PRCi_ANGLE_SCORE(inputSize - 1, iProto);
381         }
382 
383         for (iInput = 1; iInput < inputSize; iInput++)
384         {
385             for (iProto = 1; iProto < protoSize; iProto++)
386             {
387                 angleScores_(iInput, iProto) = PRCi_ANGLE_SCORE(iInput, iProto);
388             }
389         }
390     }
391 
392     // Use DP matching to find corresponding points between strokes
393 #if 1
394     sumScore_(0, 0) =
395         (doubleWidth - CityBlockDistance(&inputPoints[0], &protoPoints[0])) * angleScores_(0,
396                                                                                            0) * 2;
397     nMatches_(0, 0) = 1;
398     for (iInput = 1; iInput < inputSize; iInput++)
399     {
400         sumScore_(iInput, 0) =
401             (doubleWidth -
402              CityBlockDistance(&inputPoints[iInput], &protoPoints[0])) * (angleScores_(iInput,
403                                                                                        0) +
404                                                                           angleScores_(iInput + 1,
405                                                                                        1)) +
406             sumScore_(iInput - 1, 0);
407         nMatches_(iInput, 0) = nMatches_(iInput - 1, 0) + 1;
408         direction_(iInput, 0) = 2;
409     }
410     for (iProto = 1; iProto < protoSize; iProto++)
411     {
412         sumScore_(0, iProto) =
413             (doubleWidth -
414              CityBlockDistance(&inputPoints[0], &protoPoints[iProto])) * (angleScores_(0,
415                                                                                        iProto) +
416                                                                           angleScores_(1,
417                                                                                        iProto +
418                                                                                        1)) +
419             sumScore_(0, iProto - 1);
420         nMatches_(0, iProto) = nMatches_(0, iProto - 1) + 1;
421         direction_(0, iProto) = 1;
422     }
423 
424     for (iInput = 1; iInput < inputSize; iInput++)
425     {
426         for (iProto = 1; iProto < protoSize; iProto++)
427         {
428             int     sum, n, localScore;
429             int     sumMax, nMax, dirMax;
430 
431             localScore =
432                 (doubleWidth
433                  - CityBlockDistance(&inputPoints[iInput], &protoPoints[iProto]))
434                 * (angleScores_(iInput, iProto) + angleScores_(iInput + 1, iProto + 1));
435 
436             dirMax = 0;
437             sumMax = localScore + sumScore_(iInput - 1, iProto - 1);
438             nMax = nMatches_(iInput - 1, iProto - 1) + 1;
439 
440             sum = localScore + sumScore_(iInput, iProto - 1);
441             n = nMatches_(iInput, iProto - 1) + 1;
442             if (sum * nMax > sumMax * n)
443             {
444                 sumMax = sum;
445                 nMax = n;
446                 dirMax = 1;
447             }
448 
449             sum = localScore + sumScore_(iInput - 1, iProto);
450             n = nMatches_(iInput - 1, iProto) + 1;
451             if (sum * nMax > sumMax * n)
452             {
453                 sumMax = sum;
454                 nMax = n;
455                 dirMax = 2;
456             }
457 
458             sumScore_(iInput, iProto) = sumMax;
459             nMatches_(iInput, iProto) = nMax;
460             direction_(iInput, iProto) = dirMax;
461         }
462     }
463 #elif                                  // beam search version. // Commenting out because doesn't speed up
464     {
465         int     beamThreshold, beamN, beamValue, beamLow, beamHigh, nextBeamHigh;
466         int     maxScore, maxScoreN;
467         BOOL    fInBeamLow;
468 
469         maxScore = 0;
470         maxScoreN = 1;
471 
472         sumScore_(0, 0) =
473             (doubleWidth - CityBlockDistance(&inputPoints[0], &protoPoints[0])) * angleScores_(0,
474                                                                                                0) *
475             2;
476         nMatches_(0, 0) = 1;
477         for (iProto = 1; iProto < protoSize; iProto++)
478         {
479             sumScore_(0, iProto) =
480                 (doubleWidth -
481                  CityBlockDistance(&inputPoints[0], &protoPoints[iProto])) * (angleScores_(0,
482                                                                                            iProto) +
483                                                                               angleScores_(1,
484                                                                                            iProto +
485                                                                                            1)) +
486                 sumScore_(0, iProto - 1);
487             nMatches_(0, iProto) = nMatches_(0, iProto - 1) + 1;
488             direction_(0, iProto) = 1;
489         }
490         for (iInput = 1; iInput < inputSize; iInput++)
491         {
492             int     sc, n;
493 
494             sc = (doubleWidth -
495                   CityBlockDistance(&inputPoints[iInput], &protoPoints[0])) * (angleScores_(iInput,
496                                                                                             0) +
497                                                                                angleScores_(iInput +
498                                                                                             1,
499                                                                                             1)) +
500                 sumScore_(iInput - 1, 0);
501             n = nMatches_(iInput - 1, 0) + 1;
502             if (sc * maxScoreN > maxScore * n)
503             {
504                 maxScore = sc;
505                 maxScoreN = n;
506             }
507             sumScore_(iInput, 0) = sc;
508             nMatches_(iInput, 0) = n;
509             direction_(iInput, 0) = 2;
510         }
511 
512         beamValue = normalizeSize * 2 * 512 / 8;
513         beamLow = 1;
514         nextBeamHigh = inputSize - 1;
515 
516         for (iProto = 1; iProto < protoSize; iProto++)
517         {
518             beamHigh = nextBeamHigh + 1;
519             if (beamHigh < inputSize)
520             {
521                 beamHigh++;
522             }
523             nextBeamHigh = 1;
524             beamN = maxScoreN;
525             beamThreshold = maxScore - beamValue * beamN;
526             fInBeamLow = TRUE;
527             maxScore = 0;
528             maxScoreN = 1;
529 //            OS_Printf(" beam: [%d, %d]\n", beamLow, beamHigh);
530             if (beamLow > 1)
531             {
532                 sumScore_(beamLow - 1, iProto) = 0;
533                 nMatches_(beamLow - 1, iProto) = nMatches_(beamLow - 1, iProto - 1) + 1;
534                 direction_(beamLow - 1, iProto) = 1;
535             }
536             for (iInput = beamLow; iInput < beamHigh; iInput++)
537             {
538                 int     sum, n, localScore;
539                 int     sumMax, nMax, dirMax;
540 
541                 localScore =
542                     (doubleWidth
543                      - CityBlockDistance(&inputPoints[iInput], &protoPoints[iProto]))
544                     * (angleScores_(iInput, iProto) + angleScores_(iInput + 1, iProto + 1));
545 
546                 dirMax = 0;
547                 sumMax = localScore + sumScore_(iInput - 1, iProto - 1);
548                 nMax = nMatches_(iInput - 1, iProto - 1) + 1;
549 
550                 sum = localScore + sumScore_(iInput, iProto - 1);
551                 n = nMatches_(iInput, iProto - 1) + 1;
552                 if (sum * nMax > sumMax * n)
553                 {
554                     sumMax = sum;
555                     nMax = n;
556                     dirMax = 1;
557                 }
558 
559                 sum = localScore + sumScore_(iInput - 1, iProto);
560                 n = nMatches_(iInput - 1, iProto) + 1;
561                 if (sum * nMax > sumMax * n)
562                 {
563                     sumMax = sum;
564                     nMax = n;
565                     dirMax = 2;
566                 }
567 
568                 sumScore_(iInput, iProto) = sumMax;
569                 nMatches_(iInput, iProto) = nMax;
570                 direction_(iInput, iProto) = dirMax;
571 
572                 if (sumMax * beamN < beamThreshold * nMax)
573                 {
574                     if (fInBeamLow)
575                     {
576                         beamLow = iInput;
577                     }
578                 }
579                 else
580                 {
581                     fInBeamLow = FALSE;
582                     nextBeamHigh = iInput;
583                 }
584                 if (sumMax * maxScoreN > maxScore * nMax)
585                 {
586                     maxScore = sumMax;
587                     maxScoreN = nMax;
588                 }
589             }
590         }
591         if (beamHigh < inputSize)
592         {
593             for (iInput = beamHigh; iInput < inputSize; iInput++)
594             {
595                 sumScore_(iInput, protoSize - 1) = sumScore_(iInput - 1, protoSize - 1) + 1;
596                 nMatches_(iInput, protoSize - 1) = nMatches_(iInput - 1, protoSize - 1) + 1;
597                 direction_(iInput, protoSize - 1) = 2;
598             }
599         }
600     }
601 #endif
602 
603 #ifdef PRC_DEBUG_RECOGNIZE
604     {
605         int     localScore, angleScore;
606         iInput = inputSize - 1;
607         iProto = protoSize - 1;
608         while (!(iInput == 0 && iProto == 0))
609         {
610             int     dx, dy;
611             dx = -1 + (direction_(iInput, iProto) & 1);
612             dy = -1 + ((direction_(iInput, iProto) & 2) >> 1);
613             localScore = sumScore_(iInput, iProto) - sumScore_(iInput + dx, iProto + dy);
614             angleScore = angleScores_(iInput, iProto) + angleScores_(iInput + 1, iProto + 1);
615 
616             OS_Printf(" %2d <-> %2d : 0.%03d = 0.%03d * 0.%03d, average from begin: 0.%03d\n",
617                       iInput, iProto, localScore / normalizeSize,
618                       localScore * 512 / angleScore / normalizeSize, angleScore * 2,
619                       sumScore_(iInput, iProto) / nMatches_(iInput, iProto) / normalizeSize);
620 
621             iInput += dx;
622             iProto += dy;
623         }
624         localScore = sumScore_(iInput, iProto);
625         angleScore = angleScores_(iInput, iProto) + angleScores_(iInput + 1, iProto + 1);
626         OS_Printf(" %2d <-> %2d : 0.%03d = 0.%03d * 0.%03d\n", iInput, iProto,
627                   localScore / normalizeSize, localScore * 512 / angleScore / normalizeSize,
628                   angleScore * 2);
629     }
630 
631     OS_Printf("total: %d, matches: %d\n", sumScore_(inputSize - 1, protoSize - 1),
632               nMatches_(inputSize - 1, protoSize - 1));
633 #endif
634 
635     *score = sumScore_(inputSize - 1, protoSize - 1);
636     *weight = nMatches_(inputSize - 1, protoSize - 1) << FX32_SHIFT;
637 }
638 
639 
640 /*---------------------------------------------------------------------------*
641   End of file
642  *---------------------------------------------------------------------------*/
643