Tesseract  3.02
tesseract-ocr/classify/intfx.cpp
Go to the documentation of this file.
00001 /******************************************************************************
00002  **      Filename:    intfx.c
00003  **      Purpose:     Integer character normalization & feature extraction
00004  **      Author:      Robert Moss
00005  **      History:     Tue May 21 15:51:57 MDT 1991, RWM, Created.
00006  **
00007  **      (c) Copyright Hewlett-Packard Company, 1988.
00008  ** Licensed under the Apache License, Version 2.0 (the "License");
00009  ** you may not use this file except in compliance with the License.
00010  ** You may obtain a copy of the License at
00011  ** http://www.apache.org/licenses/LICENSE-2.0
00012  ** Unless required by applicable law or agreed to in writing, software
00013  ** distributed under the License is distributed on an "AS IS" BASIS,
00014  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  ** See the License for the specific language governing permissions and
00016  ** limitations under the License.
00017  ******************************************************************************/
00021 #include "intfx.h"
00022 #include "intmatcher.h"
00023 #include "const.h"
00024 #include "helpers.h"
00025 #include "ccutil.h"
00026 #include "statistc.h"
00027 #include "trainingsample.h"
00028 #ifdef __UNIX__
00029 #endif
00030 
00031 using tesseract::TrainingSample;
00032 
00036 int SaveFeature();
00037 uinT8 BinaryAnglePlusPi(inT32 Y, inT32 X);
00038 uinT8 MySqrt2();
00039 void ClipRadius();
00040 
00041 INT_VAR(classify_radius_gyr_min_man, 255,
00042         "Minimum Radius of Gyration Mantissa 0-255:        ");
00043 
00044 INT_VAR(classify_radius_gyr_min_exp, 0,
00045         "Minimum Radius of Gyration Exponent 0-255:        ");
00046 
00047 INT_VAR(classify_radius_gyr_max_man, 158,
00048         "Maximum Radius of Gyration Mantissa 0-255:        ");
00049 
00050 INT_VAR(classify_radius_gyr_max_exp, 8,
00051         "Maximum Radius of Gyration Exponent 0-255:        ");
00052 
00056 #define  ATAN_TABLE_SIZE    64
00057 
00058 // Look up table for arc tangent containing:
00059 //    atan(0.0) ... atan(ATAN_TABLE_SIZE - 1 / ATAN_TABLE_SIZE)
00060 // The entries are in binary degrees where a full circle is 256 binary degrees.
00061 static uinT8 AtanTable[ATAN_TABLE_SIZE];
00062 // Look up table for cos and sin to turn the intfx feature angle to a vector.
00063 // Also protected by atan_table_mutex.
00064 static float cos_table[INT_CHAR_NORM_RANGE];
00065 static float sin_table[INT_CHAR_NORM_RANGE];
00066 // Guards write access to AtanTable so we dont create it more than once.
00067 tesseract::CCUtilMutex atan_table_mutex;
00068 
00069 
00073 /*---------------------------------------------------------------------------*/
00074 void InitIntegerFX() {
00075   static bool atan_table_init = false;
00076   atan_table_mutex.Lock();
00077   if (!atan_table_init) {
00078     for (int i = 0; i < ATAN_TABLE_SIZE; i++) {
00079       AtanTable[i] =
00080           (uinT8) (atan ((i / (float) ATAN_TABLE_SIZE)) * 128.0 / PI + 0.5);
00081     }
00082     for (int i = 0; i < INT_CHAR_NORM_RANGE; ++i) {
00083       cos_table[i] = cos(i * 2 * PI / INT_CHAR_NORM_RANGE + PI);
00084       sin_table[i] = sin(i * 2 * PI / INT_CHAR_NORM_RANGE + PI);
00085     }
00086     atan_table_init = true;
00087   }
00088   atan_table_mutex.Unlock();
00089 }
00090 
00091 // Returns a vector representing the direction of a feature with the given
00092 // theta direction in an INT_FEATURE_STRUCT.
00093 FCOORD FeatureDirection(uinT8 theta) {
00094   return FCOORD(cos_table[theta], sin_table[theta]);
00095 }
00096 
00097 TrainingSample* GetIntFeatures(tesseract::NormalizationMode mode,
00098                                TBLOB *blob, const DENORM& denorm) {
00099   INT_FEATURE_ARRAY blfeatures;
00100   INT_FEATURE_ARRAY cnfeatures;
00101   INT_FX_RESULT_STRUCT fx_info;
00102   ExtractIntFeat(blob, denorm, blfeatures, cnfeatures, &fx_info, NULL);
00103   TrainingSample* sample = NULL;
00104   if (mode == tesseract::NM_CHAR_ANISOTROPIC) {
00105     int num_features = fx_info.NumCN;
00106     if (num_features > 0) {
00107       sample = TrainingSample::CopyFromFeatures(fx_info, cnfeatures,
00108                                                 num_features);
00109     }
00110   } else if (mode == tesseract::NM_BASELINE) {
00111     int num_features = fx_info.NumBL;
00112     if (num_features > 0) {
00113       sample = TrainingSample::CopyFromFeatures(fx_info, blfeatures,
00114                                                 num_features);
00115     }
00116   } else {
00117     ASSERT_HOST(!"Unsupported normalization mode!");
00118   }
00119   return sample;
00120 }
00121 
00122 
00123 /*--------------------------------------------------------------------------*/
00124 // Extract a set of standard-sized features from Blobs and write them out in
00125 // two formats: baseline normalized and character normalized.
00126 //
00127 // We presume the Blobs are already scaled so that x-height=128 units
00128 //
00129 // Standard Features:
00130 //   We take all outline segments longer than 7 units and chop them into
00131 //   standard-sized segments of approximately 13 = (64 / 5) units.
00132 //   When writing these features out, we output their center and angle as
00133 //   measured counterclockwise from the vector <-1, 0>
00134 //
00135 // Baseline Normalized Output:
00136 //   We center the grapheme by aligning the x-coordinate of its centroid with
00137 //   x=0 and subtracting 128 from the y-coordinate.
00138 //
00139 // Character Normalized Output:
00140 //   We align the grapheme's centroid at the origin and scale it asymmetrically
00141 //   in x and y so that the result is vaguely square.
00142 //
00143 int ExtractIntFeat(TBLOB *Blob,
00144                    const DENORM& denorm,
00145                    INT_FEATURE_ARRAY BLFeat,
00146                    INT_FEATURE_ARRAY CNFeat,
00147                    INT_FX_RESULT_STRUCT* Results,
00148                    inT32 *FeatureOutlineArray) {
00149 
00150   TESSLINE *OutLine;
00151   EDGEPT *Loop, *LoopStart, *Segment;
00152   inT16 LastX, LastY, Xmean, Ymean;
00153   inT32 NormX, NormY, DeltaX, DeltaY;
00154   inT32 Xsum, Ysum;
00155   uinT32 Ix, Iy, LengthSum;
00156   uinT16 n;
00157   // n - the number of features to extract from a given outline segment.
00158   //   We extract features from every outline segment longer than ~6 units.
00159   //   We chop these long segments into standard-sized features approximately
00160   //   13 (= 64 / 5) units in length.
00161   uinT8 Theta;
00162   uinT16 NumBLFeatures, NumCNFeatures;
00163   uinT8 RxInv, RyInv;            /* x.xxxxxxx  *  2^Exp  */
00164   uinT8 RxExp, RyExp;
00165                                  /* sxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxx */
00166   register inT32 pfX, pfY, dX, dY;
00167   uinT16 Length;
00168   register int i;
00169 
00170   Results->Length = 0;
00171   Results->Xmean = 0;
00172   Results->Ymean = 0;
00173   Results->Rx = 0;
00174   Results->Ry = 0;
00175   Results->NumBL = 0;
00176   Results->NumCN = 0;
00177   Results->YBottom = MAX_UINT8;
00178   Results->YTop = 0;
00179 
00180   // Calculate the centroid (Xmean, Ymean) for the blob.
00181   //   We use centroid (instead of center of bounding box or center of smallest
00182   //   enclosing circle) so the algorithm will not be too greatly influenced by
00183   //   small amounts of information at the edge of a character's bounding box.
00184   NumBLFeatures = 0;
00185   NumCNFeatures = 0;
00186   OutLine = Blob->outlines;
00187   Xsum = 0;
00188   Ysum = 0;
00189   LengthSum = 0;
00190   while (OutLine != NULL) {
00191     LoopStart = OutLine->loop;
00192     Loop = LoopStart;
00193     LastX = Loop->pos.x;
00194     LastY = Loop->pos.y;
00195     /* Check for bad loops */
00196     if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart))
00197       return FALSE;
00198     do {
00199       Segment = Loop;
00200       Loop = Loop->next;
00201       NormX = Loop->pos.x;
00202       NormY = Loop->pos.y;
00203 
00204       n = 1;
00205       if (!Segment->IsHidden()) {
00206         DeltaX = NormX - LastX;
00207         DeltaY = NormY - LastY;
00208         Length = MySqrt(DeltaX, DeltaY);
00209         n = ((Length << 2) + Length + 32) >> 6;
00210         if (n != 0) {
00211           Xsum += ((LastX << 1) + DeltaX) * (int) Length;
00212           Ysum += ((LastY << 1) + DeltaY) * (int) Length;
00213           LengthSum += Length;
00214         }
00215       }
00216       if (n != 0) {              /* Throw away a point that is too close */
00217         LastX = NormX;
00218         LastY = NormY;
00219       }
00220     }
00221     while (Loop != LoopStart);
00222     OutLine = OutLine->next;
00223   }
00224   if (LengthSum == 0)
00225     return FALSE;
00226   Xmean = (Xsum / (inT32) LengthSum) >> 1;
00227   Ymean = (Ysum / (inT32) LengthSum) >> 1;
00228 
00229   Results->Length = LengthSum;
00230   Results->Xmean = Xmean;
00231   Results->Ymean = Ymean;
00232 
00233   // Extract Baseline normalized features,
00234   // and find 2nd moments (Ix, Iy) & radius of gyration (Rx, Ry).
00235   //
00236   //   Ix = Sum y^2 dA, where:
00237   //     Ix: the second moment of area about the axis x
00238   //     dA = 1 for our standard-sized piece of outline
00239   //      y: the perependicular distance to the x axis
00240   //   Rx = sqrt(Ix / A)
00241   //      Note: 1 <= Rx <= height of blob / 2
00242   //   Ry = sqrt(Iy / A)
00243   //      Note: 1 <= Ry <=  width of blob / 2
00244   Ix = 0;
00245   Iy = 0;
00246   NumBLFeatures = 0;
00247   OutLine = Blob->outlines;
00248   int min_x = 0;
00249   int max_x = 0;
00250   while (OutLine != NULL) {
00251     LoopStart = OutLine->loop;
00252     Loop = LoopStart;
00253     LastX = Loop->pos.x - Xmean;
00254     LastY = Loop->pos.y;
00255     /* Check for bad loops */
00256     if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart))
00257       return FALSE;
00258     do {
00259       Segment = Loop;
00260       Loop = Loop->next;
00261       NormX = Loop->pos.x - Xmean;
00262       NormY = Loop->pos.y;
00263       if (NormY < Results->YBottom)
00264         Results->YBottom = ClipToRange(NormY, 0, MAX_UINT8);
00265       if (NormY > Results->YTop)
00266         Results->YTop = ClipToRange(NormY, 0, MAX_UINT8);
00267       UpdateRange(NormX, &min_x, &max_x);
00268 
00269       n = 1;
00270       if (!Segment->IsHidden()) {
00271         DeltaX = NormX - LastX;
00272         DeltaY = NormY - LastY;
00273         Length = MySqrt(DeltaX, DeltaY);
00274         n = ((Length << 2) + Length + 32) >> 6;
00275         if (n != 0) {
00276           Theta = BinaryAnglePlusPi(DeltaY, DeltaX);
00277           dX = (DeltaX << 8) / n;
00278           dY = (DeltaY << 8) / n;
00279           pfX = (LastX << 8) + (dX >> 1);
00280           pfY = (LastY << 8) + (dY >> 1);
00281           Ix += ((pfY >> 8) - Ymean) * ((pfY >> 8) - Ymean);
00282           // TODO(eger): Hmmm... Xmean is not necessarily 0.
00283           //   Figure out if we should center against Xmean for these
00284           //   features, and if so fix Iy & SaveFeature().
00285           Iy += (pfX >> 8) * (pfX >> 8);
00286           if (SaveFeature(BLFeat,
00287                           NumBLFeatures,
00288                           (inT16) (pfX >> 8),
00289                           (inT16) ((pfY >> 8) - 128),
00290                           Theta) == FALSE)
00291             return FALSE;
00292           NumBLFeatures++;
00293           for (i = 1; i < n; i++) {
00294             pfX += dX;
00295             pfY += dY;
00296             Ix += ((pfY >> 8) - Ymean) * ((pfY >> 8) - Ymean);
00297             Iy += (pfX >> 8) * (pfX >> 8);
00298             if (SaveFeature(BLFeat,
00299                             NumBLFeatures,
00300                             (inT16) (pfX >> 8),
00301                             (inT16) ((pfY >> 8) - 128),
00302                             Theta) == FALSE)
00303               return FALSE;
00304             NumBLFeatures++;
00305           }
00306         }
00307       }
00308       if (n != 0) {              /* Throw away a point that is too close */
00309         LastX = NormX;
00310         LastY = NormY;
00311       }
00312     }
00313     while (Loop != LoopStart);
00314     OutLine = OutLine->next;
00315   }
00316   Results->Width = max_x - min_x;
00317   if (Ix == 0)
00318     Ix = 1;
00319   if (Iy == 0)
00320     Iy = 1;
00321   RxInv = MySqrt2 (NumBLFeatures, Ix, &RxExp);
00322   RyInv = MySqrt2 (NumBLFeatures, Iy, &RyExp);
00323   ClipRadius(&RxInv, &RxExp, &RyInv, &RyExp);
00324 
00325   Results->Rx = (inT16) (51.2 / (double) RxInv * pow (2.0, (double) RxExp));
00326   Results->Ry = (inT16) (51.2 / (double) RyInv * pow (2.0, (double) RyExp));
00327   if (Results->Ry == 0) {
00328     /*
00329         This would result in features having 'nan' values.
00330         Since the expression is always > 0, assign a value of 1.
00331     */
00332     Results->Ry = 1;
00333   }
00334   Results->NumBL = NumBLFeatures;
00335 
00336   // Extract character normalized features
00337   //
00338   //   Rescale the co-ordinates to "equalize" distribution in X and Y, making
00339   //   all of the following unichars be sized to look similar:  , ' 1 i
00340   //
00341   //   We calculate co-ordinates relative to the centroid, and then scale them
00342   //   as follows (accomplishing a scale of up to 102.4 / dimension):
00343   //     y *= 51.2 / Rx    [ y scaled by 0.0 ... 102.4 / height of glyph  ]
00344   //     x *= 51.2 / Ry    [ x scaled by 0.0 ... 102.4 / width of glyph ]
00345   //   Although tempting to think so, this does not guarantee that our range
00346   //   is within [-102.4...102.4] x [-102.4...102.4] because (Xmean, Ymean)
00347   //   is the centroid, not the center of the bounding box.  Instead, we can
00348   //   only bound the result to [-204 ... 204] x [-204 ... 204]
00349   //
00350   NumCNFeatures = 0;
00351   OutLine = Blob->outlines;
00352   int OutLineIndex = -1;
00353   while (OutLine != NULL) {
00354     LoopStart = OutLine->loop;
00355     Loop = LoopStart;
00356     LastX = (Loop->pos.x - Xmean) * RyInv;
00357     LastY = (Loop->pos.y - Ymean) * RxInv;
00358     LastX >>= (inT8) RyExp;
00359     LastY >>= (inT8) RxExp;
00360     OutLineIndex++;
00361 
00362     /* Check for bad loops */
00363     if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart))
00364       return FALSE;
00365     do {
00366       Segment = Loop;
00367       Loop = Loop->next;
00368       NormX = (Loop->pos.x - Xmean) * RyInv;
00369       NormY = (Loop->pos.y - Ymean) * RxInv;
00370       NormX >>= (inT8) RyExp;
00371       NormY >>= (inT8) RxExp;
00372 
00373       n = 1;
00374       if (!Segment->IsHidden()) {
00375         DeltaX = NormX - LastX;
00376         DeltaY = NormY - LastY;
00377         Length = MySqrt(DeltaX, DeltaY);
00378         n = ((Length << 2) + Length + 32) >> 6;
00379         if (n != 0) {
00380           Theta = BinaryAnglePlusPi(DeltaY, DeltaX);
00381           dX = (DeltaX << 8) / n;
00382           dY = (DeltaY << 8) / n;
00383           pfX = (LastX << 8) + (dX >> 1);
00384           pfY = (LastY << 8) + (dY >> 1);
00385           if (SaveFeature(CNFeat,
00386                           NumCNFeatures,
00387                           (inT16) (pfX >> 8),
00388                           (inT16) (pfY >> 8),
00389                           Theta) == FALSE)
00390             return FALSE;
00391           if (FeatureOutlineArray) {
00392             FeatureOutlineArray[NumCNFeatures] = OutLineIndex;
00393           }
00394           NumCNFeatures++;
00395           for (i = 1; i < n; i++) {
00396             pfX += dX;
00397             pfY += dY;
00398             if (SaveFeature(CNFeat,
00399                             NumCNFeatures,
00400                             (inT16) (pfX >> 8),
00401                             (inT16) (pfY >> 8),
00402                             Theta) == FALSE)
00403               return FALSE;
00404             if (FeatureOutlineArray) {
00405               FeatureOutlineArray[NumCNFeatures] = OutLineIndex;
00406             }
00407             NumCNFeatures++;
00408           }
00409         }
00410       }
00411       if (n != 0) {              /* Throw away a point that is too close */
00412         LastX = NormX;
00413         LastY = NormY;
00414       }
00415     }
00416     while (Loop != LoopStart);
00417     OutLine = OutLine->next;
00418   }
00419 
00420   Results->NumCN = NumCNFeatures;
00421   return TRUE;
00422 }
00423 
00424 
00425 /*--------------------------------------------------------------------------*/
00426 // Return the "binary angle" [0..255]
00427 //    made by vector <X, Y> as measured counterclockwise from <-1, 0>
00428 // The order of the arguments follows the convention of atan2(3)
00429 uinT8 BinaryAnglePlusPi(inT32 Y, inT32 X) {
00430   inT16 Angle, Atan;
00431   uinT16 Ratio;
00432   uinT32 AbsX, AbsY;
00433 
00434   assert ((X != 0) || (Y != 0));
00435   if (X < 0)
00436     AbsX = -X;
00437   else
00438     AbsX = X;
00439   if (Y < 0)
00440     AbsY = -Y;
00441   else
00442     AbsY = Y;
00443   if (AbsX > AbsY)
00444     Ratio = AbsY * ATAN_TABLE_SIZE / AbsX;
00445   else
00446     Ratio = AbsX * ATAN_TABLE_SIZE / AbsY;
00447   if (Ratio >= ATAN_TABLE_SIZE)
00448     Ratio = ATAN_TABLE_SIZE - 1;
00449   Atan = AtanTable[Ratio];
00450   if (X >= 0)
00451     if (Y >= 0)
00452       if (AbsX > AbsY)
00453         Angle = Atan;
00454       else
00455         Angle = 64 - Atan;
00456     else if (AbsX > AbsY)
00457       Angle = 256 - Atan;
00458     else
00459       Angle = 192 + Atan;
00460   else if (Y >= 0)
00461     if (AbsX > AbsY)
00462       Angle = 128 - Atan;
00463     else
00464       Angle = 64 + Atan;
00465   else if (AbsX > AbsY)
00466     Angle = 128 + Atan;
00467   else
00468     Angle = 192 - Atan;
00469 
00470   /* reverse angles to match old feature extractor:   Angle += PI */
00471   Angle += 128;
00472   Angle &= 255;
00473   return (uinT8) Angle;
00474 }
00475 
00476 
00477 /*--------------------------------------------------------------------------*/
00478 int SaveFeature(INT_FEATURE_ARRAY FeatureArray,
00479                 uinT16 FeatureNum,
00480                 inT16 X,
00481                 inT16 Y,
00482                 uinT8 Theta) {
00483   INT_FEATURE Feature;
00484 
00485   if (FeatureNum >= MAX_NUM_INT_FEATURES)
00486     return FALSE;
00487 
00488   Feature = &(FeatureArray[FeatureNum]);
00489 
00490   X = X + 128;
00491   Y = Y + 128;
00492 
00493   Feature->X = ClipToRange<inT16>(X, 0, 255);
00494   Feature->Y = ClipToRange<inT16>(Y, 0, 255);
00495   Feature->Theta = Theta;
00496   Feature->CP_misses = 0;
00497 
00498   return TRUE;
00499 }
00500 
00501 
00502 /*---------------------------------------------------------------------------*/
00503 // Return floor(sqrt(min(emm, x)^2 + min(emm, y)^2))
00504 //    where emm = EvidenceMultMask.
00505 uinT16 MySqrt(inT32 X, inT32 Y) {
00506   register uinT16 SqRoot;
00507   register uinT32 Square;
00508   register uinT16 BitLocation;
00509   register uinT32 Sum;
00510   const uinT32 EvidenceMultMask =
00511     ((1 << IntegerMatcher::kIntEvidenceTruncBits) - 1);
00512 
00513   if (X < 0)
00514     X = -X;
00515   if (Y < 0)
00516     Y = -Y;
00517 
00518   if (X > EvidenceMultMask)
00519     X = EvidenceMultMask;
00520   if (Y > EvidenceMultMask)
00521     Y = EvidenceMultMask;
00522 
00523   Sum = X * X + Y * Y;
00524 
00525   BitLocation = (EvidenceMultMask + 1) << 1;
00526   SqRoot = 0;
00527   do {
00528     Square = (SqRoot | BitLocation) * (SqRoot | BitLocation);
00529     if (Square <= Sum)
00530       SqRoot |= BitLocation;
00531     BitLocation >>= 1;
00532   }
00533   while (BitLocation);
00534 
00535   return SqRoot;
00536 }
00537 
00538 
00539 /*--------------------------------------------------------------------------*/
00540 // Return two integers which can be used to express the sqrt(I/N):
00541 //   sqrt(I/N) = 51.2 * 2^(*Exp) / retval
00542 uinT8 MySqrt2(uinT16 N, uinT32 I, uinT8 *Exp) {
00543   register inT8 k;
00544   register uinT32 N2;
00545   register uinT8 SqRoot;
00546   register uinT16 Square;
00547   register uinT8 BitLocation;
00548   register uinT16 Ratio;
00549 
00550   N2 = N * 41943;
00551 
00552   k = 9;
00553   while ((N2 & 0xc0000000) == 0) {
00554     N2 <<= 2;
00555     k += 1;
00556   }
00557 
00558   while ((I & 0xc0000000) == 0) {
00559     I <<= 2;
00560     k -= 1;
00561   }
00562 
00563   if (((N2 & 0x80000000) == 0) && ((I & 0x80000000) == 0)) {
00564     N2 <<= 1;
00565     I <<= 1;
00566   }
00567 
00568   N2 &= 0xffff0000;
00569   I >>= 14;
00570   Ratio = N2 / I;
00571 
00572   BitLocation = 128;
00573   SqRoot = 0;
00574   do {
00575     Square = (SqRoot | BitLocation) * (SqRoot | BitLocation);
00576     if (Square <= Ratio)
00577       SqRoot |= BitLocation;
00578     BitLocation >>= 1;
00579   }
00580   while (BitLocation);
00581 
00582   if (k < 0) {
00583     *Exp = 0;
00584     return 255;
00585   }
00586   else {
00587     *Exp = k;
00588     return SqRoot;
00589   }
00590 }
00591 
00592 
00593 /*-------------------------------------------------------------------------*/
00594 void ClipRadius(uinT8 *RxInv, uinT8 *RxExp, uinT8 *RyInv, uinT8 *RyExp) {
00595   register uinT8 AM, BM, AE, BE;
00596   register uinT8 BitN, LastCarry;
00597   int RxInvLarge, RyInvSmall;
00598 
00599   AM = classify_radius_gyr_min_man;
00600   AE = classify_radius_gyr_min_exp;
00601   BM = *RxInv;
00602   BE = *RxExp;
00603   LastCarry = 1;
00604   while ((AM != 0) || (BM != 0)) {
00605     if (AE > BE) {
00606       BitN = LastCarry + (AM & 1) + 1;
00607       AM >>= 1;
00608       AE--;
00609     }
00610     else if (AE < BE) {
00611       BitN = LastCarry + (!(BM & 1));
00612       BM >>= 1;
00613       BE--;
00614     }
00615     else {                       /* AE == BE */
00616       BitN = LastCarry + (AM & 1) + (!(BM & 1));
00617       AM >>= 1;
00618       BM >>= 1;
00619       AE--;
00620       BE--;
00621     }
00622     LastCarry = (BitN & 2) > 1;
00623     BitN = BitN & 1;
00624   }
00625   BitN = LastCarry + 1;
00626   LastCarry = (BitN & 2) > 1;
00627   BitN = BitN & 1;
00628 
00629   if (BitN == 1) {
00630     *RxInv = classify_radius_gyr_min_man;
00631     *RxExp = classify_radius_gyr_min_exp;
00632   }
00633 
00634   AM = classify_radius_gyr_min_man;
00635   AE = classify_radius_gyr_min_exp;
00636   BM = *RyInv;
00637   BE = *RyExp;
00638   LastCarry = 1;
00639   while ((AM != 0) || (BM != 0)) {
00640     if (AE > BE) {
00641       BitN = LastCarry + (AM & 1) + 1;
00642       AM >>= 1;
00643       AE--;
00644     }
00645     else if (AE < BE) {
00646       BitN = LastCarry + (!(BM & 1));
00647       BM >>= 1;
00648       BE--;
00649     }
00650     else {                       /* AE == BE */
00651       BitN = LastCarry + (AM & 1) + (!(BM & 1));
00652       AM >>= 1;
00653       BM >>= 1;
00654       AE--;
00655       BE--;
00656     }
00657     LastCarry = (BitN & 2) > 1;
00658     BitN = BitN & 1;
00659   }
00660   BitN = LastCarry + 1;
00661   LastCarry = (BitN & 2) > 1;
00662   BitN = BitN & 1;
00663 
00664   if (BitN == 1) {
00665     *RyInv = classify_radius_gyr_min_man;
00666     *RyExp = classify_radius_gyr_min_exp;
00667   }
00668 
00669   AM = classify_radius_gyr_max_man;
00670   AE = classify_radius_gyr_max_exp;
00671   BM = *RxInv;
00672   BE = *RxExp;
00673   LastCarry = 1;
00674   while ((AM != 0) || (BM != 0)) {
00675     if (AE > BE) {
00676       BitN = LastCarry + (AM & 1) + 1;
00677       AM >>= 1;
00678       AE--;
00679     }
00680     else if (AE < BE) {
00681       BitN = LastCarry + (!(BM & 1));
00682       BM >>= 1;
00683       BE--;
00684     }
00685     else {                       /* AE == BE */
00686       BitN = LastCarry + (AM & 1) + (!(BM & 1));
00687       AM >>= 1;
00688       BM >>= 1;
00689       AE--;
00690       BE--;
00691     }
00692     LastCarry = (BitN & 2) > 1;
00693     BitN = BitN & 1;
00694   }
00695   BitN = LastCarry + 1;
00696   LastCarry = (BitN & 2) > 1;
00697   BitN = BitN & 1;
00698 
00699   if (BitN == 1)
00700     RxInvLarge = 1;
00701   else
00702     RxInvLarge = 0;
00703 
00704   AM = *RyInv;
00705   AE = *RyExp;
00706   BM = classify_radius_gyr_max_man;
00707   BE = classify_radius_gyr_max_exp;
00708   LastCarry = 1;
00709   while ((AM != 0) || (BM != 0)) {
00710     if (AE > BE) {
00711       BitN = LastCarry + (AM & 1) + 1;
00712       AM >>= 1;
00713       AE--;
00714     }
00715     else if (AE < BE) {
00716       BitN = LastCarry + (!(BM & 1));
00717       BM >>= 1;
00718       BE--;
00719     }
00720     else {                       /* AE == BE */
00721       BitN = LastCarry + (AM & 1) + (!(BM & 1));
00722       AM >>= 1;
00723       BM >>= 1;
00724       AE--;
00725       BE--;
00726     }
00727     LastCarry = (BitN & 2) > 1;
00728     BitN = BitN & 1;
00729   }
00730   BitN = LastCarry + 1;
00731   LastCarry = (BitN & 2) > 1;
00732   BitN = BitN & 1;
00733 
00734   if (BitN == 1)
00735     RyInvSmall = 1;
00736   else
00737     RyInvSmall = 0;
00738 
00739   if (RxInvLarge && RyInvSmall) {
00740     *RyInv = classify_radius_gyr_max_man;
00741     *RyExp = classify_radius_gyr_max_exp;
00742   }
00743 
00744 }