source: PSPA/madxPSPA/tools/numdiff/src/ndiff.c @ 430

Last change on this file since 430 was 430, checked in by touze, 11 years ago

import madx-5.01.00

File size: 19.1 KB
Line 
1/*
2 o---------------------------------------------------------------------o
3 |
4 | Numdiff
5 |
6 | Copyright (c) 2012+ laurent.deniau@cern.ch
7 | Gnu General Public License
8 |
9 o---------------------------------------------------------------------o
10 
11   Purpose:
12     numerical diff of files
13     provides the main numdiff loop
14 
15 o---------------------------------------------------------------------o
16*/
17
18#include <stdlib.h>
19#include <string.h>
20#include <assert.h>
21#include <ctype.h>
22#include <math.h>
23
24#include "args.h"
25#include "ndiff.h"
26#include "constraint.h"
27#include "context.h"
28#include "error.h"
29#include "utils.h"
30
31#define T struct ndiff
32
33// ----- types
34
35struct ndiff {
36  // files
37  FILE *lhs_f, *rhs_f;
38  int   row_i,  col_i; // line, num-column
39
40  // context
41  struct context* cxt;
42
43  // options
44  int blank, check;
45
46  // diff counter
47  int   cnt_i, max_i;
48
49  // numbers counter
50  long  num_i;
51
52  // buffers
53  int   lhs_i,  rhs_i; // char-columns
54  int   buf_s;         // capacity
55  char *lhs_b, *rhs_b;
56};
57
58// ----- private (parser helpers)
59
60static inline int
61is_separator (int c)
62{
63  return !c || isblank(c) || (ispunct(c) && !strchr(option.chr, c));
64}
65
66static inline int
67is_number_start(char *buf, const char *beg)
68{
69  // number starts by a '-' or is at the beginning or is preceded by a separator
70  return *buf == '-' || *buf == '+' || buf == beg || (buf > beg && is_separator(buf[-1]));
71}
72
73static inline int
74is_number (char *buf)
75{
76  int i = 0;
77
78  // sign
79  if (buf[i] == '-' || buf[i] == '+') i++;
80
81  // dot
82  if (buf[i] == '.') ++i;
83
84  // digits
85  return isdigit(buf[i]);
86}
87
88static inline char*
89backtrace_number (char *buf, const char *beg)
90{
91  if (isdigit(*buf)) {
92    if (buf > beg && buf[-1] == '.') --buf;
93    if (buf > beg && buf[-1] == '-') --buf;
94  }
95
96  return buf;
97}
98
99static inline int
100parse_number (char *buf, int *d_, int *n_, int *e_, int *f_)
101{
102  int i = 0, d = 0, n = 0, e = 0, nz=0;
103  char c;
104
105  // sign
106  if (buf[i] == '+' || buf[i] == '-') i++;
107
108  // drop leading zeros
109  while(buf[i] == '0') i++;
110  if (isdigit(buf[i])) nz = 1;
111
112  // digits
113  while(isdigit(buf[i])) n += nz, i++;
114
115  // dot
116  if (buf[i] == '.') d = ++i;
117
118  // decimals
119  if (d) {
120    if (!nz) {
121      // drop leading zeros
122      while(buf[i] == '0') i++;
123      if (isdigit(buf[i])) nz = 1;
124    }
125
126    // digits
127    while(isdigit(buf[i])) n += nz, i++;
128  }
129
130  // ensure at least ±# or ±#. or ±.#
131  if(!(i > 0 && (isdigit(buf[i-1]) || (i > 1 &&  isdigit(buf[i-2])))))
132    return 0;
133
134  // exponent
135  if (buf[i] == 'e' || buf[i] == 'E' || buf[i] == 'd' || buf[i] == 'D')
136    c = buf[i], buf[i] = 'e', e = ++i;
137
138  if (e) {
139    // sign
140    if (buf[i] == '+' || buf[i] == '-') i++;
141
142    // digits
143    while(isdigit(buf[i])) i++;
144
145    // ensure e# or e±# otherwise backtrack
146    if (!isdigit(buf[i-1]))
147      i = e-1, buf[i] = c, e = 0;
148  }
149
150  if (n_) *n_ = n;
151  if (d_) *d_ = d-1;
152  if (e_) *e_ = e-1;
153  if (f_) *f_ = d > 0 || e > 0;
154
155  return i;
156}
157
158static inline void
159skip_identifier(char *restrict *lhs, char *restrict *rhs, int strict)
160{
161  if (strict)
162    while (**lhs == **rhs && !is_separator(**lhs)) ++*lhs, ++*rhs;
163  else {
164    while (!is_separator(**lhs)) ++*lhs;
165    while (!is_separator(**rhs)) ++*rhs;
166  }
167}
168
169static inline int
170is_valid_omit(const char *lhs_p, const char *rhs_p, const T *dif, const char *tag)
171{
172  const char *p = tag+strlen(tag);
173
174  while (--p >= tag && --lhs_p >= dif->lhs_b && --rhs_p >= dif->rhs_b)
175    if (*p != *lhs_p || *p != *rhs_p) return false;
176
177  return true;
178}
179
180// ----- private (ctor & dtor helpers)
181
182static inline void
183ndiff_setup (T *dif, int n)
184{
185  enum { min_alloc = 65536 };
186
187  if (n < min_alloc) n = min_alloc;
188
189  dif->lhs_b = malloc(n * sizeof *dif->lhs_b);
190  dif->rhs_b = malloc(n * sizeof *dif->rhs_b);
191  ensure(dif->lhs_b && dif->rhs_b, "out of memory");
192
193  *dif = (T) {
194    .lhs_f = dif->lhs_f, .rhs_f = dif->rhs_f,
195    .lhs_b = dif->lhs_b, .rhs_b = dif->rhs_b,
196    .blank = dif->blank, .check = dif->check,
197    .cxt = dif->cxt,     
198    .buf_s = n,
199    .max_i = 25
200  };
201}
202
203static inline void
204ndiff_teardown (T *dif)
205{
206  free(dif->lhs_b);
207  free(dif->rhs_b);
208
209  *dif = (T) {
210    .lhs_f = dif->lhs_f, .rhs_f = dif->rhs_f,
211    .blank = dif->blank, .check = dif->check,
212    .cxt = dif->cxt
213  };
214}
215
216static inline void
217ndiff_grow (T *dif, int n)
218{
219  if (n > dif->buf_s) { // enlarge on need
220    dif->lhs_b = realloc(dif->lhs_b, n * sizeof *dif->lhs_b);
221    dif->rhs_b = realloc(dif->rhs_b, n * sizeof *dif->rhs_b);
222    ensure(dif->lhs_b && dif->rhs_b, "out of memory");
223    dif->buf_s = n;
224  }
225}
226
227static inline void
228ndiff_reset_buf (T *dif)
229{
230  dif->lhs_i = dif->rhs_i = 0;
231  dif->lhs_b[0] = dif->rhs_b[0] = 0;
232}
233
234// ----- private (error helpers)
235
236static void
237ndiff_error(const struct context    *cxt,
238            const struct constraint *c,
239            const struct constraint *c2,
240            int row, int col)
241{
242  warning("dual constraints differ at %d:%d", row, col);
243  warning("getIncr select [#%d]", context_findIdx(cxt, c ));
244  warning("getAt   select [#%d]", context_findIdx(cxt, c2));
245  warning("rules list:");
246  context_print(cxt, stderr);
247  error("please report to mad@cern.ch");
248}
249
250static void
251ndiff_header(void)
252{
253  if (option.test)
254    warning("(*) files " CSTR_RED("%s") " from %s differ", option.indexed_filename, option.test);
255  else
256    warning("(*) files " CSTR_RED("%s") " differ", option.indexed_filename);
257}
258
259// -----------------------------------------------------------------------------
260// ----- interface
261// -----------------------------------------------------------------------------
262
263T*
264ndiff_alloc (FILE *lhs_f, FILE *rhs_f, struct context *cxt, int n_)
265{
266  assert(lhs_f && rhs_f);
267
268  T *dif = malloc(sizeof *dif);
269  ensure(dif, "out of memory");
270
271  *dif = (T) { .lhs_f = lhs_f, .rhs_f = rhs_f, .cxt = cxt };
272
273  ndiff_setup(dif, n_);
274  return dif;
275}
276
277void
278ndiff_free (T *dif)
279{
280  assert(dif);
281  ndiff_teardown(dif);
282  free(dif);
283}
284
285void
286ndiff_clear (T *dif)
287{
288  assert(dif);
289  ndiff_teardown(dif);
290  ndiff_setup(dif, 0);
291}
292
293int
294ndiff_skipLine (T *dif)
295{
296  assert(dif);
297  int s1 = 0, s2 = 0;
298  int c1, c2;
299
300  // trace("->skipLine line %d", dif->row_i);
301
302  ndiff_reset_buf(dif);
303
304  c1 = skipLine(dif->lhs_f, &s1);
305  c2 = skipLine(dif->rhs_f, &s2);
306
307  dif->col_i  = 0;
308  dif->row_i += 1;
309
310  // trace("<-skipLine line %d", dif->row_i);
311
312  return c1 == EOF || c2 == EOF ? EOF : !EOF;
313}
314
315int
316ndiff_readLine (T *dif)
317{
318  assert(dif);
319  int s1 = 0, s2 = 0;
320  int c1, c2, n = 0;
321
322  trace("->readLine line %d", dif->row_i);
323
324  ndiff_reset_buf(dif);
325
326  while (1) {
327    c1 = readLine(dif->lhs_f, dif->lhs_b+s1, dif->buf_s-s1, &n); s1 += n;
328    c2 = readLine(dif->rhs_f, dif->rhs_b+s2, dif->buf_s-s2, &n); s2 += n;
329    if (c1 == '\n' || c2 == '\n' || c1 == EOF || c2 == EOF) break;
330    ndiff_grow(dif, 2*dif->buf_s);
331  }
332
333  dif->col_i  = 0;
334  dif->row_i += 1;
335
336  trace("<-readLine line %d", dif->row_i);
337  trace("  buffers: '%.25s'|'%.25s'", dif->lhs_b, dif->rhs_b);
338
339  return c1 == EOF || c2 == EOF ? EOF : !EOF;
340}
341
342int
343ndiff_gotoLine (T *dif, const char *tag)
344{
345  assert(dif && tag);
346
347  int c1=0, c2=0, i1=0, i2=0;
348
349  trace("->gotoLine line %d", dif->row_i);
350
351  // lhs
352  while (1) {
353    int s = 0, n = 0;
354
355    dif->lhs_i    = 0;
356    dif->lhs_b[0] = 0;
357
358    if (c1 == EOF) break;
359
360    while (1) {
361      c1 = readLine(dif->lhs_f, dif->lhs_b+s, dif->buf_s-s, &n); s += n;
362      if (c1 == '\n' || c1 == EOF) break;
363      ndiff_grow(dif, 2*dif->buf_s);
364    }
365
366    i1 += 1;
367    trace("  lhs[%d]: '%s'", dif->row_i+i1, dif->lhs_b);
368
369    if (strstr(dif->lhs_b, tag)) break;
370  }
371
372  // rhs
373  while (1) {
374    int s = 0, n = 0;
375
376    dif->rhs_i    = 0;
377    dif->rhs_b[0] = 0;
378
379    if (c2 == EOF) break;
380
381    while (1) {
382      c2 = readLine(dif->rhs_f, dif->rhs_b+s, dif->buf_s-s, &n); s += n;
383      if (c2 == '\n' || c2 == EOF) break;
384      ndiff_grow(dif, 2*dif->buf_s);
385    }
386
387    i2 += 1;
388    trace("  rhs[%d]: '%s'", dif->row_i+i2, dif->rhs_b);
389
390    if (strstr(dif->rhs_b, tag)) break;
391  }
392
393  dif->col_i  = 0;
394  dif->row_i += i1 < i2 ? i1 : i2;
395
396  // return with last lhs and rhs lines loaded if tag was found
397
398  trace("<-gotoLine line %d (%+d|%+d)", dif->row_i, i1, i2);
399  trace("  buffers: '%.25s'|'%.25s'", dif->lhs_b, dif->rhs_b);
400
401  return c1 == EOF || c2 == EOF ? EOF : !EOF;
402}
403
404int
405ndiff_fillLine (T *dif, const char *lhs_b, const char *rhs_b)
406{
407  assert(dif);
408  assert(lhs_b && rhs_b);
409
410  ndiff_reset_buf(dif);
411
412  int s1 = strlen(lhs_b)+1; 
413  int s2 = strlen(rhs_b)+1; 
414  ndiff_grow(dif, imax(s1,s2));
415  memcpy(dif->lhs_b, lhs_b, s1);
416  memcpy(dif->rhs_b, rhs_b, s2);
417
418  dif->col_i  = 0;
419  dif->row_i += 1;
420
421  return 0; // never fails
422}
423
424void
425ndiff_diffLine (T *dif)
426{
427  assert(dif);
428
429  char *lhs_p = dif->lhs_b+dif->lhs_i;
430  char *rhs_p = dif->rhs_b+dif->rhs_i;
431
432  trace("->diffLine line %d char-column %d|%d", dif->row_i, dif->lhs_i, dif->rhs_i);
433  trace("  buffers: '%.25s'|'%.25s'", lhs_p, rhs_p);
434
435retry:
436
437  // fast search
438  if (!strcmp(lhs_p, rhs_p)) {
439    int n = strlen(lhs_p);
440    dif->lhs_i += n;
441    dif->rhs_i += n;
442    return;
443  }
444
445  // slow search (find index)
446  int i;
447  for(i = 0; lhs_p[i] == rhs_p[i]; i++) ;
448
449  lhs_p += i; dif->lhs_i += i;
450  rhs_p += i; dif->rhs_i += i;
451  i = 0;
452
453  if (dif->blank && (isblank(*lhs_p) || isblank(*rhs_p))) {
454    while (isblank(*lhs_p)) ++lhs_p, ++dif->lhs_i;
455    while (isblank(*rhs_p)) ++rhs_p, ++dif->rhs_i;
456    goto retry;
457  }
458
459  dif->lhs_i += 1;
460  dif->rhs_i += 1;
461  if (++dif->cnt_i <= dif->max_i) {
462    if (dif->cnt_i == 1) ndiff_header();
463    warning("(%d) files differ at line %d at char-column %d|%d",
464            dif->cnt_i, dif->row_i, dif->lhs_i, dif->rhs_i);
465    warning("(%d) strings: '%.25s'|'%.25s'", dif->cnt_i, lhs_p, rhs_p);
466  }
467
468  trace("<-diffLine line %d", dif->row_i);
469}
470
471int
472ndiff_nextNum (T *dif, const struct constraint *c)
473{
474  assert(dif);
475
476  char *restrict lhs_p = dif->lhs_b+dif->lhs_i;
477  char *restrict rhs_p = dif->rhs_b+dif->rhs_i;
478
479  trace("->nextNum line %d char-column %d|%d", dif->row_i, dif->lhs_i, dif->rhs_i);
480  trace("  strings: '%.25s'|'%.25s'", lhs_p, rhs_p);
481
482  if (ndiff_isempty(dif)) goto quit;
483
484retry:
485
486  // search for difference or digits
487  { int i = 0;
488
489    while (lhs_p[i] && lhs_p[i] == rhs_p[i] && !isdigit(lhs_p[i]))
490      i++;
491
492    lhs_p += i; rhs_p += i;
493  }
494
495  // skip whitespaces differences
496  if (dif->blank && (isblank(*lhs_p) || isblank(*rhs_p))) {
497    while (isblank(*lhs_p)) ++lhs_p;
498    while (isblank(*rhs_p)) ++rhs_p;
499    goto retry;
500  }
501
502  // end-of-line
503  if (!*lhs_p && !*rhs_p)
504    goto quit;
505
506  trace("  diff found for strings '%.25s'|'%.25s'", lhs_p, rhs_p);
507
508  // difference in not-a-number
509  if (*lhs_p != *rhs_p && (!is_number(lhs_p) || !is_number(rhs_p)))
510    goto quit_diff;
511
512  // backtrace number
513  lhs_p = backtrace_number(lhs_p, dif->lhs_b);
514  rhs_p = backtrace_number(rhs_p, dif->rhs_b);
515
516  trace("  backtracing to numbers '%.25s'|'%.25s'", lhs_p, rhs_p);
517
518  // at the start of a number?
519  if (!is_number_start(lhs_p, dif->lhs_b) || !is_number_start(rhs_p, dif->rhs_b)) {
520    int strict = true;
521    if (c->eps.cmd & eps_omit)
522      strict = !is_valid_omit(lhs_p, rhs_p, dif, c->eps.tag);
523    int j = strict ? 0 : strlen(c->eps.tag);
524    trace("  %s strings '%.25s'|'%.25s'", strict ? "skipping" : "omitting", lhs_p-j, rhs_p-j);
525    skip_identifier(&lhs_p, &rhs_p, strict);
526    if (!isdigit(*lhs_p)) goto retry;
527    goto quit_diff;
528  }
529
530  // numbers found
531  dif->lhs_i = lhs_p-dif->lhs_b;
532  dif->rhs_i = rhs_p-dif->rhs_b;
533  trace("<-nextNum line %d char-column %d|%d", dif->row_i, dif->lhs_i, dif->rhs_i);
534  trace("  strnums: '%.25s'|'%.25s'", lhs_p, rhs_p);
535  return ++dif->num_i, ++dif->col_i;
536
537quit_diff:
538  dif->lhs_i = lhs_p-dif->lhs_b+1;
539  dif->rhs_i = rhs_p-dif->rhs_b+1;
540  if (++dif->cnt_i <= dif->max_i) {
541    if (dif->cnt_i == 1) ndiff_header();
542    warning("(%d) files differ at line %d and char-columns %d|%d",
543            dif->cnt_i, dif->row_i, dif->lhs_i, dif->rhs_i);
544    warning("(%d) strings: '%.25s'|'%.25s'", dif->cnt_i, lhs_p, rhs_p);
545  }
546
547quit:
548  trace("<-nextNum line %d", dif->row_i);
549  dif->lhs_i = lhs_p-dif->lhs_b+1;
550  dif->rhs_i = rhs_p-dif->rhs_b+1;
551  return dif->col_i = 0;
552}
553
554int
555ndiff_testNum (T *dif, const struct constraint *c)
556{
557  assert(dif);
558
559  char *restrict lhs_p = dif->lhs_b+dif->lhs_i;
560  char *restrict rhs_p = dif->rhs_b+dif->rhs_i;
561  char *end;
562
563  double lhs_d, rhs_d, dif_a=0, min_a=0, pow_a=0;
564
565  // no constraint means equal
566  if (!c) {
567    static const struct constraint equ = { .eps = { .cmd = eps_equ } };
568    c = &equ;
569  }
570
571  trace("->testNum line %d char-column %d|%d", dif->row_i, dif->lhs_i, dif->rhs_i);
572  trace("  strnums: '%.25s'|'%.25s'", lhs_p, rhs_p);
573  trace("  rule [#%d, line %d]", context_findIdx(dif->cxt,c), context_findLine(dif->cxt,c));
574
575  // parse numbers
576  int d1=0, d2=0, n1=0, n2=0, e1=0, e2=0, f1=0, f2=0;
577  int l1 = parse_number(lhs_p, &d1, &n1, &e1, &f1);
578  int l2 = parse_number(rhs_p, &d2, &n2, &e2, &f2);
579  int ret = 0;
580
581  // missing numbers
582  if (!l1 || !l2) {
583    l1 = l2 = 20;
584    ret |= eps_ign;
585    goto quit_diff;
586  }
587
588  // ignore difference
589  if (c->eps.cmd & eps_ign) {
590    trace("  ignoring numbers '%.25s'|'%.25s'", lhs_p, rhs_p);
591    goto quit;
592  }
593
594  // omit difference
595  if (c->eps.cmd & eps_omit) {
596    if (is_valid_omit(lhs_p, rhs_p, dif, c->eps.tag)) {
597      trace("  omitting numbers '%.25s'|'%.25s'", lhs_p, rhs_p);
598      goto quit;
599    }
600  }
601
602  // strict comparison...
603  if (l1 == l2 && memcmp(lhs_p, rhs_p, l1) == 0)
604    goto quit;
605
606  // ...required
607  if ((c->eps.cmd & eps_equ) && !(c->eps.cmd & eps_dra)) {
608    ret |= eps_equ;
609    goto quit_diff;
610  }
611
612  // convert numbers
613  lhs_d = strtod(lhs_p, &end); assert(end == lhs_p+l1);
614  rhs_d = strtod(rhs_p, &end); assert(end == rhs_p+l2);
615  dif_a = fabs(lhs_d - rhs_d);
616  min_a = fmin(fabs(lhs_d),fabs(rhs_d));
617  pow_a = pow10(-imax(n1, n2));
618
619  // if one number is zero -> relative becomes absolute
620  if (!(min_a > 0)) min_a = 1.0;
621
622  // input-specific relative comparison (does not apply to integers)
623  if ((c->eps.cmd & eps_dig) && (f1 || f2))
624    if (dif_a > c->eps.dig * min_a * pow_a) ret |= eps_dig;
625
626  // relative comparison
627  if (c->eps.cmd & eps_rel)
628    if (dif_a > c->eps.rel * min_a) ret |= eps_rel;
629
630  // absolute comparison
631  if (c->eps.cmd & eps_abs)
632    if (dif_a > c->eps.abs) ret |= eps_abs;
633
634  if (!ret) goto quit;
635  if (c->eps.either && (ret & eps_dra) != (c->eps.cmd & eps_dra)) goto quit;
636
637quit_diff:
638  if (++dif->cnt_i <= dif->max_i) {
639    if (dif->cnt_i == 1) ndiff_header();
640    warning("(%d) files differ at line %d column %d between char-columns %d|%d and %d|%d",
641            dif->cnt_i, dif->row_i, dif->col_i, dif->lhs_i+1, dif->rhs_i+1, dif->lhs_i+1+l1, dif->rhs_i+1+l2);
642
643    char str[128];
644    sprintf(str, "(%%d) numbers: '%%.%ds'|'%%.%ds'", l1,l2);
645    warning(str, dif->cnt_i, lhs_p, rhs_p);
646
647    if (ret & eps_ign)
648      warning("(%d) one number is missing", dif->cnt_i);
649
650    if (ret & eps_equ)
651      warning("(%d) numbers strict representation differ", dif->cnt_i);
652
653    if (ret & eps_dig)
654      warning("(%d) numdigit error (rule #%d, line %d: rel=%g) |abs_err|=%.2g, |rel_err|=%.2g, ndig=%d",
655              dif->cnt_i, context_findIdx(dif->cxt, c), context_findLine(dif->cxt, c),
656              c->eps.dig*pow_a, dif_a, dif_a/min_a, imax(n1, n2));   
657 
658    if (ret & eps_rel)
659      warning("(%d) relative error (rule #%d, line %d: rel=%g) |abs_err|=%.2g, |rel_err|=%.2g, ndig=%d",
660              dif->cnt_i, context_findIdx(dif->cxt, c), context_findLine(dif->cxt, c),
661              c->eps.rel, dif_a, dif_a/min_a, imax(n1, n2));   
662
663    if (ret & eps_abs)
664      warning("(%d) absolute error (rule #%d, line %d: abs=%g) |abs_err|=%.2g, |rel_err|=%.2g, ndig=%d",
665              dif->cnt_i, context_findIdx(dif->cxt, c), context_findLine(dif->cxt, c),
666              c->eps.abs, dif_a, dif_a/min_a, imax(n1, n2));   
667  }
668  ret = 1;
669
670quit:
671  dif->lhs_i += l1;
672  dif->rhs_i += l2;
673
674  trace("<-testNum line %d char-column %d|%d", dif->row_i, dif->lhs_i, dif->rhs_i);
675  trace("  strnums: [%d|%d] '%.25s'|'%.25s'", l1, l2, lhs_p, rhs_p);
676
677  return ret;
678}
679
680void
681ndiff_option  (T *dif, const int *keep_, const int *blank_, const int *check_)
682{
683  assert(dif);
684 
685  if (keep_ ) dif->max_i = *keep_;
686  if (blank_) dif->blank = *blank_; 
687  if (check_) dif->check = *check_; 
688
689  ensure(dif->max_i > 0, "number of kept diff must be positive");
690}
691
692void
693ndiff_getInfo (const T *dif, int *row_, int *col_, int *cnt_, long *num_)
694{
695  assert(dif);
696
697  if (row_) *row_ = dif->row_i;
698  if (col_) *col_ = dif->col_i;
699  if (cnt_) *cnt_ = dif->cnt_i;
700  if (num_) *num_ = dif->num_i;
701}
702
703int
704ndiff_feof (const T *dif, int both)
705{
706  assert(dif);
707
708  return both ? feof(dif->lhs_f) && feof(dif->rhs_f)
709              : feof(dif->lhs_f) || feof(dif->rhs_f);
710}
711
712int
713ndiff_isempty (const T *dif)
714{
715  assert(dif);
716
717  return !dif->lhs_b[dif->lhs_i] && !dif->rhs_b[dif->rhs_i];
718}
719
720void
721ndiff_loop(T *dif)
722{
723  assert(dif);
724
725  const struct constraint *c, *c2;
726  int row=0, col;
727
728  while(!ndiff_feof(dif, 0)) {
729    ++row, col=0;
730
731    c = context_getInc(dif->cxt, row, col);
732    if (dif->check && c != (c2 = context_getAt(dif->cxt, row, col)))
733      ndiff_error(dif->cxt, c, c2, row, col);
734
735    // no constraint, diff-lines
736    if (!c) {
737      ndiff_readLine(dif);
738      if (!ndiff_isempty(dif))
739        ndiff_diffLine(dif);
740      continue;
741    }
742
743    // skip this line
744    if (c->eps.cmd & eps_skip) {
745      ndiff_skipLine(dif);
746      continue;
747    }
748
749    // goto or read line(s)
750    if (c->eps.cmd & eps_goto) {
751      ndiff_gotoLine(dif, c->eps.tag);
752      ndiff_getInfo(dif, &row, 0, 0, 0);
753    } else {
754      ndiff_readLine(dif);
755      if (ndiff_isempty(dif)) continue;
756    }
757
758    // for each number column, diff-chars between numbers
759    while((col = ndiff_nextNum(dif, c))) {
760      c = context_getInc(dif->cxt, row, col);
761      if (dif->check && c != (c2 = context_getAt(dif->cxt, row, col)))
762        ndiff_error(dif->cxt, c, c2, row, col); 
763
764      ndiff_testNum(dif, c);
765    }
766  }
767
768  if (dif->blank) {
769    skipSpace(dif->lhs_f, 0);
770    skipSpace(dif->rhs_f, 0);
771  }
772}
773
774#undef T
775
776// -----------------------------------------------------------------------------
777// ----- testsuite
778// -----------------------------------------------------------------------------
779
780#ifndef NTEST
781
782#include "utest.h"
783
784#define T struct ndiff
785
786// ----- debug
787
788// ----- teardown
789
790static T*
791ut_teardown(T *dif)
792{
793  ndiff_clear(dif);
794  return dif;
795}
796
797// ----- test
798
799static void 
800ut_testPow10(struct utest *utest, T* dif)
801{
802  (void)dif;
803
804  for (int k = -100; k < 100; k++) {
805    UTEST(pow10(k) == pow(10, k)); 
806//    if (pow10(k) != pow(10, k))
807//      fprintf(stderr, "pow10(%d)=%g != pow(10,%d)=%g, err=%g\n", k, pow10(k), k, pow(10,k), fabs(pow10(k)-pow(10,k)));
808  }
809}
810
811static void
812ut_testNul(struct utest *utest, T* dif)
813{
814  UTEST(dif != 0);
815}
816
817// ----- unit tests
818
819static struct spec {
820  const char *name;
821  T*        (*setup)   (T*);
822  void      (*test )   (struct utest*, T*);
823  T*        (*teardown)(T*);
824} spec[] = {
825  { "power of 10",                          0        , ut_testPow10, 0           },
826  { "empty input",                          0        , ut_testNul  , ut_teardown },
827};
828enum { spec_n = sizeof spec/sizeof *spec };
829
830// ----- interface
831
832void
833ndiff_utest(struct utest *ut)
834{
835  assert(ut);
836  T *dif = ndiff_alloc(stdout, stdout, 0, 0);
837
838  utest_title(ut, "File diff");
839
840  for (int k = 0; k < spec_n; k++) {
841    utest_init(ut, spec[k].name);
842    if (spec[k].setup)    dif = spec[k].setup(dif);
843    spec[k].test(ut, dif);
844    if (spec[k].teardown) dif = spec[k].teardown(dif);
845    utest_fini(ut);
846  }
847
848  ndiff_free(dif);
849}
850
851#endif
Note: See TracBrowser for help on using the repository browser.