File: field.c

package info (click to toggle)
ras 1.03-2.1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k, lenny
  • size: 264 kB
  • ctags: 113
  • sloc: ansic: 1,085; sh: 481; makefile: 68
file content (261 lines) | stat: -rw-r--r-- 9,087 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
    ras - Redundant Archive System
    Copyright (C) 1999  Nick Cleaton

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

    Nick Cleaton <nick@cleaton.net>
*/
/*
 * The magic of Lagrange polynomial interpolation...
 *
 * Given (x1,y1), (x2,y2)...,(xn,yn) and x0, you want to find y0.
 * This is hardly unique unless you specify that you want a polynomial in x
 * fitted to the given points and evaluated at x0.  If the polynomial is
 * constrained to be of degree <= n-1 (in the unknown x), then it is unique.
 *
 * We use this (bytewise) to make sumfiles by letting the bytes of the
 * segfiles be y1,y2, ...yn (the x values being 0,1,...n-1) and 
 * evaluating the polynomial at other x values (n to 255) to get the
 * bytes of the segfiles. The segfile of sum ID m is generated using the
 * x value (m + segcount).
 *
 * The easy way to find this polynomial is to use a set of degree n-1 basis
 * polynomials bi(x) which are 1 at a single xi and 0 at all the other
 * xj, j != i.  (Needless to say, this is only possible if xi != xj.)
 * Then you can just evaluate y = b1(x)*y1 + ... + bn(x)*yn.
 *
 * The zeroness is assured by including the factors (x-x1), ... (x-xn)
 * in the numerator of the polynomial bi(x), omitting only (x-xi).
 * This makes a polynomial of degree n-1 in x, as desired.  Ensuring
 * that bi(xi) == 1 requires dividing by the appropriate constant,
 * (xi-x1)*...*(xi-xn), the numerator evaluated at xi, again omitting the
 * (xi-xi) term.
 *
 * In more concrete terms,
 *
 *                   (x0-x2)*(x0-x3)*(x0-x4)*...*(x0-xn)
 * y0 = y1 * -------------------------------------------
 *                   (x1-x2)*(x1-x3)*(x1-x4)*...*(x1-xn)
 *
 *           (x0-x1)*        (x0-x3)*(x0-x4)*...*(x0-xn)
 *    + y2 * -------------------------------------------
 *           (x2-x0)*        (x2-x3)*(x2-x4)*...*(x2-xn)
 * 
 *    + ...
 *
 *           (x0-x1)*(x0-x2)*(x0-x3)*(x0-x4)*...
 *    + yn * -------------------------------------------
 *           (xn-x0)*(xn-x2)*(xn-x3)*(xn-x4)*...
 *
 * It turns out to be easier in practice to pre-compute (x0-x1)*...*(x0-xn)
 * and then add the appropriate (x0-xi) term to the denominator to cancel
 * the unneeded numerator term.
 *
 * For this arithmetic to work a finite field is needed. F_256 is
 * convenient, as each number occupies one byte (the field_t type).
 * Multiplication is done through log and antilog tables, f_log[] and
 * f_exp[].  The f_exp[] * table is double-sized so that the sum of two
 * logs can be looked up in it without range reduction, although larger
 * accumulations require reduction modulo the order of the multiplicative
 * group, namely 256-1 = 255.
 *
 * Most compilers can optimize division and remainder by a power
 * of 2, but this one-off is usually handled by a general division,
 * which is slow on most machines.  There is a much faster version,
 * using the fact that x*FIELD_SIZE == x (mod FIELD_SIZE-1).  Thus,
 * x == (x%FIELD_SIZE) + (x/FIELD_SIZE) (mod FIELD_SIZE-1), using
 * truncating integer division.  This obviously reduces the range of
 * the output x.  It can't reduce it below 0..FIELD_SIZE-1, but
 * the antilog table is already big enough for that.
 *
 * The smallest x which is mapped to FIELD_SIZE or larger under this
 * reduction is 2*FIELD_SIZE-1.  The smallest x which is reduced to
 * *that* or larger than FIELD_SIZE^2 - 1.  So adding up
 * FIELD_SIZE+1 entries with all values of FIELD_SIZE-1 will require a
 * third iteration to achieve complete reduction, but less than that (all
 * that we ever do here) requires only two.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include "common.h"
#include "utils.h"
#include "field.h"

#define FIELD_SIZE ((size_t)256)
#define FIELD_POLY 0x169

/*
 * The possible primitive polynomials of degree 8 are
 * 0x11d, 0x12b, 0x12d, 0x14d, 0x15f, 0x163, 0x165, 0x169,
 * 0x171, 0x187, 0x18d, 0x1a9, 0x1c3, 0x1cf, 0x1e7, 0x1f5
 * Other polynomials (irreducible but not primitive) are possible
 * if you use something other than x as the generator.
 */

static field_t *f_exp;
static field_t *f_log;
static field_t *f_mult;

/**************************************************************************/

/*
 * Initialize the f_exp and f_log arrays and the multiplicative lookup
 * table.
 */
