source: PSPA/madxPSPA/tools/numdiff/src/context.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: 15.0 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     manage contexts of constraints
13     print, scan contexts from file
14 
15 o---------------------------------------------------------------------o
16*/
17
18#include <stdlib.h>
19#include <assert.h>
20
21#include "context.h"
22#include "constraint.h"
23
24#define T struct context
25#define C struct constraint
26
27// ----- types
28
29struct context {
30  // heaps of contraints
31  const C **fut; // future, sorted
32  const C **act; // active, sorted
33  const C **row; // active, sorted   
34  int fut_n, act_n, row_n;
35
36  // current status
37  int row_u, row_i, col_i;
38  bool sorted;
39
40  // storage
41  int dat_n, dat_sz;
42  C dat[];
43};
44
45// ----- forward decl
46
47static void
48ut_trace(const T *cxt, int i, int j, const C* cst1, const C* cst2);
49
50// ----- private (sort helpers)
51
52static inline int
53cmpCst (const void *cst1_, const void *cst2_)
54{
55  const C *cst1 = *(const void* const*)cst1_; 
56  const C *cst2 = *(const void* const*)cst2_;
57  const struct slice *row1 = &cst1->row;
58  const struct slice *row2 = &cst2->row;
59  const struct slice *col1 = &cst1->col;
60  const struct slice *col2 = &cst2->col;
61
62  // sorted by (> row start, > col start, > idx)
63  return slice_first(row1) < slice_first(row2) ?  1
64       : slice_first(row1) > slice_first(row2) ? -1
65       : slice_first(col1) < slice_first(col2) ?  1
66       : slice_first(col1) > slice_first(col2) ? -1
67       : cst1 < cst2 ? 1 : -1;
68}
69
70static inline int
71cmpRow (const C *cst1, const C *cst2)
72{
73  const struct slice *row1 = &cst1->row;
74  const struct slice *row2 = &cst2->row;
75
76  // sorted by (> row last, > idx)
77  return slice_last(row1) < slice_last(row2) ?  1
78       : slice_last(row1) > slice_last(row2) ? -1
79       : cst1 < cst2 ? 1 : -1;
80}
81
82// ----- private (ctor & dtor helpers)
83
84static inline void
85context_setup (T *cxt)
86{
87  cxt->fut = malloc(3 * cxt->dat_n * sizeof *cxt->fut);
88  ensure(cxt->fut, "out of memory");
89
90  cxt->act = cxt->fut + cxt->dat_n;
91  cxt->row = cxt->act + cxt->dat_n;
92  cxt->fut_n = cxt->dat_n;
93  cxt->act_n = cxt->row_n = 0;
94  cxt->row_i = cxt->col_i = 0;
95
96  for (int i = 0; i < cxt->fut_n; i++)
97    cxt->fut[i] = cxt->dat+i;
98
99  qsort(cxt->fut, cxt->fut_n, sizeof *cxt->fut, cmpCst);
100  cxt->sorted = true; 
101}
102
103static inline void
104context_teardown (T *cxt)
105{
106  free(cxt->fut);
107
108  *cxt = (T) {
109      .dat_n  = cxt->dat_n,
110      .dat_sz = cxt->dat_sz
111    }; 
112}
113
114static inline T*
115context_grow (T *cxt, int n)
116{
117  // enlarge on need
118  if (n > cxt->dat_sz) {
119    if (cxt->sorted == true)
120      context_teardown(cxt);
121
122    cxt = realloc(cxt, sizeof *cxt + n * sizeof *cxt->dat);
123    ensure(cxt, "out of memory");
124    cxt->dat_sz = n;
125  }
126
127  return cxt; 
128}
129
130// ----- private (eps helpers)
131
132static inline void
133context_updateAct (T *cxt, int row_i)
134{
135  // remove obsolete constraints
136  for (; cxt->act_n; --cxt->act_n) {
137    const C *act = cxt->act[cxt->act_n-1];
138    uint i = slice_last(&act->row);
139
140    if (i >= (uint)row_i) {
141      if (i < (uint)cxt->row_u) cxt->row_u = i;
142      break;
143    }
144  }
145
146  // select future constraints
147  for (; cxt->fut_n; --cxt->fut_n) {
148    const C *fut = cxt->fut[cxt->fut_n-1];
149    uint i = slice_first(&fut->row);
150
151    if (i > (uint)row_i) {         // not yet active
152      if (i < (uint)cxt->row_u) cxt->row_u = i;
153      break;
154    }
155
156    if (slice_last(&fut->row) < (uint)row_i) continue; // already obsolete
157
158    // insert future constraint
159    if (!cxt->act_n) *cxt->act = fut;
160    else {
161      const C **act = cxt->act+cxt->act_n-1;
162      for (; act >= cxt->act; --act) {
163        if (cmpRow(fut, *act) >= 0) break;
164        act[1] = act[0];
165      }
166      act[1] = fut;
167    }
168    ++cxt->act_n;
169  }
170}
171
172static inline void
173context_setupRow (T *cxt, int row_i)
174{
175  cxt->row_n = 0;
176
177  // select active constraints for this row
178  for (int i = 0; i < cxt->act_n; ++i) {
179    const C *act = cxt->act[i];
180    if (!slice_isEnum(&act->row, row_i)) continue; // not active
181
182    // skip|goto line always dominates
183    if (act->eps.cmd >= eps_skip) {
184      cxt->row[0] = act;
185      cxt->row_n  = 1;
186      return;
187    }
188
189    // add active constraint
190    cxt->row[cxt->row_n++] = act;
191  }
192}
193
194static inline const C*
195context_setupCol (T *cxt, int col_i)
196{
197  const C *cst = 0;
198
199  // select last-added active constraint for this col
200  for (int i = 0; i < cxt->row_n; ++i) {
201    const C *row = cxt->row[i];
202    if (row > cst && slice_isElem(&row->col, col_i)) cst = row;
203  }
204
205  return cst;
206}
207
208static inline const struct constraint*
209context_getIncCst (T *cxt, int row_i, int col_i)
210{
211  const C *cst = 0;
212
213  ensure(row_i >= cxt->row_i, "obsolete row");
214
215  if (row_i > cxt->row_i) {
216    if (row_i >= cxt->row_u)
217      context_updateAct(cxt, row_i);       // update active constraints
218
219    context_setupRow(cxt, row_i);          // setup constraints for this row
220
221    cxt->row_i = row_i;
222    cxt->col_i = 0;
223  }
224
225  ensure(col_i >= cxt->col_i, "obsolete column");
226
227  cst = context_setupCol(cxt, col_i);      // setup constraints for this col
228
229  cxt->col_i = col_i;
230
231  return cst;
232}
233
234static inline const struct constraint*
235context_getAtCst (T *cxt, int row_i, int col_i)
236{
237  const C *cur = cxt->dat+cxt->dat_n-1;
238  const C *cst = 0;
239
240  // select last-added active constraint, brute force...
241  for (; cur >= cxt->dat; --cur)
242    if (slice_isElem(&cur->row, row_i)) {
243      if (cur->eps.cmd >= eps_skip) return cur;
244      if (slice_isElem(&cur->col, col_i)) { cst = cur--; break; }
245    }
246
247  // check for pending skip|goto line
248  for (; cur >= cxt->dat; --cur)
249    if (cur->eps.cmd >= eps_skip && slice_isElem(&cur->row, row_i)) return cur;
250
251  return cst;
252}
253
254// -----------------------------------------------------------------------------
255// ----- interface
256// -----------------------------------------------------------------------------
257
258T*
259context_alloc (int n)
260{
261  enum { min_alloc = 128 };
262
263  if (n < min_alloc) n = min_alloc;
264
265  T *cxt = malloc(sizeof *cxt + n * sizeof *cxt->dat);
266  ensure(cxt, "out of memory");
267
268  *cxt = (T) { .dat_sz = n };
269
270  return cxt;
271}
272
273void
274context_clear (T *cxt)
275{
276  assert(cxt);
277  context_teardown(cxt);
278  cxt->dat_n = 0;
279}
280
281void
282context_free (T *cxt)
283{
284  assert(cxt);
285  context_teardown(cxt);
286  free(cxt);
287}
288
289T*
290context_add (T *cxt, const C *cst)
291{
292  assert(cxt && cst);
293
294  // check if in use
295  if (cxt->sorted == true)
296    context_teardown(cxt);
297
298  // check for storage space
299  if (cxt->dat_n == cxt->dat_sz)
300    cxt = context_grow(cxt, 1.75*cxt->dat_sz); // +75%
301
302  // add constraint
303  cxt->dat[cxt->dat_n++] = *cst;
304
305  // expand to all columns
306  if (cst->eps.cmd >= eps_skip)
307    cxt->dat[cxt->dat_n-1].col = slice_initAll();
308
309  return cxt;
310}
311
312const C*
313context_getAt (T *cxt, int row, int col)
314{
315  assert(cxt);
316  ensure(row > 0, "null row");
317
318  // check if ready for use
319  if (cxt->sorted == false)
320    context_setup(cxt);
321
322  return context_getAtCst(cxt, row, col);
323}
324
325const C*
326context_getInc (T *cxt, int row, int col)
327{
328  assert(cxt);
329  ensure(row > 0, "null row");
330
331  // check if ready for use
332  if (cxt->sorted == false)
333    context_setup(cxt);
334
335  return context_getIncCst(cxt, row, col);
336}
337
338const C*
339context_getIdx (const T *cxt, int idx)
340{
341  assert(cxt);
342  return idx < cxt->dat_n ? cxt->dat+idx : 0;
343}
344
345int
346context_findIdx (const T *cxt, const C *cst)
347{
348  assert(cxt);
349  return cst >= cxt->dat && cst < cxt->dat+cxt->dat_n ? cst-cxt->dat : -1;
350}
351
352int
353context_findLine (const T *cxt, const C *cst)
354{
355  assert(cxt);
356  return cst >= cxt->dat && cst < cxt->dat+cxt->dat_n ? cst->line : -1;
357}
358
359T*
360context_scan(T *cxt, FILE *fp)
361{
362  int row = 1;
363  C cst;
364
365  while(!feof(fp)) {
366    constraint_scan(&cst, fp, &row);
367    if (cst.eps.cmd != eps_invalid)
368      cxt = context_add(cxt, &cst);
369  }
370
371  return cxt;
372}
373
374void
375context_print(const T *cxt, FILE *fp)
376{
377  const C *c;
378
379  for (int i = 0; (c = context_getIdx(cxt, i)) != 0; i++) {
380    fprintf(fp,"[#%d:%d] ", i, c->line);
381    constraint_print(c, fp);
382    putc('\n', fp);
383  }
384}
385
386#undef T
387#undef C
388
389// -----------------------------------------------------------------------------
390// ----- testsuite
391// -----------------------------------------------------------------------------
392
393#ifndef NTEST
394
395#include "utest.h"
396
397#define T struct context
398#define C struct constraint
399
400enum { NROW = 5, NCOL = 5 };
401
402// ----- debug
403
404static void
405ut_trace(const T *cxt, int i, int j, const C* cst1, const C* cst2)
406{
407  fprintf(stderr, "(%d,%d)\n", i, j);
408  if (cst1) {
409    fprintf(stderr, "[%d].1: ", context_findIdx(cxt, cst1));
410    constraint_print(cst1, stderr);
411    putc('\n', stderr);
412  }
413  if (cst2) {
414    fprintf(stderr, "[%d].2: ", context_findIdx(cxt, cst2));
415    constraint_print(cst2, stderr);
416    putc('\n', stderr);
417  }
418  fprintf(stderr, "{F} ");
419  for(int k = 0; k < cxt->fut_n; k++)
420    fprintf(stderr, "%d ", context_findIdx(cxt, cxt->fut[k]));
421
422  fprintf(stderr, "\n{A} ");
423  for(int k = 0; k < cxt->act_n; k++)
424    fprintf(stderr, "%d ", context_findIdx(cxt, cxt->act[k]));
425
426  fprintf(stderr, "\n{R} ");
427  for(int k = 0; k < cxt->row_n; k++)
428    fprintf(stderr, "%d ", context_findIdx(cxt, cxt->row[k]));
429
430  putc('\n', stderr);
431}
432
433// ----- teardown
434
435static T*
436ut_teardown(T *cxt)
437{
438  context_clear(cxt);
439  return cxt;
440}
441
442// ----- test
443
444#if 0
445/* tests for speed:
446   - context_getAt  is much simpler but has bad complexity
447   - context_getInc is (almost) always faster and much more stable
448*/
449
450static void
451ut_testAt(struct utest *utest, T* cxt, int i, int j)
452{
453  const C* cst = context_getAt(cxt, i, j);
454  UTEST(cst || !cst);
455}
456
457static void
458ut_testInc(struct utest *utest, T* cxt, int i, int j)
459{
460  const C* cst = context_getInc(cxt, i, j);
461  UTEST(cst || !cst);
462}
463#endif
464
465static void
466ut_testNul(struct utest *utest, T* cxt, int i, int j)
467{
468  const C* cst1 = context_getAt (cxt, i, j);
469  const C* cst2 = context_getInc(cxt, i, j);
470
471  UTEST(!cst1 && !cst2);
472
473  if (cst1 || cst2)
474    ut_trace(cxt, i, j, cst1, cst2);
475}
476
477static void
478ut_testEqu(struct utest *utest, T* cxt, int i, int j)
479{
480  const C* cst1 = context_getAt (cxt, i, j);
481  const C* cst2 = context_getInc(cxt, i, j);
482
483  UTEST( (cst1 == cst2 ||
484          (cst1 && (cst1->eps.cmd & eps_skip) &&
485           cst2 && (cst2->eps.cmd & eps_skip)) ) );
486
487  if (cst1 != cst2)
488    ut_trace(cxt, i, j, cst1, cst2);
489}
490
491// ----- setup
492
493static T*
494ut_setup1(T *cxt)
495{
496  C cst;
497  struct eps eps = eps_init(eps_dig, 1);
498
499  // 3  2
500  cst = constraint_init(slice_init(3), slice_init(2), eps, 0);
501  cxt = context_add(cxt, &cst);
502
503  return cxt;
504}
505
506static T*
507ut_setup2(T *cxt)
508{
509  C cst;
510  struct eps eps = eps_init(eps_dig, 2);
511
512  // 1-2  2
513  cst = constraint_init(slice_initLast(1, 2), slice_init(2), eps, 0);
514  cxt = context_add(cxt, &cst);
515  // 3    2
516  cst = constraint_init(slice_init(3), slice_init(2), eps, 0);
517  cxt = context_add(cxt, &cst);
518  // 4-5  2
519  cst = constraint_init(slice_initSize(4, 2), slice_init(2), eps, 0);
520  cxt = context_add(cxt, &cst);
521
522  return cxt;
523}
524
525static T*
526ut_setup3(T *cxt)
527{
528  C cst;
529  struct eps eps = eps_init(eps_dig, 3);
530
531  // 2  1-2
532  cst = constraint_init(slice_init(2), slice_initSize(1, 2), eps, 0);
533  cxt = context_add(cxt, &cst);
534  // 2  3
535  cst = constraint_init(slice_init(2), slice_init(3), eps, 0);
536  cxt = context_add(cxt, &cst);
537  // 2  4-5
538  cst = constraint_init(slice_init(2), slice_initSize(4, 2), eps, 0);
539  cxt = context_add(cxt, &cst);
540
541  return cxt;
542}
543
544static T*
545ut_setup4(T *cxt)
546{
547  C cst;
548  struct eps eps = eps_init(eps_dig , 4);
549  struct eps skp = eps_init(eps_skip, 4);
550
551  // 2    4-5
552  cst = constraint_init(slice_init(2), slice_initSize(4, 2), eps, 0);
553  cxt = context_add(cxt, &cst);
554  // 1-2  2
555  cst = constraint_init(slice_initSize(1, 2), slice_init(2), eps, 0);
556  cxt = context_add(cxt, &cst);
557  // 2    3
558  cst = constraint_init(slice_init(2), slice_init(3), skp, 0);
559  cxt = context_add(cxt, &cst);
560  // 3    2
561  cst = constraint_init(slice_init(3), slice_init(2), eps, 0);
562  cxt = context_add(cxt, &cst);
563  // 2    1-2
564  cst = constraint_init(slice_init(2), slice_initSize(1, 2), eps, 0);
565  cxt = context_add(cxt, &cst);
566  // 4-5  2
567  cst = constraint_init(slice_initSize(4, 2), slice_init(2), eps, 0);
568  cxt = context_add(cxt, &cst);
569  // 1-3  2-4
570  cst = constraint_init(slice_initSize(1, 3), slice_initSize(2, 3), eps, 0);
571  cxt = context_add(cxt, &cst);
572
573  return cxt;
574}
575
576static T*
577ut_setup5(T *cxt)
578{
579  C cst;
580  struct eps eps = eps_init(eps_dig , 5);
581  struct eps skp = eps_init(eps_skip, 5);
582
583  // 2-4/2    4-5
584  cst = constraint_init(slice_initSizeStride(2, 2, 2), slice_initSize(4, 2), eps, 0);
585  cxt = context_add(cxt, &cst);
586  // 4-5      2
587  cst = constraint_init(slice_initSize(4, 2), slice_init(2), eps, 0);
588  cxt = context_add(cxt, &cst);
589  // 1-3      2-4
590  cst = constraint_init(slice_initSize(1, 3), slice_initSize(2, 3), eps, 0);
591  cxt = context_add(cxt, &cst);
592  // 1-6/2    2
593  cst = constraint_init(slice_initSizeStride(1, 2, 3), slice_init(2), eps, 0);
594  cxt = context_add(cxt, &cst);
595  // 2        3-7/2
596  cst = constraint_init(slice_init(2), slice_initSizeStride(3, 2, 2), eps, 0);
597  cxt = context_add(cxt, &cst);
598  // 3        2
599  cst = constraint_init(slice_init(3), slice_init(2), skp, 0);
600  cxt = context_add(cxt, &cst);
601  // 2        1-5/2
602  cst = constraint_init(slice_init(2), slice_initSizeStride(1, 2, 2), eps, 0);
603  cxt = context_add(cxt, &cst);
604
605  return cxt;
606}
607
608static T*
609ut_setup6(T *cxt)
610{
611  C cst;
612  struct eps eps = eps_init(eps_dig , 6);
613
614  ut_setup3(cxt);
615  ut_setup2(cxt);
616
617  // 1-5    1-5
618  cst = constraint_init(slice_initSize(1, 5), slice_initSize(1, 5), eps, 0);
619  cxt = context_add(cxt, &cst);
620
621  return cxt;
622}
623
624static T*
625ut_setup7(T *cxt)
626{
627  ut_setup3(cxt);
628  ut_setup5(cxt);
629  ut_setup2(cxt);
630  ut_setup4(cxt);
631  ut_setup1(cxt);
632  return cxt;
633}
634
635// ----- unit tests
636
637static struct spec {
638  const char *name;
639  T*        (*setup)   (T*);
640  void      (*test )   (struct utest*, T*, int, int);
641  T*        (*teardown)(T*);
642} spec[] = {
643  { "no constraint",                        0        , ut_testNul, ut_teardown },
644  { "single constraint",                    ut_setup1, ut_testEqu, ut_teardown },
645  { "multiple row constraints",             ut_setup2, ut_testEqu, ut_teardown },
646  { "multiple column constraints",          ut_setup3, ut_testEqu, ut_teardown },
647  { "overlapping constraints",              ut_setup4, ut_testEqu, ut_teardown },
648  { "overlapping strided constraints",      ut_setup5, ut_testEqu, ut_teardown },
649  { "sparse mixed strided constraints",     ut_setup6, ut_testEqu, ut_teardown },
650  { "many mixed strided constraints",       ut_setup7, ut_testEqu, ut_teardown },
651  { "no constraint (after use)",            0        , ut_testNul, ut_teardown }
652};
653enum { spec_n = sizeof spec/sizeof *spec };
654
655// ----- interface
656
657void
658context_utest(struct utest *ut)
659{
660  assert(ut);
661  T *cxt = context_alloc(0);
662
663  utest_title(ut, "Context");
664
665  for (int k = 0; k < spec_n; k++) {
666    utest_init(ut, spec[k].name);
667    if (spec[k].setup) cxt = spec[k].setup(cxt);
668
669    spec[k].test(ut, cxt, 1, 1); // idempotent
670
671    for (int i = 1; i <= NROW; i++)
672    for (int j = 1; j <= NCOL; j++)
673      spec[k].test(ut, cxt, i, j);
674 
675    spec[k].test(ut, cxt, NROW, NCOL); // idempotent
676
677    if (spec[k].teardown) cxt = spec[k].teardown(cxt);
678    utest_fini(ut);
679  }
680
681  context_free(cxt);
682}
683
684#endif
Note: See TracBrowser for help on using the repository browser.