reedsolomon.h

Go to the documentation of this file.
00001 // This file is part of par2cmdline (a PAR 2.0 compatible file verification and
00002 // repair tool). See https://parchive.sourceforge.net for details of PAR 2.0.
00003 //
00004 // Copyright (c) 2003 Peter Brian Clements
00005 //
00006 // par2cmdline is free software; you can redistribute it and/or modify
00007 // it under the terms of the GNU General Public License as published by
00008 // the Free Software Foundation; either version 2 of the License, or
00009 // (at your option) any later version.
00010 //
00011 // par2cmdline is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014 // GNU General Public License for more details.
00015 //
00016 // You should have received a copy of the GNU General Public License
00017 // along with this program; if not, write to the Free Software
00018 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00019 
00020 #ifndef __REEDSOLOMON_H__
00021 #define __REEDSOLOMON_H__
00022 
00023 // The ReedSolomon object is used to calculate and store the matrix
00024 // used during recovery block creation or data block reconstruction.
00025 //
00026 // During initialisation, one RSOutputRow object is created for each
00027 // recovery block that either needs to be created or is available for
00028 // use.
00029 
00030 class RSOutputRow
00031 {
00032 public:
00033   RSOutputRow(void) {};
00034   RSOutputRow(bool _present, u16 _exponent) : present(_present), exponent(_exponent) {}
00035 
00036 public:
00037   bool present;
00038   u16 exponent;
00039 };
00040 
00041 templateclass g>
00042 class ReedSolomon
00043 {
00044 public:
00045   typedef g G;
00046 
00047   ReedSolomon(void);
00048   ~ReedSolomon(void);
00049 
00050   // Set which input blocks are present or missing
00051   bool SetInput(const vector &present); // Some input blocks are present
00052   bool SetInput(u32 count);                   // All input blocks are present
00053 
00054   // Set which output block are available or need to be computed
00055   bool SetOutput(bool present, u16 exponent);
00056   bool SetOutput(bool present, u16 lowexponent, u16 highexponent);
00057 
00058   // Compute the RS Matrix
00059   bool Compute(CommandLine::NoiseLevel noiselevel);
00060 
00061   // Process a block of data
00062   bool Process(size_t size,             // The size of the block of data
00063                u32 inputindex,          // The column in the RS matrix
00064                const void *inputbuffer, // Buffer containing input data
00065                u32 outputindex,         // The row in the RS matrix
00066                void *outputbuffer);     // Buffer containing output data
00067 
00068 private:
00069   bool InternalProcess(const g &factor, size_t size, const void *inputbuffer, void *outputbuffer);      // Optimization
00070 
00071 protected:
00072   // Perform Gaussian Elimination
00073   bool GaussElim(CommandLine::NoiseLevel noiselevel,
00074                  unsigned int rows, 
00075                  unsigned int leftcols, 
00076                  G *leftmatrix, 
00077                  G *rightmatrix, 
00078                  unsigned int datamissing);
00079 
00080 protected:
00081   u32 inputcount;        // Total number of input blocks
00082 
00083   u32 datapresent;       // Number of input blocks that are present 
00084   u32 datamissing;       // Number of input blocks that are missing
00085   u32 *datapresentindex; // The index numbers of the data blocks that are present
00086   u32 *datamissingindex; // The index numbers of the data blocks that are missing
00087 
00088   typename G::ValueType *database;// The "base" value to use for each input block
00089 
00090   u32 outputcount;       // Total number of output blocks
00091 
00092   u32 parpresent;        // Number of output blocks that are present
00093   u32 parmissing;        // Number of output blocks that are missing
00094   u32 *parpresentindex;  // The index numbers of the output blocks that are present
00095   u32 *parmissingindex;  // The index numbers of the output blocks that are missing
00096 
00097   vector outputrows; // Details of the output blocks
00098 
00099   G *leftmatrix;    // The main matrix
00100 
00101   // When the matrices are initialised: values of the form base ^ exponent are
00102   // stored (where the base values are obtained from database[] and the exponent
00103   // values are obtained from outputrows[]).
00104 
00105 #ifdef LONGMULTIPLY
00106   GaloisLongMultiplyTable *glmt;  // A multiplication table used by Process()
00107 #endif
00108 };
00109 
00110 templateclass g>
00111 inline ReedSolomon::ReedSolomon(void)
00112 {
00113   inputcount = 0;
00114 
00115   datapresent = 0;
00116   datamissing = 0;
00117   datapresentindex = 0;
00118   datamissingindex = 0;
00119   database = 0;
00120 
00121   outputrows.empty();
00122 
00123   outputcount = 0;
00124 
00125   parpresent = 0;
00126   parmissing = 0;
00127   parpresentindex = 0;
00128   parmissingindex = 0;
00129 
00130   leftmatrix = 0;
00131 
00132 #ifdef LONGMULTIPLY
00133   glmt = new GaloisLongMultiplyTable;
00134 #endif
00135 }
00136 
00137 templateclass g>
00138 inline ReedSolomon::~ReedSolomon(void)
00139 {
00140   delete [] datapresentindex;
00141   delete [] datamissingindex;
00142   delete [] database;
00143   delete [] parpresentindex;
00144   delete [] parmissingindex;
00145   delete [] leftmatrix;
00146 
00147 #ifdef LONGMULTIPLY
00148   delete glmt;
00149 #endif
00150 }
00151 
00152 templateclass g>
00153 inline bool ReedSolomon::Process(size_t size, u32 inputindex, const void *inputbuffer, u32 outputindex, void *outputbuffer)
00154 {
00155         // Optimization: it occurs frequently the function exits early on, so inline the start.
00156         // This resulted in a speed gain of approx. 8% in repairing.
00157 
00158         // Look up the appropriate element in the RS matrix
00159         g factor = leftmatrix[outputindex * (datapresent + datamissing) + inputindex];
00160         // Do nothing if the factor happens to be 0
00161         if (factor == 0 || 0 == size)
00162                 return eSuccess;
00163         return this->InternalProcess (factor, size, inputbuffer, outputbuffer);
00164 }
00165 
00166 u32 gcd(u32 a, u32 b);
00167 
00168 // Record whether the recovery block with the specified
00169 // exponent values is present or missing.
00170 templateclass g>
00171 inline bool ReedSolomon::SetOutput(bool present, u16 exponent)
00172 {
00173   // Store the exponent and whether or not the recovery block is present or missing
00174   outputrows.push_back(RSOutputRow(present, exponent));
00175 
00176   outputcount++;
00177 
00178   // Update the counts.
00179   if (present)
00180   {
00181     parpresent++;
00182   }
00183   else
00184   {
00185     parmissing++;
00186   }
00187 
00188   return true;
00189 }
00190 
00191 // Record whether the recovery blocks with the specified
00192 // range of exponent values are present or missing.
00193 templateclass g>
00194 inline bool ReedSolomon::SetOutput(bool present, u16 lowexponent, u16 highexponent)
00195 {
00196   for (unsigned int exponent=lowexponent; exponent00197   {
00198     if (!SetOutput(present, exponent))
00199       return false;
00200   }
00201 
00202   return true;
00203 }
00204 
00205 // Construct the Vandermonde matrix and solve it if necessary
00206 templateclass g>
00207 inline bool ReedSolomon::Compute(CommandLine::NoiseLevel noiselevel)
00208 {
00209   u32 outcount = datamissing + parmissing;
00210   u32 incount = datapresent + datamissing;
00211 
00212   if (datamissing > parpresent)
00213   {
00214     cerr "Not enough recovery blocks." 00215     return false;
00216   }
00217   else if (outcount == 0)
00218   {
00219     cerr "No output blocks." 00220     return false;
00221   }
00222 
00223   if (noiselevel > CommandLine::nlQuiet)
00224     cout "Computing Reed Solomon matrix." 00225 
00226   /* Layout of RS Matrix:
00227 
00228  parpresent
00229  datapresent datamissing datamissing parmissing
00230  / | \ / | \
00231  parpresent | (ppi[row])| | | (ppi[row])| |
00232  datamissing | ^ | I | | ^ | 0 |
00233  |(dpi[col]) | | |(dmi[col]) | |
00234  +---------------------+-------------+ +---------------------+-----------+
00235  | (pmi[row])| | | (pmi[row])| |
00236  parmissing | ^ | 0 | | ^ | I |
00237  |(dpi[col]) | | |(dmi[col]) | |
00238  \ | / \ | /
00239  */
00240 
00241   // Allocate the left hand matrix
00242 
00243   leftmatrix = new G[outcount * incount];
00244   memset(leftmatrix, 0, outcount * incount * sizeof(G));
00245 
00246   // Allocate the right hand matrix only if we are recovering
00247 
00248   G *rightmatrix = 0;
00249   if (datamissing > 0)
00250   {
00251     rightmatrix = new G[outcount * outcount];
00252     memset(rightmatrix, 0, outcount *outcount * sizeof(G));
00253   }
00254 
00255   // Fill in the two matrices:
00256 
00257   vector::const_iterator outputrow = outputrows.begin();
00258 
00259   // One row for each present recovery block that will be used for a missing data block
00260   for (unsigned int row=0; row00261   {
00262     if (noiselevel > CommandLine::nlQuiet)
00263     {
00264       int progress = row * 1000 / (datamissing+parmissing);
00265       cout "Constructing: " '.' "%\r" 00266     }
00267 
00268     // Get the exponent of the next present recovery block
00269     while (!outputrow->present)
00270     {
00271       outputrow++;
00272     }
00273     u16 exponent = outputrow->exponent;
00274 
00275     // One column for each present data block
00276     for (unsigned int col=0; coldatapresent; col++)
00277     {
00278       leftmatrix[row * incount + col] = G(database[datapresentindex[col]]).pow(exponent);
00279     }
00280     // One column for each each present recovery block that will be used for a missing data block
00281     for (unsigned int col=0; col00282     {
00283       leftmatrix[row * incount + col + datapresent] = (row == col) ? 1 : 0;
00284     }
00285 
00286     if (datamissing > 0)
00287     {
00288       // One column for each missing data block
00289       for (unsigned int col=0; col00290       {
00291         rightmatrix[row * outcount + col] = G(database[datamissingindex[col]]).pow(exponent);
00292       }
00293       // One column for each missing recovery block
00294       for (unsigned int col=0; col00295       {
00296         rightmatrix[row * outcount + col + datamissing] = 0;
00297       }
00298     }
00299 
00300     outputrow++;
00301   }
00302   // One row for each recovery block being computed
00303   outputrow = outputrows.begin();
00304   for (unsigned int row=0; row00305   {
00306     if (noiselevel > CommandLine::nlQuiet)
00307     {
00308       int progress = (row+datamissing) * 1000 / (datamissing+parmissing);
00309       cout "Constructing: " '.' "%\r" 00310     }
00311 
00312     // Get the exponent of the next missing recovery block
00313     while (outputrow->present)
00314     {
00315       outputrow++;
00316     }
00317     u16 exponent = outputrow->exponent;
00318 
00319     // One column for each present data block
00320     for (unsigned int col=0; coldatapresent; col++)
00321     {
00322       leftmatrix[(row+datamissing) * incount + col] = G(database[datapresentindex[col]]).pow(exponent);
00323     }
00324     // One column for each each present recovery block that will be used for a missing data block
00325     for (unsigned int col=0; col00326     {
00327       leftmatrix[(row+datamissing) * incount + col + datapresent] = 0;
00328     }
00329 
00330     if (datamissing > 0)
00331     {
00332       // One column for each missing data block
00333       for (unsigned int col=0; col00334       {
00335         rightmatrix[(row+datamissing) * outcount + col] = G(database[datamissingindex[col]]).pow(exponent);
00336       }
00337       // One column for each missing recovery block
00338       for (unsigned int col=0; col00339       {
00340         rightmatrix[(row+datamissing) * outcount + col + datamissing] = (row == col) ? 1 : 0;
00341       }
00342     }
00343 
00344     outputrow++;
00345   }
00346   if (noiselevel > CommandLine::nlQuiet)
00347     cout "Constructing: done." 00348 
00349   // Solve the matrices only if recovering data
00350   if (datamissing > 0)
00351   {
00352     // Perform Gaussian Elimination and then delete the right matrix (which 
00353     // will no longer be required).
00354     bool success = GaussElim(noiselevel, outcount, incount, leftmatrix, rightmatrix, datamissing);
00355     delete [] rightmatrix;
00356     return success;
00357   }
00358 
00359   return true;
00360 }
00361 
00362 // Use Gaussian Elimination to solve the matrices
00363 templateclass g>
00364 inline bool ReedSolomon::GaussElim(CommandLine::NoiseLevel noiselevel, unsigned int rows, unsigned int leftcols, G *leftmatrix, G *rightmatrix, unsigned int datamissing)
00365 {
00366   if (noiselevel == CommandLine::nlDebug)
00367   {
00368     for (unsigned int row=0; row00369     {
00370       cout "/"    : (row==rows-1) ? "\\"    : "|");
00371       for (unsigned int col=0; col00372       {
00373         cout " "
00374              8?4:2) '0')
00375              unsigned int)leftmatrix[row*leftcols+col];
00376       }
00377       cout " \\ /" : (row==rows-1) ? " / \\" : " | |");
00378       for (unsigned int col=0; col00379       {
00380         cout " "
00381              8?4:2) '0')
00382              unsigned int)rightmatrix[row*rows+col];
00383       }
00384       cout " \\"   : (row==rows-1) ? " /"    : " | |");
00385       cout 00386 
00387       cout ' ');
00388     }
00389   }
00390 
00391   // Because the matrices being operated on are Vandermonde matrices
00392   // they are guaranteed not to be singular.
00393 
00394   // Additionally, because Galois arithmetic is being used, all calulations
00395   // involve exact values with no loss of precision. It is therefore
00396   // not necessary to carry out any row or column swapping.
00397 
00398   // Solve one row at a time
00399 
00400   int progress = 0;
00401 
00402   // For each row in the matrix
00403   for (unsigned int row=0; row00404   {
00405     // NB Row and column swapping to find a non zero pivot value or to find the largest value
00406     // is not necessary due to the nature of the arithmetic and construction of the RS matrix.
00407 
00408     // Get the pivot value.
00409     G pivotvalue = rightmatrix[row * rows + row];
00410     assert(pivotvalue != 0);
00411     if (pivotvalue == 0)
00412     {
00413       cerr "RS computation error." 00414       return false;
00415     }
00416 
00417     // If the pivot value is not 1, then the whole row has to be scaled
00418     if (pivotvalue != 1)
00419     {
00420       for (unsigned int col=0; col00421       {
00422         if (leftmatrix[row * leftcols + col] != 0)
00423         {
00424           leftmatrix[row * leftcols + col] /= pivotvalue;
00425         }
00426       }
00427       rightmatrix[row * rows + row] = 1;
00428       for (unsigned int col=row+1; col00429       {
00430         if (rightmatrix[row * rows + col] != 0)
00431         {
00432           rightmatrix[row * rows + col] /= pivotvalue;
00433         }
00434       }
00435     }
00436 
00437     // For every other row in the matrix
00438     for (unsigned int row2=0; row200439     {
00440       if (noiselevel > CommandLine::nlQuiet)
00441       {
00442         int newprogress = (row*rows+row2) * 1000 / (datamissing*rows);
00443         if (progress != newprogress)
00444         {
00445           progress = newprogress;
00446           cout "Solving: " '.' "%\r" 00447         }
00448       }
00449 
00450       if (row != row2)
00451       {
00452         // Get the scaling factor for this row.
00453         G scalevalue = rightmatrix[row2 * rows + row];
00454 
00455         if (scalevalue == 1)
00456         {
00457           // If the scaling factor happens to be 1, just subtract rows
00458           for (unsigned int col=0; col00459           {
00460             if (leftmatrix[row * leftcols + col] != 0)
00461             {
00462               leftmatrix[row2 * leftcols + col] -= leftmatrix[row * leftcols + col];
00463             }
00464           }
00465 
00466           for (unsigned int col=row; col00467           {
00468             if (rightmatrix[row * rows + col] != 0)
00469             {
00470               rightmatrix[row2 * rows + col] -= rightmatrix[row * rows + col];
00471             }
00472           }
00473         }
00474         else if (scalevalue != 0)
00475         {
00476           // If the scaling factor is not 0, then compute accordingly.
00477           for (unsigned int col=0; col00478           {
00479             if (leftmatrix[row * leftcols + col] != 0)
00480             {
00481               leftmatrix[row2 * leftcols + col] -= leftmatrix[row * leftcols + col] * scalevalue;
00482             }
00483           }
00484 
00485           for (unsigned int col=row; col00486           {
00487             if (rightmatrix[row * rows + col] != 0)
00488             {
00489               rightmatrix[row2 * rows + col] -= rightmatrix[row * rows + col] * scalevalue;
00490             }
00491           }
00492         }
00493       }
00494     }
00495   }
00496   if (noiselevel > CommandLine::nlQuiet)
00497     cout "Solving: done." 00498   if (noiselevel == CommandLine::nlDebug)
00499   {
00500     for (unsigned int row=0; row00501     {
00502       cout "/"    : (row==rows-1) ? "\\"    : "|");
00503       for (unsigned int col=0; col00504       {
00505         cout " "
00506              8?4:2) '0')
00507              unsigned int)leftmatrix[row*leftcols+col];
00508       }
00509       cout " \\ /" : (row==rows-1) ? " / \\" : " | |");
00510       for (unsigned int col=0; col00511       {
00512         cout " "
00513              8?4:2) '0')
00514              unsigned int)rightmatrix[row*rows+col];
00515       }
00516       cout " \\"   : (row==rows-1) ? " /"    : " | |");
00517       cout 00518 
00519       cout ' ');
00520     }
00521   }
00522 
00523   return true;
00524 }
00525 
00526 
00527 #endif // __REEDSOLOMON_H__

Generated on Sun Oct 12 01:45:30 2008 for NNTPGrab by  1.5.4