void init_field()
{
   unsigned i, j, x;
   field_t prod;

   f_exp = Malloc( 2*FIELD_SIZE           * sizeof(*f_exp)  );
   f_log = Malloc( FIELD_SIZE             * sizeof(*f_log)  );
   f_mult = Malloc( FIELD_SIZE*FIELD_SIZE * sizeof(*f_mult) );
   
   /*
   ** Fill in the log and antilog tables
   */
   x = 1;
   for (i = 0; i < FIELD_SIZE-1; i++) {
      f_exp[i] = x;
      f_exp[i+FIELD_SIZE-1] = x;
      f_log[x] = i;
      x <<= 1;
      if (x & FIELD_SIZE)
	 x ^= FIELD_POLY;
   }
   /* x should be 1 here */
   f_exp[2*FIELD_SIZE-2] = f_exp[0];
   f_exp[2*FIELD_SIZE-1] = f_exp[1];
   f_log[0] = i;   /* Bogus value, FIELD_SIZE-1 */

   /*
   ** Fill in the multiplication table
   */
   for( i=0 ; i<FIELD_SIZE ; i++ )
   {  f_mult[FIELD_SIZE*i + 0] = 0;
      f_mult[FIELD_SIZE*0 + i] = 0;
   }
   for( j=1 ; j<FIELD_SIZE ; j++ )
   {  for( i=j ; i<FIELD_SIZE ; i++ )
      {  prod = f_exp[ f_log[i] + f_log[j] ];
         f_mult[FIELD_SIZE*i + j] = prod;
	 f_mult[FIELD_SIZE*j + i] = prod;
      }
   }
}

/**************************************************************************/

/* Just to make the application clear... */
#define f_add(x,y) ((x)^(y))
#define f_sub(x,y) f_add(x,y)

/*
 * This computes the coefficients used in Lagrange polynomial 
 * interpolation, returning the vector of b1(xtarget), b2(xtarget), ...,
 * bn(xtarget).
 * The y value at xtarget can be computed by taking the dot product of
 * the returned coefficient vector with the y values.
 */
field_t *Makevector(size_t segcount, int *gotpoints, int wantpoint)
{
   field_t *vec;
   field_t xi, xj;
   int i, j;
   unsigned long numer, denom;
   
   vec = Malloc(segcount * sizeof(*vec));

   /* First, accumulate the numerator, Prod(wantpoint-gotpoints[i],i=0..n) */
   numer = 0;
   for (i = 0; i < segcount; i++)
      numer += f_log[f_sub(gotpoints[i],wantpoint)];
   /* Preliminary partial reduction */
   numer = (numer%FIELD_SIZE) + (numer/FIELD_SIZE);

   /* Then, for each coefficient, compute the corresponding denominator */
   for (i = 0; i < segcount; i++) {
      xi = gotpoints[i];
      denom = 0;
      for (j = 0; j < segcount; j++) {
         xj = (i == j) ? wantpoint : gotpoints[j];
         if (xi == xj)
         {  fprintf(stderr,PROGNAME
	                   ": internal fault: indistinct xi in Makevector\n");
	    exit_because_of_error(INTERNAL_FAILURE);
	 }
         denom += f_log[f_sub(xi,xj)];
      }
      denom = (denom%FIELD_SIZE)+(denom/FIELD_SIZE);
      /* 0 <= denom < 2*FIELD_SIZE-1. */

      /* Now find numer/denom.  In log form, that's a subtract. */
      denom = numer + 2*FIELD_SIZE-2 - denom;
      denom = (denom%FIELD_SIZE)+(denom/FIELD_SIZE);
      denom = (denom%FIELD_SIZE)+(denom/FIELD_SIZE);

      vec[i] = f_exp[denom];
   }

   return vec;
}

/**************************************************************************/

void free_field()
{
   free( f_mult );
   free( f_log );
   free( f_exp );
}

/**************************************************************************/

void dotproduct(size_t segcount, uchar *inbufbase, size_t inbufoffset,
                                   uchar *outbuf, field_t *vector, size_t bytes)
{
   int i, n;
   unsigned char *src, *dst, *mult;
   
   /*  Start by making the output vector[0] multiplied by the first input  */
   mult = f_mult + (vector[0] * FIELD_SIZE);
   /*  mult now points to the base of a table of size FIELD_SIZE for
       multiplication by vector[0].                        */
   src = inbufbase;
   dst = outbuf;
   n = bytes;
   while(n--)
      *dst++ = mult[ *src++ ];

   /*  Continue by adding vector[i] multiplied by the ith input to the
       output for i>0 
   */
   for( i=1 ; i<segcount ; i++ )
   {  mult = f_mult + (vector[i] * FIELD_SIZE);
      /*  mult now points to the base of a table of size FIELD_SIZE for
          multiplication by vector[i].                        */
      src = inbufbase + (i * inbufoffset);
      dst = outbuf;
      n = bytes;
      while(n--)
         *dst++ ^= mult[ *src++ ];
   }
}

/**************************************************************************/