1 /* glpmpl03.c */
2 
3 /***********************************************************************
4 *  This code is part of GLPK (GNU Linear Programming Kit).
5 *
6 *  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
7 *  2009, 2010 Andrew Makhorin, Department for Applied Informatics,
8 *  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
9 *  E-mail: <mao@gnu.org>.
10 *
11 *  GLPK is free software: you can redistribute it and/or modify it
12 *  under the terms of the GNU General Public License as published by
13 *  the Free Software Foundation, either version 3 of the License, or
14 *  (at your option) any later version.
15 *
16 *  GLPK is distributed in the hope that it will be useful, but WITHOUT
17 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
19 *  License for more details.
20 *
21 *  You should have received a copy of the GNU General Public License
22 *  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
23 ***********************************************************************/
24 
25 #define _GLPSTD_ERRNO
26 #define _GLPSTD_STDIO
27 #include "glpenv.h"
28 #include "glpmpl.h"
29 
30 /**********************************************************************/
31 /* * *                   FLOATING-POINT NUMBERS                   * * */
32 /**********************************************************************/
33 
34 /*----------------------------------------------------------------------
35 -- fp_add - floating-point addition.
36 --
37 -- This routine computes the sum x + y. */
38 
fp_add(MPL * mpl,double x,double y)39 double fp_add(MPL *mpl, double x, double y)
40 {     if (x > 0.0 && y > 0.0 && x > + 0.999 * DBL_MAX - y ||
41           x < 0.0 && y < 0.0 && x < - 0.999 * DBL_MAX - y)
42          mpl_error(mpl, "%.*g + %.*g; floating-point overflow",
43             DBL_DIG, x, DBL_DIG, y);
44       return x + y;
45 }
46 
47 /*----------------------------------------------------------------------
48 -- fp_sub - floating-point subtraction.
49 --
50 -- This routine computes the difference x - y. */
51 
fp_sub(MPL * mpl,double x,double y)52 double fp_sub(MPL *mpl, double x, double y)
53 {     if (x > 0.0 && y < 0.0 && x > + 0.999 * DBL_MAX + y ||
54           x < 0.0 && y > 0.0 && x < - 0.999 * DBL_MAX + y)
55          mpl_error(mpl, "%.*g - %.*g; floating-point overflow",
56             DBL_DIG, x, DBL_DIG, y);
57       return x - y;
58 }
59 
60 /*----------------------------------------------------------------------
61 -- fp_less - floating-point non-negative subtraction.
62 --
63 -- This routine computes the non-negative difference max(0, x - y). */
64 
fp_less(MPL * mpl,double x,double y)65 double fp_less(MPL *mpl, double x, double y)
66 {     if (x < y) return 0.0;
67       if (x > 0.0 && y < 0.0 && x > + 0.999 * DBL_MAX + y)
68          mpl_error(mpl, "%.*g less %.*g; floating-point overflow",
69             DBL_DIG, x, DBL_DIG, y);
70       return x - y;
71 }
72 
73 /*----------------------------------------------------------------------
74 -- fp_mul - floating-point multiplication.
75 --
76 -- This routine computes the product x * y. */
77 
fp_mul(MPL * mpl,double x,double y)78 double fp_mul(MPL *mpl, double x, double y)
79 {     if (fabs(y) > 1.0 && fabs(x) > (0.999 * DBL_MAX) / fabs(y))
80          mpl_error(mpl, "%.*g * %.*g; floating-point overflow",
81             DBL_DIG, x, DBL_DIG, y);
82       return x * y;
83 }
84 
85 /*----------------------------------------------------------------------
86 -- fp_div - floating-point division.
87 --
88 -- This routine computes the quotient x / y. */
89 
fp_div(MPL * mpl,double x,double y)90 double fp_div(MPL *mpl, double x, double y)
91 {     if (fabs(y) < DBL_MIN)
92          mpl_error(mpl, "%.*g / %.*g; floating-point zero divide",
93             DBL_DIG, x, DBL_DIG, y);
94       if (fabs(y) < 1.0 && fabs(x) > (0.999 * DBL_MAX) * fabs(y))
95          mpl_error(mpl, "%.*g / %.*g; floating-point overflow",
96             DBL_DIG, x, DBL_DIG, y);
97       return x / y;
98 }
99 
100 /*----------------------------------------------------------------------
101 -- fp_idiv - floating-point quotient of exact division.
102 --
103 -- This routine computes the quotient of exact division x div y. */
104 
fp_idiv(MPL * mpl,double x,double y)105 double fp_idiv(MPL *mpl, double x, double y)
106 {     if (fabs(y) < DBL_MIN)
107          mpl_error(mpl, "%.*g div %.*g; floating-point zero divide",
108             DBL_DIG, x, DBL_DIG, y);
109       if (fabs(y) < 1.0 && fabs(x) > (0.999 * DBL_MAX) * fabs(y))
110          mpl_error(mpl, "%.*g div %.*g; floating-point overflow",
111             DBL_DIG, x, DBL_DIG, y);
112       x /= y;
113       return x > 0.0 ? floor(x) : x < 0.0 ? ceil(x) : 0.0;
114 }
115 
116 /*----------------------------------------------------------------------
117 -- fp_mod - floating-point remainder of exact division.
118 --
119 -- This routine computes the remainder of exact division x mod y.
120 --
121 -- NOTE: By definition x mod y = x - y * floor(x / y). */
122 
fp_mod(MPL * mpl,double x,double y)123 double fp_mod(MPL *mpl, double x, double y)
124 {     double r;
125       xassert(mpl == mpl);
126       if (x == 0.0)
127          r = 0.0;
128       else if (y == 0.0)
129          r = x;
130       else
131       {  r = fmod(fabs(x), fabs(y));
132          if (r != 0.0)
133          {  if (x < 0.0) r = - r;
134             if (x > 0.0 && y < 0.0 || x < 0.0 && y > 0.0) r += y;
135          }
136       }
137       return r;
138 }
139 
140 /*----------------------------------------------------------------------
141 -- fp_power - floating-point exponentiation (raise to power).
142 --
143 -- This routine computes the exponentiation x ** y. */
144 
fp_power(MPL * mpl,double x,double y)145 double fp_power(MPL *mpl, double x, double y)
146 {     double r;
147       if (x == 0.0 && y <= 0.0 || x < 0.0 && y != floor(y))
148          mpl_error(mpl, "%.*g ** %.*g; result undefined",
149             DBL_DIG, x, DBL_DIG, y);
150       if (x == 0.0) goto eval;
151       if (fabs(x) > 1.0 && y > +1.0 &&
152             +log(fabs(x)) > (0.999 * log(DBL_MAX)) / y ||
153           fabs(x) < 1.0 && y < -1.0 &&
154             +log(fabs(x)) < (0.999 * log(DBL_MAX)) / y)
155          mpl_error(mpl, "%.*g ** %.*g; floating-point overflow",
156             DBL_DIG, x, DBL_DIG, y);
157       if (fabs(x) > 1.0 && y < -1.0 &&
158             -log(fabs(x)) < (0.999 * log(DBL_MAX)) / y ||
159           fabs(x) < 1.0 && y > +1.0 &&
160             -log(fabs(x)) > (0.999 * log(DBL_MAX)) / y)
161          r = 0.0;
162       else
163 eval:    r = pow(x, y);
164       return r;
165 }
166 
167 /*----------------------------------------------------------------------
168 -- fp_exp - floating-point base-e exponential.
169 --
170 -- This routine computes the base-e exponential e ** x. */
171 
fp_exp(MPL * mpl,double x)172 double fp_exp(MPL *mpl, double x)
173 {     if (x > 0.999 * log(DBL_MAX))
174          mpl_error(mpl, "exp(%.*g); floating-point overflow", DBL_DIG, x);
175       return exp(x);
176 }
177 
178 /*----------------------------------------------------------------------
179 -- fp_log - floating-point natural logarithm.
180 --
181 -- This routine computes the natural logarithm log x. */
182 
fp_log(MPL * mpl,double x)183 double fp_log(MPL *mpl, double x)
184 {     if (x <= 0.0)
185          mpl_error(mpl, "log(%.*g); non-positive argument", DBL_DIG, x);
186       return log(x);
187 }
188 
189 /*----------------------------------------------------------------------
190 -- fp_log10 - floating-point common (decimal) logarithm.
191 --
192 -- This routine computes the common (decimal) logarithm lg x. */
193 
fp_log10(MPL * mpl,double x)194 double fp_log10(MPL *mpl, double x)
195 {     if (x <= 0.0)
196          mpl_error(mpl, "log10(%.*g); non-positive argument", DBL_DIG, x);
197       return log10(x);
198 }
199 
200 /*----------------------------------------------------------------------
201 -- fp_sqrt - floating-point square root.
202 --
203 -- This routine computes the square root x ** 0.5. */
204 
fp_sqrt(MPL * mpl,double x)205 double fp_sqrt(MPL *mpl, double x)
206 {     if (x < 0.0)
207          mpl_error(mpl, "sqrt(%.*g); negative argument", DBL_DIG, x);
208       return sqrt(x);
209 }
210 
211 /*----------------------------------------------------------------------
212 -- fp_sin - floating-point trigonometric sine.
213 --
214 -- This routine computes the trigonometric sine sin(x). */
215 
fp_sin(MPL * mpl,double x)216 double fp_sin(MPL *mpl, double x)
217 {     if (!(-1e6 <= x && x <= +1e6))
218          mpl_error(mpl, "sin(%.*g); argument too large", DBL_DIG, x);
219       return sin(x);
220 }
221 
222 /*----------------------------------------------------------------------
223 -- fp_cos - floating-point trigonometric cosine.
224 --
225 -- This routine computes the trigonometric cosine cos(x). */
226 
fp_cos(MPL * mpl,double x)227 double fp_cos(MPL *mpl, double x)
228 {     if (!(-1e6 <= x && x <= +1e6))
229          mpl_error(mpl, "cos(%.*g); argument too large", DBL_DIG, x);
230       return cos(x);
231 }
232 
233 /*----------------------------------------------------------------------
234 -- fp_atan - floating-point trigonometric arctangent.
235 --
236 -- This routine computes the trigonometric arctangent atan(x). */
237 
fp_atan(MPL * mpl,double x)238 double fp_atan(MPL *mpl, double x)
239 {     xassert(mpl == mpl);
240       return atan(x);
241 }
242 
243 /*----------------------------------------------------------------------
244 -- fp_atan2 - floating-point trigonometric arctangent.
245 --
246 -- This routine computes the trigonometric arctangent atan(y / x). */
247 
fp_atan2(MPL * mpl,double y,double x)248 double fp_atan2(MPL *mpl, double y, double x)
249 {     xassert(mpl == mpl);
250       return atan2(y, x);
251 }
252 
253 /*----------------------------------------------------------------------
254 -- fp_round - round floating-point value to n fractional digits.
255 --
256 -- This routine rounds given floating-point value x to n fractional
257 -- digits with the formula:
258 --
259 --    round(x, n) = floor(x * 10^n + 0.5) / 10^n.
260 --
261 -- The parameter n is assumed to be integer. */
262 
fp_round(MPL * mpl,double x,double n)263 double fp_round(MPL *mpl, double x, double n)
264 {     double ten_to_n;
265       if (n != floor(n))
266          mpl_error(mpl, "round(%.*g, %.*g); non-integer second argument",
267             DBL_DIG, x, DBL_DIG, n);
268       if (n <= DBL_DIG + 2)
269       {  ten_to_n = pow(10.0, n);
270          if (fabs(x) < (0.999 * DBL_MAX) / ten_to_n)
271          {  x = floor(x * ten_to_n + 0.5);
272             if (x != 0.0) x /= ten_to_n;
273          }
274       }
275       return x;
276 }
277 
278 /*----------------------------------------------------------------------
279 -- fp_trunc - truncate floating-point value to n fractional digits.
280 --
281 -- This routine truncates given floating-point value x to n fractional
282 -- digits with the formula:
283 --
284 --                  ( floor(x * 10^n) / 10^n,  if x >= 0
285 --    trunc(x, n) = <
286 --                  ( ceil(x * 10^n) / 10^n,   if x < 0
287 --
288 -- The parameter n is assumed to be integer. */
289 
fp_trunc(MPL * mpl,double x,double n)290 double fp_trunc(MPL *mpl, double x, double n)
291 {     double ten_to_n;
292       if (n != floor(n))
293          mpl_error(mpl, "trunc(%.*g, %.*g); non-integer second argument",
294             DBL_DIG, x, DBL_DIG, n);
295       if (n <= DBL_DIG + 2)
296       {  ten_to_n = pow(10.0, n);
297          if (fabs(x) < (0.999 * DBL_MAX) / ten_to_n)
298          {  x = (x >= 0.0 ? floor(x * ten_to_n) : ceil(x * ten_to_n));
299             if (x != 0.0) x /= ten_to_n;
300          }
301       }
302       return x;
303 }
304 
305 /**********************************************************************/
306 /* * *              PSEUDO-RANDOM NUMBER GENERATORS               * * */
307 /**********************************************************************/
308 
309 /*----------------------------------------------------------------------
310 -- fp_irand224 - pseudo-random integer in the range [0, 2^24).
311 --
312 -- This routine returns a next pseudo-random integer (converted to
313 -- floating-point) which is uniformly distributed between 0 and 2^24-1,
314 -- inclusive. */
315 
316 #define two_to_the_24 0x1000000
317 
fp_irand224(MPL * mpl)318 double fp_irand224(MPL *mpl)
319 {     return
320          (double)rng_unif_rand(mpl->rand, two_to_the_24);
321 }
322 
323 /*----------------------------------------------------------------------
324 -- fp_uniform01 - pseudo-random number in the range [0, 1).
325 --
326 -- This routine returns a next pseudo-random number which is uniformly
327 -- distributed in the range [0, 1). */
328 
329 #define two_to_the_31 ((unsigned int)0x80000000)
330 
fp_uniform01(MPL * mpl)331 double fp_uniform01(MPL *mpl)
332 {     return
333          (double)rng_next_rand(mpl->rand) / (double)two_to_the_31;
334 }
335 
336 /*----------------------------------------------------------------------
337 -- fp_uniform - pseudo-random number in the range [a, b).
338 --
339 -- This routine returns a next pseudo-random number which is uniformly
340 -- distributed in the range [a, b). */
341 
fp_uniform(MPL * mpl,double a,double b)342 double fp_uniform(MPL *mpl, double a, double b)
343 {     double x;
344       if (a >= b)
345          mpl_error(mpl, "Uniform(%.*g, %.*g); invalid range",
346             DBL_DIG, a, DBL_DIG, b);
347       x = fp_uniform01(mpl);
348 #if 0
349       x = a * (1.0 - x) + b * x;
350 #else
351       x = fp_add(mpl, a * (1.0 - x), b * x);
352 #endif
353       return x;
354 }
355 
356 /*----------------------------------------------------------------------
357 -- fp_normal01 - Gaussian random variate with mu = 0 and sigma = 1.
358 --
359 -- This routine returns a Gaussian random variate with zero mean and
360 -- unit standard deviation. The polar (Box-Mueller) method is used.
361 --
362 -- This code is a modified version of the routine gsl_ran_gaussian from
363 -- the GNU Scientific Library Version 1.0. */
364 
fp_normal01(MPL * mpl)365 double fp_normal01(MPL *mpl)
366 {     double x, y, r2;
367       do
368       {  /* choose x, y in uniform square (-1,-1) to (+1,+1) */
369          x = -1.0 + 2.0 * fp_uniform01(mpl);
370          y = -1.0 + 2.0 * fp_uniform01(mpl);
371          /* see if it is in the unit circle */
372          r2 = x * x + y * y;
373       } while (r2 > 1.0 || r2 == 0.0);
374       /* Box-Muller transform */
375       return y * sqrt(-2.0 * log (r2) / r2);
376 }
377 
378 /*----------------------------------------------------------------------
379 -- fp_normal - Gaussian random variate with specified mu and sigma.
380 --
381 -- This routine returns a Gaussian random variate with mean mu and
382 -- standard deviation sigma. */
383 
fp_normal(MPL * mpl,double mu,double sigma)384 double fp_normal(MPL *mpl, double mu, double sigma)
385 {     double x;
386 #if 0
387       x = mu + sigma * fp_normal01(mpl);
388 #else
389       x = fp_add(mpl, mu, fp_mul(mpl, sigma, fp_normal01(mpl)));
390 #endif
391       return x;
392 }
393 
394 /**********************************************************************/
395 /* * *                SEGMENTED CHARACTER STRINGS                 * * */
396 /**********************************************************************/
397 
398 /*----------------------------------------------------------------------
399 -- create_string - create character string.
400 --
401 -- This routine creates a segmented character string, which is exactly
402 -- equivalent to specified character string. */
403 
create_string(MPL * mpl,char buf[MAX_LENGTH+1])404 STRING *create_string
405 (     MPL *mpl,
406       char buf[MAX_LENGTH+1]  /* not changed */
407 )
408 #if 0
409 {     STRING *head, *tail;
410       int i, j;
411       xassert(buf != NULL);
412       xassert(strlen(buf) <= MAX_LENGTH);
413       head = tail = dmp_get_atom(mpl->strings, sizeof(STRING));
414       for (i = j = 0; ; i++)
415       {  if ((tail->seg[j++] = buf[i]) == '\0') break;
416          if (j == STRSEG_SIZE)
417 tail = (tail->next = dmp_get_atom(mpl->strings, sizeof(STRING))), j = 0;
418       }
419       tail->next = NULL;
420       return head;
421 }
422 #else
423 {     STRING *str;
424       xassert(strlen(buf) <= MAX_LENGTH);
425       str = dmp_get_atom(mpl->strings, strlen(buf)+1);
426       strcpy(str, buf);
427       return str;
428 }
429 #endif
430 
431 /*----------------------------------------------------------------------
432 -- copy_string - make copy of character string.
433 --
434 -- This routine returns an exact copy of segmented character string. */
435 
copy_string(MPL * mpl,STRING * str)436 STRING *copy_string
437 (     MPL *mpl,
438       STRING *str             /* not changed */
439 )
440 #if 0
441 {     STRING *head, *tail;
442       xassert(str != NULL);
443       head = tail = dmp_get_atom(mpl->strings, sizeof(STRING));
444       for (; str != NULL; str = str->next)
445       {  memcpy(tail->seg, str->seg, STRSEG_SIZE);
446          if (str->next != NULL)
447 tail = (tail->next = dmp_get_atom(mpl->strings, sizeof(STRING)));
448       }
449       tail->next = NULL;
450       return head;
451 }
452 #else
453 {     xassert(mpl == mpl);
454       return create_string(mpl, str);
455 }
456 #endif
457 
458 /*----------------------------------------------------------------------
459 -- compare_strings - compare one character string with another.
460 --
461 -- This routine compares one segmented character strings with another
462 -- and returns the result of comparison as follows:
463 --
464 -- = 0 - both strings are identical;
465 -- < 0 - the first string precedes the second one;
466 -- > 0 - the first string follows the second one. */
467 
compare_strings(MPL * mpl,STRING * str1,STRING * str2)468 int compare_strings
469 (     MPL *mpl,
470       STRING *str1,           /* not changed */
471       STRING *str2            /* not changed */
472 )
473 #if 0
474 {     int j, c1, c2;
475       xassert(mpl == mpl);
476       for (;; str1 = str1->next, str2 = str2->next)
477       {  xassert(str1 != NULL);
478          xassert(str2 != NULL);
479          for (j = 0; j < STRSEG_SIZE; j++)
480          {  c1 = (unsigned char)str1->seg[j];
481             c2 = (unsigned char)str2->seg[j];
482             if (c1 < c2) return -1;
483             if (c1 > c2) return +1;
484             if (c1 == '\0') goto done;
485          }
486       }
487 done: return 0;
488 }
489 #else
490 {     xassert(mpl == mpl);
491       return strcmp(str1, str2);
492 }
493 #endif
494 
495 /*----------------------------------------------------------------------
496 -- fetch_string - extract content of character string.
497 --
498 -- This routine returns a character string, which is exactly equivalent
499 -- to specified segmented character string. */
500 
fetch_string(MPL * mpl,STRING * str,char buf[MAX_LENGTH+1])501 char *fetch_string
502 (     MPL *mpl,
503       STRING *str,            /* not changed */
504       char buf[MAX_LENGTH+1]  /* modified */
505 )
506 #if 0
507 {     int i, j;
508       xassert(mpl == mpl);
509       xassert(buf != NULL);
510       for (i = 0; ; str = str->next)
511       {  xassert(str != NULL);
512          for (j = 0; j < STRSEG_SIZE; j++)
513             if ((buf[i++] = str->seg[j]) == '\0') goto done;
514       }
515 done: xassert(strlen(buf) <= MAX_LENGTH);
516       return buf;
517 }
518 #else
519 {     xassert(mpl == mpl);
520       return strcpy(buf, str);
521 }
522 #endif
523 
524 /*----------------------------------------------------------------------
525 -- delete_string - delete character string.
526 --
527 -- This routine deletes specified segmented character string. */
528 
delete_string(MPL * mpl,STRING * str)529 void delete_string
530 (     MPL *mpl,
531       STRING *str             /* destroyed */
532 )
533 #if 0
534 {     STRING *temp;
535       xassert(str != NULL);
536       while (str != NULL)
537       {  temp = str;
538          str = str->next;
539          dmp_free_atom(mpl->strings, temp, sizeof(STRING));
540       }
541       return;
542 }
543 #else
544 {     dmp_free_atom(mpl->strings, str, strlen(str)+1);
545       return;
546 }
547 #endif
548 
549 /**********************************************************************/
550 /* * *                          SYMBOLS                           * * */
551 /**********************************************************************/
552 
553 /*----------------------------------------------------------------------
554 -- create_symbol_num - create symbol of numeric type.
555 --
556 -- This routine creates a symbol, which has a numeric value specified
557 -- as floating-point number. */
558 
create_symbol_num(MPL * mpl,double num)559 SYMBOL *create_symbol_num(MPL *mpl, double num)
560 {     SYMBOL *sym;
561       sym = dmp_get_atom(mpl->symbols, sizeof(SYMBOL));
562       sym->num = num;
563       sym->str = NULL;
564       return sym;
565 }
566 
567 /*----------------------------------------------------------------------
568 -- create_symbol_str - create symbol of abstract type.
569 --
570 -- This routine creates a symbol, which has an abstract value specified
571 -- as segmented character string. */
572 
create_symbol_str(MPL * mpl,STRING * str)573 SYMBOL *create_symbol_str
574 (     MPL *mpl,
575       STRING *str             /* destroyed */
576 )
577 {     SYMBOL *sym;
578       xassert(str != NULL);
579       sym = dmp_get_atom(mpl->symbols, sizeof(SYMBOL));
580       sym->num = 0.0;
581       sym->str = str;
582       return sym;
583 }
584 
585 /*----------------------------------------------------------------------
586 -- copy_symbol - make copy of symbol.
587 --
588 -- This routine returns an exact copy of symbol. */
589 
copy_symbol(MPL * mpl,SYMBOL * sym)590 SYMBOL *copy_symbol
591 (     MPL *mpl,
592       SYMBOL *sym             /* not changed */
593 )
594 {     SYMBOL *copy;
595       xassert(sym != NULL);
596       copy = dmp_get_atom(mpl->symbols, sizeof(SYMBOL));
597       if (sym->str == NULL)
598       {  copy->num = sym->num;
599          copy->str = NULL;
600       }
601       else
602       {  copy->num = 0.0;
603          copy->str = copy_string(mpl, sym->str);
604       }
605       return copy;
606 }
607 
608 /*----------------------------------------------------------------------
609 -- compare_symbols - compare one symbol with another.
610 --
611 -- This routine compares one symbol with another and returns the result
612 -- of comparison as follows:
613 --
614 -- = 0 - both symbols are identical;
615 -- < 0 - the first symbol precedes the second one;
616 -- > 0 - the first symbol follows the second one.
617 --
618 -- Note that the linear order, in which symbols follow each other, is
619 -- implementation-dependent. It may be not an alphabetical order. */
620 
compare_symbols(MPL * mpl,SYMBOL * sym1,SYMBOL * sym2)621 int compare_symbols
622 (     MPL *mpl,
623       SYMBOL *sym1,           /* not changed */
624       SYMBOL *sym2            /* not changed */
625 )
626 {     xassert(sym1 != NULL);
627       xassert(sym2 != NULL);
628       /* let all numeric quantities precede all symbolic quantities */
629       if (sym1->str == NULL && sym2->str == NULL)
630       {  if (sym1->num < sym2->num) return -1;
631          if (sym1->num > sym2->num) return +1;
632          return 0;
633       }
634       if (sym1->str == NULL) return -1;
635       if (sym2->str == NULL) return +1;
636       return compare_strings(mpl, sym1->str, sym2->str);
637 }
638 
639 /*----------------------------------------------------------------------
640 -- delete_symbol - delete symbol.
641 --
642 -- This routine deletes specified symbol. */
643 
delete_symbol(MPL * mpl,SYMBOL * sym)644 void delete_symbol
645 (     MPL *mpl,
646       SYMBOL *sym             /* destroyed */
647 )
648 {     xassert(sym != NULL);
649       if (sym->str != NULL) delete_string(mpl, sym->str);
650       dmp_free_atom(mpl->symbols, sym, sizeof(SYMBOL));
651       return;
652 }
653 
654 /*----------------------------------------------------------------------
655 -- format_symbol - format symbol for displaying or printing.
656 --
657 -- This routine converts specified symbol to a charater string, which
658 -- is suitable for displaying or printing.
659 --
660 -- The resultant string is never longer than 255 characters. If it gets
661 -- longer, it is truncated from the right and appended by dots. */
662 
format_symbol(MPL * mpl,SYMBOL * sym)663 char *format_symbol
664 (     MPL *mpl,
665       SYMBOL *sym             /* not changed */
666 )
667 {     char *buf = mpl->sym_buf;
668       xassert(sym != NULL);
669       if (sym->str == NULL)
670          sprintf(buf, "%.*g", DBL_DIG, sym->num);
671       else
672       {  char str[MAX_LENGTH+1];
673          int quoted, j, len;
674          fetch_string(mpl, sym->str, str);
675          if (!(isalpha((unsigned char)str[0]) || str[0] == '_'))
676             quoted = 1;
677          else
678          {  quoted = 0;
679             for (j = 1; str[j] != '\0'; j++)
680             {  if (!(isalnum((unsigned char)str[j]) ||
681                      strchr("+-._", (unsigned char)str[j]) != NULL))
682                {  quoted = 1;
683                   break;
684                }
685             }
686          }
687 #        define safe_append(c) \
688             (void)(len < 255 ? (buf[len++] = (char)(c)) : 0)
689          buf[0] = '\0', len = 0;
690          if (quoted) safe_append('\'');
691          for (j = 0; str[j] != '\0'; j++)
692          {  if (quoted && str[j] == '\'') safe_append('\'');
693             safe_append(str[j]);
694          }
695          if (quoted) safe_append('\'');
696 #        undef safe_append
697          buf[len] = '\0';
698          if (len == 255) strcpy(buf+252, "...");
699       }
700       xassert(strlen(buf) <= 255);
701       return buf;
702 }
703 
704 /*----------------------------------------------------------------------
705 -- concat_symbols - concatenate one symbol with another.
706 --
707 -- This routine concatenates values of two given symbols and assigns
708 -- the resultant character string to a new symbol, which is returned on
709 -- exit. Both original symbols are destroyed. */
710 
concat_symbols(MPL * mpl,SYMBOL * sym1,SYMBOL * sym2)711 SYMBOL *concat_symbols
712 (     MPL *mpl,
713       SYMBOL *sym1,           /* destroyed */
714       SYMBOL *sym2            /* destroyed */
715 )
716 {     char str1[MAX_LENGTH+1], str2[MAX_LENGTH+1];
717       xassert(MAX_LENGTH >= DBL_DIG + DBL_DIG);
718       if (sym1->str == NULL)
719          sprintf(str1, "%.*g", DBL_DIG, sym1->num);
720       else
721          fetch_string(mpl, sym1->str, str1);
722       if (sym2->str == NULL)
723          sprintf(str2, "%.*g", DBL_DIG, sym2->num);
724       else
725          fetch_string(mpl, sym2->str, str2);
726       if (strlen(str1) + strlen(str2) > MAX_LENGTH)
727       {  char buf[255+1];
728          strcpy(buf, format_symbol(mpl, sym1));
729          xassert(strlen(buf) < sizeof(buf));
730          mpl_error(mpl, "%s & %s; resultant symbol exceeds %d characters",
731             buf, format_symbol(mpl, sym2), MAX_LENGTH);
732       }
733       delete_symbol(mpl, sym1);
734       delete_symbol(mpl, sym2);
735       return create_symbol_str(mpl, create_string(mpl, strcat(str1,
736          str2)));
737 }
738 
739 /**********************************************************************/
740 /* * *                          N-TUPLES                          * * */
741 /**********************************************************************/
742 
743 /*----------------------------------------------------------------------
744 -- create_tuple - create n-tuple.
745 --
746 -- This routine creates a n-tuple, which initially has no components,
747 -- i.e. which is 0-tuple. */
748 
create_tuple(MPL * mpl)749 TUPLE *create_tuple(MPL *mpl)
750 {     TUPLE *tuple;
751       xassert(mpl == mpl);
752       tuple = NULL;
753       return tuple;
754 }
755 
756 /*----------------------------------------------------------------------
757 -- expand_tuple - append symbol to n-tuple.
758 --
759 -- This routine expands n-tuple appending to it a given symbol, which
760 -- becomes its new last component. */
761 
expand_tuple(MPL * mpl,TUPLE * tuple,SYMBOL * sym)762 TUPLE *expand_tuple
763 (     MPL *mpl,
764       TUPLE *tuple,           /* destroyed */
765       SYMBOL *sym             /* destroyed */
766 )
767 {     TUPLE *tail, *temp;
768       xassert(sym != NULL);
769       /* create a new component */
770       tail = dmp_get_atom(mpl->tuples, sizeof(TUPLE));
771       tail->sym = sym;
772       tail->next = NULL;
773       /* and append it to the component list */
774       if (tuple == NULL)
775          tuple = tail;
776       else
777       {  for (temp = tuple; temp->next != NULL; temp = temp->next);
778          temp->next = tail;
779       }
780       return tuple;
781 }
782 
783 /*----------------------------------------------------------------------
784 -- tuple_dimen - determine dimension of n-tuple.
785 --
786 -- This routine returns dimension of n-tuple, i.e. number of components
787 -- in the n-tuple. */
788 
tuple_dimen(MPL * mpl,TUPLE * tuple)789 int tuple_dimen
790 (     MPL *mpl,
791       TUPLE *tuple            /* not changed */
792 )
793 {     TUPLE *temp;
794       int dim = 0;
795       xassert(mpl == mpl);
796       for (temp = tuple; temp != NULL; temp = temp->next) dim++;
797       return dim;
798 }
799 
800 /*----------------------------------------------------------------------
801 -- copy_tuple - make copy of n-tuple.
802 --
803 -- This routine returns an exact copy of n-tuple. */
804 
copy_tuple(MPL * mpl,TUPLE * tuple)805 TUPLE *copy_tuple
806 (     MPL *mpl,
807       TUPLE *tuple            /* not changed */
808 )
809 {     TUPLE *head, *tail;
810       if (tuple == NULL)
811          head = NULL;
812       else
813       {  head = tail = dmp_get_atom(mpl->tuples, sizeof(TUPLE));
814          for (; tuple != NULL; tuple = tuple->next)
815          {  xassert(tuple->sym != NULL);
816             tail->sym = copy_symbol(mpl, tuple->sym);
817             if (tuple->next != NULL)
818 tail = (tail->next = dmp_get_atom(mpl->tuples, sizeof(TUPLE)));
819          }
820          tail->next = NULL;
821       }
822       return head;
823 }
824 
825 /*----------------------------------------------------------------------
826 -- compare_tuples - compare one n-tuple with another.
827 --
828 -- This routine compares two given n-tuples, which must have the same
829 -- dimension (not checked for the sake of efficiency), and returns one
830 -- of the following codes:
831 --
832 -- = 0 - both n-tuples are identical;
833 -- < 0 - the first n-tuple precedes the second one;
834 -- > 0 - the first n-tuple follows the second one.
835 --
836 -- Note that the linear order, in which n-tuples follow each other, is
837 -- implementation-dependent. It may be not an alphabetical order. */
838 
compare_tuples(MPL * mpl,TUPLE * tuple1,TUPLE * tuple2)839 int compare_tuples
840 (     MPL *mpl,
841       TUPLE *tuple1,          /* not changed */
842       TUPLE *tuple2           /* not changed */
843 )
844 {     TUPLE *item1, *item2;
845       int ret;
846       xassert(mpl == mpl);
847       for (item1 = tuple1, item2 = tuple2; item1 != NULL;
848            item1 = item1->next, item2 = item2->next)
849       {  xassert(item2 != NULL);
850          xassert(item1->sym != NULL);
851          xassert(item2->sym != NULL);
852          ret = compare_symbols(mpl, item1->sym, item2->sym);
853          if (ret != 0) return ret;
854       }
855       xassert(item2 == NULL);
856       return 0;
857 }
858 
859 /*----------------------------------------------------------------------
860 -- build_subtuple - build subtuple of given n-tuple.
861 --
862 -- This routine builds subtuple, which consists of first dim components
863 -- of given n-tuple. */
864 
build_subtuple(MPL * mpl,TUPLE * tuple,int dim)865 TUPLE *build_subtuple
866 (     MPL *mpl,
867       TUPLE *tuple,           /* not changed */
868       int dim
869 )
870 {     TUPLE *head, *temp;
871       int j;
872       head = create_tuple(mpl);
873       for (j = 1, temp = tuple; j <= dim; j++, temp = temp->next)
874       {  xassert(temp != NULL);
875          head = expand_tuple(mpl, head, copy_symbol(mpl, temp->sym));
876       }
877       return head;
878 }
879 
880 /*----------------------------------------------------------------------
881 -- delete_tuple - delete n-tuple.
882 --
883 -- This routine deletes specified n-tuple. */
884 
delete_tuple(MPL * mpl,TUPLE * tuple)885 void delete_tuple
886 (     MPL *mpl,
887       TUPLE *tuple            /* destroyed */
888 )
889 {     TUPLE *temp;
890       while (tuple != NULL)
891       {  temp = tuple;
892          tuple = temp->next;
893          xassert(temp->sym != NULL);
894          delete_symbol(mpl, temp->sym);
895          dmp_free_atom(mpl->tuples, temp, sizeof(TUPLE));
896       }
897       return;
898 }
899 
900 /*----------------------------------------------------------------------
901 -- format_tuple - format n-tuple for displaying or printing.
902 --
903 -- This routine converts specified n-tuple to a character string, which
904 -- is suitable for displaying or printing.
905 --
906 -- The resultant string is never longer than 255 characters. If it gets
907 -- longer, it is truncated from the right and appended by dots. */
908 
format_tuple(MPL * mpl,int c,TUPLE * tuple)909 char *format_tuple
910 (     MPL *mpl,
911       int c,
912       TUPLE *tuple            /* not changed */
913 )
914 {     TUPLE *temp;
915       int dim, j, len;
916       char *buf = mpl->tup_buf, str[255+1], *save;
917 #     define safe_append(c) \
918          (void)(len < 255 ? (buf[len++] = (char)(c)) : 0)
919       buf[0] = '\0', len = 0;
920       dim = tuple_dimen(mpl, tuple);
921       if (c == '[' && dim > 0) safe_append('[');
922       if (c == '(' && dim > 1) safe_append('(');
923       for (temp = tuple; temp != NULL; temp = temp->next)
924       {  if (temp != tuple) safe_append(',');
925          xassert(temp->sym != NULL);
926          save = mpl->sym_buf;
927          mpl->sym_buf = str;
928          format_symbol(mpl, temp->sym);
929          mpl->sym_buf = save;
930          xassert(strlen(str) < sizeof(str));
931          for (j = 0; str[j] != '\0'; j++) safe_append(str[j]);
932       }
933       if (c == '[' && dim > 0) safe_append(']');
934       if (c == '(' && dim > 1) safe_append(')');
935 #     undef safe_append
936       buf[len] = '\0';
937       if (len == 255) strcpy(buf+252, "...");
938       xassert(strlen(buf) <= 255);
939       return buf;
940 }
941 
942 /**********************************************************************/
943 /* * *                       ELEMENTAL SETS                       * * */
944 /**********************************************************************/
945 
946 /*----------------------------------------------------------------------
947 -- create_elemset - create elemental set.
948 --
949 -- This routine creates an elemental set, whose members are n-tuples of
950 -- specified dimension. Being created the set is initially empty. */
951 
create_elemset(MPL * mpl,int dim)952 ELEMSET *create_elemset(MPL *mpl, int dim)
953 {     ELEMSET *set;
954       xassert(dim > 0);
955       set = create_array(mpl, A_NONE, dim);
956       return set;
957 }
958 
959 /*----------------------------------------------------------------------
960 -- find_tuple - check if elemental set contains given n-tuple.
961 --
962 -- This routine finds given n-tuple in specified elemental set in order
963 -- to check if the set contains that n-tuple. If the n-tuple is found,
964 -- the routine returns pointer to corresponding array member. Otherwise
965 -- null pointer is returned. */
966 
find_tuple(MPL * mpl,ELEMSET * set,TUPLE * tuple)967 MEMBER *find_tuple
968 (     MPL *mpl,
969       ELEMSET *set,           /* not changed */
970       TUPLE *tuple            /* not changed */
971 )
972 {     xassert(set != NULL);
973       xassert(set->type == A_NONE);
974       xassert(set->dim == tuple_dimen(mpl, tuple));
975       return find_member(mpl, set, tuple);
976 }
977 
978 /*----------------------------------------------------------------------
979 -- add_tuple - add new n-tuple to elemental set.
980 --
981 -- This routine adds given n-tuple to specified elemental set.
982 --
983 -- For the sake of efficiency this routine doesn't check whether the
984 -- set already contains the same n-tuple or not. Therefore the calling
985 -- program should use the routine find_tuple (if necessary) in order to
986 -- make sure that the given n-tuple is not contained in the set, since
987 -- duplicate n-tuples within the same set are not allowed. */
988 
add_tuple(MPL * mpl,ELEMSET * set,TUPLE * tuple)989 MEMBER *add_tuple
990 (     MPL *mpl,
991       ELEMSET *set,           /* modified */
992       TUPLE *tuple            /* destroyed */
993 )
994 {     MEMBER *memb;
995       xassert(set != NULL);
996       xassert(set->type == A_NONE);
997       xassert(set->dim == tuple_dimen(mpl, tuple));
998       memb = add_member(mpl, set, tuple);
999       memb->value.none = NULL;
1000       return memb;
1001 }
1002 
1003 /*----------------------------------------------------------------------
1004 -- check_then_add - check and add new n-tuple to elemental set.
1005 --
1006 -- This routine is equivalent to the routine add_tuple except that it
1007 -- does check for duplicate n-tuples. */
1008 
check_then_add(MPL * mpl,ELEMSET * set,TUPLE * tuple)1009 MEMBER *check_then_add
1010 (     MPL *mpl,
1011       ELEMSET *set,           /* modified */
1012       TUPLE *tuple            /* destroyed */
1013 )
1014 {     if (find_tuple(mpl, set, tuple) != NULL)
1015          mpl_error(mpl, "duplicate tuple %s detected", format_tuple(mpl,
1016             '(', tuple));
1017       return add_tuple(mpl, set, tuple);
1018 }
1019 
1020 /*----------------------------------------------------------------------
1021 -- copy_elemset - make copy of elemental set.
1022 --
1023 -- This routine makes an exact copy of elemental set. */
1024 
copy_elemset(MPL * mpl,ELEMSET * set)1025 ELEMSET *copy_elemset
1026 (     MPL *mpl,
1027       ELEMSET *set            /* not changed */
1028 )
1029 {     ELEMSET *copy;
1030       MEMBER *memb;
1031       xassert(set != NULL);
1032       xassert(set->type == A_NONE);
1033       xassert(set->dim > 0);
1034       copy = create_elemset(mpl, set->dim);
1035       for (memb = set->head; memb != NULL; memb = memb->next)
1036          add_tuple(mpl, copy, copy_tuple(mpl, memb->tuple));
1037       return copy;
1038 }
1039 
1040 /*----------------------------------------------------------------------
1041 -- delete_elemset - delete elemental set.
1042 --
1043 -- This routine deletes specified elemental set. */
1044 
delete_elemset(MPL * mpl,ELEMSET * set)1045 void delete_elemset
1046 (     MPL *mpl,
1047       ELEMSET *set            /* destroyed */
1048 )
1049 {     xassert(set != NULL);
1050       xassert(set->type == A_NONE);
1051       delete_array(mpl, set);
1052       return;
1053 }
1054 
1055 /*----------------------------------------------------------------------
1056 -- arelset_size - compute size of "arithmetic" elemental set.
1057 --
1058 -- This routine computes the size of "arithmetic" elemental set, which
1059 -- is specified in the form of arithmetic progression:
1060 --
1061 --    { t0 .. tf by dt }.
1062 --
1063 -- The size is computed using the formula:
1064 --
1065 --    n = max(0, floor((tf - t0) / dt) + 1). */
1066 
arelset_size(MPL * mpl,double t0,double tf,double dt)1067 int arelset_size(MPL *mpl, double t0, double tf, double dt)
1068 {     double temp;
1069       if (dt == 0.0)
1070          mpl_error(mpl, "%.*g .. %.*g by %.*g; zero stride not allowed",
1071             DBL_DIG, t0, DBL_DIG, tf, DBL_DIG, dt);
1072       if (tf > 0.0 && t0 < 0.0 && tf > + 0.999 * DBL_MAX + t0)
1073          temp = +DBL_MAX;
1074       else if (tf < 0.0 && t0 > 0.0 && tf < - 0.999 * DBL_MAX + t0)
1075          temp = -DBL_MAX;
1076       else
1077          temp = tf - t0;
1078       if (fabs(dt) < 1.0 && fabs(temp) > (0.999 * DBL_MAX) * fabs(dt))
1079       {  if (temp > 0.0 && dt > 0.0 || temp < 0.0 && dt < 0.0)
1080             temp = +DBL_MAX;
1081          else
1082             temp = 0.0;
1083       }
1084       else
1085       {  temp = floor(temp / dt) + 1.0;
1086          if (temp < 0.0) temp = 0.0;
1087       }
1088       xassert(temp >= 0.0);
1089       if (temp > (double)(INT_MAX - 1))
1090          mpl_error(mpl, "%.*g .. %.*g by %.*g; set too large",
1091             DBL_DIG, t0, DBL_DIG, tf, DBL_DIG, dt);
1092       return (int)(temp + 0.5);
1093 }
1094 
1095 /*----------------------------------------------------------------------
1096 -- arelset_member - compute member of "arithmetic" elemental set.
1097 --
1098 -- This routine returns a numeric value of symbol, which is equivalent
1099 -- to j-th member of given "arithmetic" elemental set specified in the
1100 -- form of arithmetic progression:
1101 --
1102 --    { t0 .. tf by dt }.
1103 --
1104 -- The symbol value is computed with the formula:
1105 --
1106 --    j-th member = t0 + (j - 1) * dt,
1107 --
1108 -- The number j must satisfy to the restriction 1 <= j <= n, where n is
1109 -- the set size computed by the routine arelset_size. */
1110 
arelset_member(MPL * mpl,double t0,double tf,double dt,int j)1111 double arelset_member(MPL *mpl, double t0, double tf, double dt, int j)
1112 {     xassert(1 <= j && j <= arelset_size(mpl, t0, tf, dt));
1113       return t0 + (double)(j - 1) * dt;
1114 }
1115 
1116 /*----------------------------------------------------------------------
1117 -- create_arelset - create "arithmetic" elemental set.
1118 --
1119 -- This routine creates "arithmetic" elemental set, which is specified
1120 -- in the form of arithmetic progression:
1121 --
1122 --    { t0 .. tf by dt }.
1123 --
1124 -- Components of this set are 1-tuples. */
1125 
create_arelset(MPL * mpl,double t0,double tf,double dt)1126 ELEMSET *create_arelset(MPL *mpl, double t0, double tf, double dt)
1127 {     ELEMSET *set;
1128       int j, n;
1129       set = create_elemset(mpl, 1);
1130       n = arelset_size(mpl, t0, tf, dt);
1131       for (j = 1; j <= n; j++)
1132       {  add_tuple
1133          (  mpl,
1134             set,
1135             expand_tuple
1136             (  mpl,
1137                create_tuple(mpl),
1138                create_symbol_num
1139                (  mpl,
1140                   arelset_member(mpl, t0, tf, dt, j)
1141                )
1142             )
1143          );
1144       }
1145       return set;
1146 }
1147 
1148 /*----------------------------------------------------------------------
1149 -- set_union - union of two elemental sets.
1150 --
1151 -- This routine computes the union:
1152 --
1153 --    X U Y = { j | (j in X) or (j in Y) },
1154 --
1155 -- where X and Y are given elemental sets (destroyed on exit). */
1156 
set_union(MPL * mpl,ELEMSET * X,ELEMSET * Y)1157 ELEMSET *set_union
1158 (     MPL *mpl,
1159       ELEMSET *X,             /* destroyed */
1160       ELEMSET *Y              /* destroyed */
1161 )
1162 {     MEMBER *memb;
1163       xassert(X != NULL);
1164       xassert(X->type == A_NONE);
1165       xassert(X->dim > 0);
1166       xassert(Y != NULL);
1167       xassert(Y->type == A_NONE);
1168       xassert(Y->dim > 0);
1169       xassert(X->dim == Y->dim);
1170       for (memb = Y->head; memb != NULL; memb = memb->next)
1171       {  if (find_tuple(mpl, X, memb->tuple) == NULL)
1172             add_tuple(mpl, X, copy_tuple(mpl, memb->tuple));
1173       }
1174       delete_elemset(mpl, Y);
1175       return X;
1176 }
1177 
1178 /*----------------------------------------------------------------------
1179 -- set_diff - difference between two elemental sets.
1180 --
1181 -- This routine computes the difference:
1182 --
1183 --    X \ Y = { j | (j in X) and (j not in Y) },
1184 --
1185 -- where X and Y are given elemental sets (destroyed on exit). */
1186 
set_diff(MPL * mpl,ELEMSET * X,ELEMSET * Y)1187 ELEMSET *set_diff
1188 (     MPL *mpl,
1189       ELEMSET *X,             /* destroyed */
1190       ELEMSET *Y              /* destroyed */
1191 )
1192 {     ELEMSET *Z;
1193       MEMBER *memb;
1194       xassert(X != NULL);
1195       xassert(X->type == A_NONE);
1196       xassert(X->dim > 0);
1197       xassert(Y != NULL);
1198       xassert(Y->type == A_NONE);
1199       xassert(Y->dim > 0);
1200       xassert(X->dim == Y->dim);
1201       Z = create_elemset(mpl, X->dim);
1202       for (memb = X->head; memb != NULL; memb = memb->next)
1203       {  if (find_tuple(mpl, Y, memb->tuple) == NULL)
1204             add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple));
1205       }
1206       delete_elemset(mpl, X);
1207       delete_elemset(mpl, Y);
1208       return Z;
1209 }
1210 
1211 /*----------------------------------------------------------------------
1212 -- set_symdiff - symmetric difference between two elemental sets.
1213 --
1214 -- This routine computes the symmetric difference:
1215 --
1216 --    X (+) Y = (X \ Y) U (Y \ X),
1217 --
1218 -- where X and Y are given elemental sets (destroyed on exit). */
1219 
set_symdiff(MPL * mpl,ELEMSET * X,ELEMSET * Y)1220 ELEMSET *set_symdiff
1221 (     MPL *mpl,
1222       ELEMSET *X,             /* destroyed */
1223       ELEMSET *Y              /* destroyed */
1224 )
1225 {     ELEMSET *Z;
1226       MEMBER *memb;
1227       xassert(X != NULL);
1228       xassert(X->type == A_NONE);
1229       xassert(X->dim > 0);
1230       xassert(Y != NULL);
1231       xassert(Y->type == A_NONE);
1232       xassert(Y->dim > 0);
1233       xassert(X->dim == Y->dim);
1234       /* Z := X \ Y */
1235       Z = create_elemset(mpl, X->dim);
1236       for (memb = X->head; memb != NULL; memb = memb->next)
1237       {  if (find_tuple(mpl, Y, memb->tuple) == NULL)
1238             add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple));
1239       }
1240       /* Z := Z U (Y \ X) */
1241       for (memb = Y->head; memb != NULL; memb = memb->next)
1242       {  if (find_tuple(mpl, X, memb->tuple) == NULL)
1243             add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple));
1244       }
1245       delete_elemset(mpl, X);
1246       delete_elemset(mpl, Y);
1247       return Z;
1248 }
1249 
1250 /*----------------------------------------------------------------------
1251 -- set_inter - intersection of two elemental sets.
1252 --
1253 -- This routine computes the intersection:
1254 --
1255 --    X ^ Y = { j | (j in X) and (j in Y) },
1256 --
1257 -- where X and Y are given elemental sets (destroyed on exit). */
1258 
set_inter(MPL * mpl,ELEMSET * X,ELEMSET * Y)1259 ELEMSET *set_inter
1260 (     MPL *mpl,
1261       ELEMSET *X,             /* destroyed */
1262       ELEMSET *Y              /* destroyed */
1263 )
1264 {     ELEMSET *Z;
1265       MEMBER *memb;
1266       xassert(X != NULL);
1267       xassert(X->type == A_NONE);
1268       xassert(X->dim > 0);
1269       xassert(Y != NULL);
1270       xassert(Y->type == A_NONE);
1271       xassert(Y->dim > 0);
1272       xassert(X->dim == Y->dim);
1273       Z = create_elemset(mpl, X->dim);
1274       for (memb = X->head; memb != NULL; memb = memb->next)
1275       {  if (find_tuple(mpl, Y, memb->tuple) != NULL)
1276             add_tuple(mpl, Z, copy_tuple(mpl, memb->tuple));
1277       }
1278       delete_elemset(mpl, X);
1279       delete_elemset(mpl, Y);
1280       return Z;
1281 }
1282 
1283 /*----------------------------------------------------------------------
1284 -- set_cross - cross (Cartesian) product of two elemental sets.
1285 --
1286 -- This routine computes the cross (Cartesian) product:
1287 --
1288 --    X x Y = { (i,j) | (i in X) and (j in Y) },
1289 --
1290 -- where X and Y are given elemental sets (destroyed on exit). */
1291 
set_cross(MPL * mpl,ELEMSET * X,ELEMSET * Y)1292 ELEMSET *set_cross
1293 (     MPL *mpl,
1294       ELEMSET *X,             /* destroyed */
1295       ELEMSET *Y              /* destroyed */
1296 )
1297 {     ELEMSET *Z;
1298       MEMBER *memx, *memy;
1299       TUPLE *tuple, *temp;
1300       xassert(X != NULL);
1301       xassert(X->type == A_NONE);
1302       xassert(X->dim > 0);
1303       xassert(Y != NULL);
1304       xassert(Y->type == A_NONE);
1305       xassert(Y->dim > 0);
1306       Z = create_elemset(mpl, X->dim + Y->dim);
1307       for (memx = X->head; memx != NULL; memx = memx->next)
1308       {  for (memy = Y->head; memy != NULL; memy = memy->next)
1309          {  tuple = copy_tuple(mpl, memx->tuple);
1310             for (temp = memy->tuple; temp != NULL; temp = temp->next)
1311                tuple = expand_tuple(mpl, tuple, copy_symbol(mpl,
1312                   temp->sym));
1313             add_tuple(mpl, Z, tuple);
1314          }
1315       }
1316       delete_elemset(mpl, X);
1317       delete_elemset(mpl, Y);
1318       return Z;
1319 }
1320 
1321 /**********************************************************************/
1322 /* * *                    ELEMENTAL VARIABLES                     * * */
1323 /**********************************************************************/
1324 
1325 /* (there are no specific routines for elemental variables) */
1326 
1327 /**********************************************************************/
1328 /* * *                        LINEAR FORMS                        * * */
1329 /**********************************************************************/
1330 
1331 /*----------------------------------------------------------------------
1332 -- constant_term - create constant term.
1333 --
1334 -- This routine creates the linear form, which is a constant term. */
1335 
constant_term(MPL * mpl,double coef)1336 FORMULA *constant_term(MPL *mpl, double coef)
1337 {     FORMULA *form;
1338       if (coef == 0.0)
1339          form = NULL;
1340       else
1341       {  form = dmp_get_atom(mpl->formulae, sizeof(FORMULA));
1342          form->coef = coef;
1343          form->var = NULL;
1344          form->next = NULL;
1345       }
1346       return form;
1347 }
1348 
1349 /*----------------------------------------------------------------------
1350 -- single_variable - create single variable.
1351 --
1352 -- This routine creates the linear form, which is a single elemental
1353 -- variable. */
1354 
single_variable(MPL * mpl,ELEMVAR * var)1355 FORMULA *single_variable
1356 (     MPL *mpl,
1357       ELEMVAR *var            /* referenced */
1358 )
1359 {     FORMULA *form;
1360       xassert(var != NULL);
1361       form = dmp_get_atom(mpl->formulae, sizeof(FORMULA));
1362       form->coef = 1.0;
1363       form->var = var;
1364       form->next = NULL;
1365       return form;
1366 }
1367 
1368 /*----------------------------------------------------------------------
1369 -- copy_formula - make copy of linear form.
1370 --
1371 -- This routine returns an exact copy of linear form. */
1372 
copy_formula(MPL * mpl,FORMULA * form)1373 FORMULA *copy_formula
1374 (     MPL *mpl,
1375       FORMULA *form           /* not changed */
1376 )
1377 {     FORMULA *head, *tail;
1378       if (form == NULL)
1379          head = NULL;
1380       else
1381       {  head = tail = dmp_get_atom(mpl->formulae, sizeof(FORMULA));
1382          for (; form != NULL; form = form->next)
1383          {  tail->coef = form->coef;
1384             tail->var = form->var;
1385             if (form->next != NULL)
1386 tail = (tail->next = dmp_get_atom(mpl->formulae, sizeof(FORMULA)));
1387          }
1388          tail->next = NULL;
1389       }
1390       return head;
1391 }
1392 
1393 /*----------------------------------------------------------------------
1394 -- delete_formula - delete linear form.
1395 --
1396 -- This routine deletes specified linear form. */
1397 
delete_formula(MPL * mpl,FORMULA * form)1398 void delete_formula
1399 (     MPL *mpl,
1400       FORMULA *form           /* destroyed */
1401 )
1402 {     FORMULA *temp;
1403       while (form != NULL)
1404       {  temp = form;
1405          form = form->next;
1406          dmp_free_atom(mpl->formulae, temp, sizeof(FORMULA));
1407       }
1408       return;
1409 }
1410 
1411 /*----------------------------------------------------------------------
1412 -- linear_comb - linear combination of two linear forms.
1413 --
1414 -- This routine computes the linear combination:
1415 --
1416 --    a * fx + b * fy,
1417 --
1418 -- where a and b are numeric coefficients, fx and fy are linear forms
1419 -- (destroyed on exit). */
1420 
linear_comb(MPL * mpl,double a,FORMULA * fx,double b,FORMULA * fy)1421 FORMULA *linear_comb
1422 (     MPL *mpl,
1423       double a, FORMULA *fx,  /* destroyed */
1424       double b, FORMULA *fy   /* destroyed */
1425 )
1426 {     FORMULA *form = NULL, *term, *temp;
1427       double c0 = 0.0;
1428       for (term = fx; term != NULL; term = term->next)
1429       {  if (term->var == NULL)
1430             c0 = fp_add(mpl, c0, fp_mul(mpl, a, term->coef));
1431          else
1432             term->var->temp =
1433                fp_add(mpl, term->var->temp, fp_mul(mpl, a, term->coef));
1434       }
1435       for (term = fy; term != NULL; term = term->next)
1436       {  if (term->var == NULL)
1437             c0 = fp_add(mpl, c0, fp_mul(mpl, b, term->coef));
1438          else
1439             term->var->temp =
1440                fp_add(mpl, term->var->temp, fp_mul(mpl, b, term->coef));
1441       }
1442       for (term = fx; term != NULL; term = term->next)
1443       {  if (term->var != NULL && term->var->temp != 0.0)
1444          {  temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA));
1445             temp->coef = term->var->temp, temp->var = term->var;
1446             temp->next = form, form = temp;
1447             term->var->temp = 0.0;
1448          }
1449       }
1450       for (term = fy; term != NULL; term = term->next)
1451       {  if (term->var != NULL && term->var->temp != 0.0)
1452          {  temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA));
1453             temp->coef = term->var->temp, temp->var = term->var;
1454             temp->next = form, form = temp;
1455             term->var->temp = 0.0;
1456          }
1457       }
1458       if (c0 != 0.0)
1459       {  temp = dmp_get_atom(mpl->formulae, sizeof(FORMULA));
1460          temp->coef = c0, temp->var = NULL;
1461          temp->next = form, form = temp;
1462       }
1463       delete_formula(mpl, fx);
1464       delete_formula(mpl, fy);
1465       return form;
1466 }
1467 
1468 /*----------------------------------------------------------------------
1469 -- remove_constant - remove constant term from linear form.
1470 --
1471 -- This routine removes constant term from linear form and stores its
1472 -- value to given location. */
1473 
remove_constant(MPL * mpl,FORMULA * form,double * coef)1474 FORMULA *remove_constant
1475 (     MPL *mpl,
1476       FORMULA *form,          /* destroyed */
1477       double *coef            /* modified */
1478 )
1479 {     FORMULA *head = NULL, *temp;
1480       *coef = 0.0;
1481       while (form != NULL)
1482       {  temp = form;
1483          form = form->next;
1484          if (temp->var == NULL)
1485          {  /* constant term */
1486             *coef = fp_add(mpl, *coef, temp->coef);
1487             dmp_free_atom(mpl->formulae, temp, sizeof(FORMULA));
1488          }
1489          else
1490          {  /* linear term */
1491             temp->next = head;
1492             head = temp;
1493          }
1494       }
1495       return head;
1496 }
1497 
1498 /*----------------------------------------------------------------------
1499 -- reduce_terms - reduce identical terms in linear form.
1500 --
1501 -- This routine reduces identical terms in specified linear form. */
1502 
reduce_terms(MPL * mpl,FORMULA * form)1503 FORMULA *reduce_terms
1504 (     MPL *mpl,
1505       FORMULA *form           /* destroyed */
1506 )
1507 {     FORMULA *term, *next_term;
1508       double c0 = 0.0;
1509       for (term = form; term != NULL; term = term->next)
1510       {  if (term->var == NULL)
1511             c0 = fp_add(mpl, c0, term->coef);
1512          else
1513             term->var->temp = fp_add(mpl, term->var->temp, term->coef);
1514       }
1515       next_term = form, form = NULL;
1516       for (term = next_term; term != NULL; term = next_term)
1517       {  next_term = term->next;
1518          if (term->var == NULL && c0 != 0.0)
1519          {  term->coef = c0, c0 = 0.0;
1520             term->next = form, form = term;
1521          }
1522          else if (term->var != NULL && term->var->temp != 0.0)
1523          {  term->coef = term->var->temp, term->var->temp = 0.0;
1524             term->next = form, form = term;
1525          }
1526          else
1527             dmp_free_atom(mpl->formulae, term, sizeof(FORMULA));
1528       }
1529       return form;
1530 }
1531 
1532 /**********************************************************************/
1533 /* * *                   ELEMENTAL CONSTRAINTS                    * * */
1534 /**********************************************************************/
1535 
1536 /* (there are no specific routines for elemental constraints) */
1537 
1538 /**********************************************************************/
1539 /* * *                       GENERIC VALUES                       * * */
1540 /**********************************************************************/
1541 
1542 /*----------------------------------------------------------------------
1543 -- delete_value - delete generic value.
1544 --
1545 -- This routine deletes specified generic value.
1546 --
1547 -- NOTE: The generic value to be deleted must be valid. */
1548 
delete_value(MPL * mpl,int type,VALUE * value)1549 void delete_value
1550 (     MPL *mpl,
1551       int type,
1552       VALUE *value            /* content destroyed */
1553 )
1554 {     xassert(value != NULL);
1555       switch (type)
1556       {  case A_NONE:
1557             value->none = NULL;
1558             break;
1559          case A_NUMERIC:
1560             value->num = 0.0;
1561             break;
1562          case A_SYMBOLIC:
1563             delete_symbol(mpl, value->sym), value->sym = NULL;
1564             break;
1565          case A_LOGICAL:
1566             value->bit = 0;
1567             break;
1568          case A_TUPLE:
1569             delete_tuple(mpl, value->tuple), value->tuple = NULL;
1570             break;
1571          case A_ELEMSET:
1572             delete_elemset(mpl, value->set), value->set = NULL;
1573             break;
1574          case A_ELEMVAR:
1575             value->var = NULL;
1576             break;
1577          case A_FORMULA:
1578             delete_formula(mpl, value->form), value->form = NULL;
1579             break;
1580          case A_ELEMCON:
1581             value->con = NULL;
1582             break;
1583          default:
1584             xassert(type != type);
1585       }
1586       return;
1587 }
1588 
1589 /**********************************************************************/
1590 /* * *                SYMBOLICALLY INDEXED ARRAYS                 * * */
1591 /**********************************************************************/
1592 
1593 /*----------------------------------------------------------------------
1594 -- create_array - create array.
1595 --
1596 -- This routine creates an array of specified type and dimension. Being
1597 -- created the array is initially empty.
1598 --
1599 -- The type indicator determines generic values, which can be assigned
1600 -- to the array members:
1601 --
1602 -- A_NONE     - none (members have no assigned values)
1603 -- A_NUMERIC  - floating-point numbers
1604 -- A_SYMBOLIC - symbols
1605 -- A_ELEMSET  - elemental sets
1606 -- A_ELEMVAR  - elemental variables
1607 -- A_ELEMCON  - elemental constraints
1608 --
1609 -- The dimension may be 0, in which case the array consists of the only
1610 -- member (such arrays represent 0-dimensional objects). */
1611 
create_array(MPL * mpl,int type,int dim)1612 ARRAY *create_array(MPL *mpl, int type, int dim)
1613 {     ARRAY *array;
1614       xassert(type == A_NONE || type == A_NUMERIC ||
1615              type == A_SYMBOLIC || type == A_ELEMSET ||
1616              type == A_ELEMVAR || type == A_ELEMCON);
1617       xassert(dim >= 0);
1618       array = dmp_get_atom(mpl->arrays, sizeof(ARRAY));
1619       array->type = type;
1620       array->dim = dim;
1621       array->size = 0;
1622       array->head = NULL;
1623       array->tail = NULL;
1624       array->tree = NULL;
1625       array->prev = NULL;
1626       array->next = mpl->a_list;
1627       /* include the array in the global array list */
1628       if (array->next != NULL) array->next->prev = array;
1629       mpl->a_list = array;
1630       return array;
1631 }
1632 
1633 /*----------------------------------------------------------------------
1634 -- find_member - find array member with given n-tuple.
1635 --
1636 -- This routine finds an array member, which has given n-tuple. If the
1637 -- array is short, the linear search is used. Otherwise the routine
1638 -- autimatically creates the search tree (i.e. the array index) to find
1639 -- members for logarithmic time. */
1640 
compare_member_tuples(void * info,const void * key1,const void * key2)1641 static int compare_member_tuples(void *info, const void *key1,
1642       const void *key2)
1643 {     /* this is an auxiliary routine used to compare keys, which are
1644          n-tuples assigned to array members */
1645       return compare_tuples((MPL *)info, (TUPLE *)key1, (TUPLE *)key2);
1646 }
1647 
find_member(MPL * mpl,ARRAY * array,TUPLE * tuple)1648 MEMBER *find_member
1649 (     MPL *mpl,
1650       ARRAY *array,           /* not changed */
1651       TUPLE *tuple            /* not changed */
1652 )
1653 {     MEMBER *memb;
1654       xassert(array != NULL);
1655       /* the n-tuple must have the same dimension as the array */
1656       xassert(tuple_dimen(mpl, tuple) == array->dim);
1657       /* if the array is large enough, create the search tree and index
1658          all existing members of the array */
1659       if (array->size > 30 && array->tree == NULL)
1660       {  array->tree = avl_create_tree(compare_member_tuples, mpl);
1661          for (memb = array->head; memb != NULL; memb = memb->next)
1662 avl_set_node_link(avl_insert_node(array->tree, memb->tuple),
1663                (void *)memb);
1664       }
1665       /* find a member, which has the given tuple */
1666       if (array->tree == NULL)
1667       {  /* the search tree doesn't exist; use the linear search */
1668          for (memb = array->head; memb != NULL; memb = memb->next)
1669             if (compare_tuples(mpl, memb->tuple, tuple) == 0) break;
1670       }
1671       else
1672       {  /* the search tree exists; use the binary search */
1673          AVLNODE *node;
1674          node = avl_find_node(array->tree, tuple);
1675 memb = (MEMBER *)(node == NULL ? NULL : avl_get_node_link(node));
1676       }
1677       return memb;
1678 }
1679 
1680 /*----------------------------------------------------------------------
1681 -- add_member - add new member to array.
1682 --
1683 -- This routine creates a new member with given n-tuple and adds it to
1684 -- specified array.
1685 --
1686 -- For the sake of efficiency this routine doesn't check whether the
1687 -- array already contains a member with the given n-tuple or not. Thus,
1688 -- if necessary, the calling program should use the routine find_member
1689 -- in order to be sure that the array contains no member with the same
1690 -- n-tuple, because members with duplicate n-tuples are not allowed.
1691 --
1692 -- This routine assigns no generic value to the new member, because the
1693 -- calling program must do that. */
1694 
add_member(MPL * mpl,ARRAY * array,TUPLE * tuple)1695 MEMBER *add_member
1696 (     MPL *mpl,
1697       ARRAY *array,           /* modified */
1698       TUPLE *tuple            /* destroyed */
1699 )
1700 {     MEMBER *memb;
1701       xassert(array != NULL);
1702       /* the n-tuple must have the same dimension as the array */
1703       xassert(tuple_dimen(mpl, tuple) == array->dim);
1704       /* create new member */
1705       memb = dmp_get_atom(mpl->members, sizeof(MEMBER));
1706       memb->tuple = tuple;
1707       memb->next = NULL;
1708       memset(&memb->value, '?', sizeof(VALUE));
1709       /* and append it to the member list */
1710       array->size++;
1711       if (array->head == NULL)
1712          array->head = memb;
1713       else
1714          array->tail->next = memb;
1715       array->tail = memb;
1716       /* if the search tree exists, index the new member */
1717       if (array->tree != NULL)
1718 avl_set_node_link(avl_insert_node(array->tree, memb->tuple),
1719             (void *)memb);
1720       return memb;
1721 }
1722 
1723 /*----------------------------------------------------------------------
1724 -- delete_array - delete array.
1725 --
1726 -- This routine deletes specified array.
1727 --
1728 -- Generic values assigned to the array members are not deleted by this
1729 -- routine. The calling program itself must delete all assigned generic
1730 -- values before deleting the array. */
1731 
delete_array(MPL * mpl,ARRAY * array)1732 void delete_array
1733 (     MPL *mpl,
1734       ARRAY *array            /* destroyed */
1735 )
1736 {     MEMBER *memb;
1737       xassert(array != NULL);
1738       /* delete all existing array members */
1739       while (array->head != NULL)
1740       {  memb = array->head;
1741          array->head = memb->next;
1742          delete_tuple(mpl, memb->tuple);
1743          dmp_free_atom(mpl->members, memb, sizeof(MEMBER));
1744       }
1745       /* if the search tree exists, also delete it */
1746       if (array->tree != NULL) avl_delete_tree(array->tree);
1747       /* remove the array from the global array list */
1748       if (array->prev == NULL)
1749          mpl->a_list = array->next;
1750       else
1751          array->prev->next = array->next;
1752       if (array->next == NULL)
1753          ;
1754       else
1755          array->next->prev = array->prev;
1756       /* delete the array descriptor */
1757       dmp_free_atom(mpl->arrays, array, sizeof(ARRAY));
1758       return;
1759 }
1760 
1761 /**********************************************************************/
1762 /* * *                 DOMAINS AND DUMMY INDICES                  * * */
1763 /**********************************************************************/
1764 
1765 /*----------------------------------------------------------------------
1766 -- assign_dummy_index - assign new value to dummy index.
1767 --
1768 -- This routine assigns new value to specified dummy index and, that is
1769 -- important, invalidates all temporary resultant values, which depends
1770 -- on that dummy index. */
1771 
assign_dummy_index(MPL * mpl,DOMAIN_SLOT * slot,SYMBOL * value)1772 void assign_dummy_index
1773 (     MPL *mpl,
1774       DOMAIN_SLOT *slot,      /* modified */
1775       SYMBOL *value           /* not changed */
1776 )
1777 {     CODE *leaf, *code;
1778       xassert(slot != NULL);
1779       xassert(value != NULL);
1780       /* delete the current value assigned to the dummy index */
1781       if (slot->value != NULL)
1782       {  /* if the current value and the new one are identical, actual
1783             assignment is not needed */
1784          if (compare_symbols(mpl, slot->value, value) == 0) goto done;
1785          /* delete a symbol, which is the current value */
1786          delete_symbol(mpl, slot->value), slot->value = NULL;
1787       }
1788       /* now walk through all the pseudo-codes with op = O_INDEX, which
1789          refer to the dummy index to be changed (these pseudo-codes are
1790          leaves in the forest of *all* expressions in the database) */
1791       for (leaf = slot->list; leaf != NULL; leaf = leaf->arg.index.
1792          next)
1793       {  xassert(leaf->op == O_INDEX);
1794          /* invalidate all resultant values, which depend on the dummy
1795             index, walking from the current leaf toward the root of the
1796             corresponding expression tree */
1797          for (code = leaf; code != NULL; code = code->up)
1798          {  if (code->valid)
1799             {  /* invalidate and delete resultant value */
1800                code->valid = 0;
1801                delete_value(mpl, code->type, &code->value);
1802             }
1803          }
1804       }
1805       /* assign new value to the dummy index */
1806       slot->value = copy_symbol(mpl, value);
1807 done: return;
1808 }
1809 
1810 /*----------------------------------------------------------------------
1811 -- update_dummy_indices - update current values of dummy indices.
1812 --
1813 -- This routine assigns components of "backup" n-tuple to dummy indices
1814 -- of specified domain block. If no "backup" n-tuple is defined for the
1815 -- domain block, values of the dummy indices remain untouched. */
1816 
update_dummy_indices(MPL * mpl,DOMAIN_BLOCK * block)1817 void update_dummy_indices
1818 (     MPL *mpl,
1819       DOMAIN_BLOCK *block     /* not changed */
1820 )
1821 {     DOMAIN_SLOT *slot;
1822       TUPLE *temp;
1823       if (block->backup != NULL)
1824       {  for (slot = block->list, temp = block->backup; slot != NULL;
1825             slot = slot->next, temp = temp->next)
1826          {  xassert(temp != NULL);
1827             xassert(temp->sym != NULL);
1828             assign_dummy_index(mpl, slot, temp->sym);
1829          }
1830       }
1831       return;
1832 }
1833 
1834 /*----------------------------------------------------------------------
1835 -- enter_domain_block - enter domain block.
1836 --
1837 -- Let specified domain block have the form:
1838 --
1839 --    { ..., (j1, j2, ..., jn) in J, ... }
1840 --
1841 -- where j1, j2, ..., jn are dummy indices, J is a basic set.
1842 --
1843 -- This routine does the following:
1844 --
1845 -- 1. Checks if the given n-tuple is a member of the basic set J. Note
1846 --    that J being *out of the scope* of the domain block cannot depend
1847 --    on the dummy indices in the same and inner domain blocks, so it
1848 --    can be computed before the dummy indices are assigned new values.
1849 --    If this check fails, the routine returns with non-zero code.
1850 --
1851 -- 2. Saves current values of the dummy indices j1, j2, ..., jn.
1852 --
1853 -- 3. Assigns new values, which are components of the given n-tuple, to
1854 --    the dummy indices j1, j2, ..., jn. If dimension of the n-tuple is
1855 --    larger than n, its extra components n+1, n+2, ... are not used.
1856 --
1857 -- 4. Calls the formal routine func which either enters the next domain
1858 --    block or evaluates some code within the domain scope.
1859 --
1860 -- 5. Restores former values of the dummy indices j1, j2, ..., jn.
1861 --
1862 -- Since current values assigned to the dummy indices on entry to this
1863 -- routine are restored on exit, the formal routine func is allowed to
1864 -- call this routine recursively. */
1865 
enter_domain_block(MPL * mpl,DOMAIN_BLOCK * block,TUPLE * tuple,void * info,void (* func)(MPL * mpl,void * info))1866 int enter_domain_block
1867 (     MPL *mpl,
1868       DOMAIN_BLOCK *block,    /* not changed */
1869       TUPLE *tuple,           /* not changed */
1870       void *info, void (*func)(MPL *mpl, void *info)
1871 )
1872 {     TUPLE *backup;
1873       int ret = 0;
1874       /* check if the given n-tuple is a member of the basic set */
1875       xassert(block->code != NULL);
1876       if (!is_member(mpl, block->code, tuple))
1877       {  ret = 1;
1878          goto done;
1879       }
1880       /* save reference to "backup" n-tuple, which was used to assign
1881          current values of the dummy indices (it is sufficient to save
1882          reference, not value, because that n-tuple is defined in some
1883          outer level of recursion and therefore cannot be changed on
1884          this and deeper recursive calls) */
1885       backup = block->backup;
1886       /* set up new "backup" n-tuple, which defines new values of the
1887          dummy indices */
1888       block->backup = tuple;
1889       /* assign new values to the dummy indices */
1890       update_dummy_indices(mpl, block);
1891       /* call the formal routine that does the rest part of the job */
1892       func(mpl, info);
1893       /* restore reference to the former "backup" n-tuple */
1894       block->backup = backup;
1895       /* restore former values of the dummy indices; note that if the
1896          domain block just escaped has no other active instances which
1897          may exist due to recursion (it is indicated by a null pointer
1898          to the former n-tuple), former values of the dummy indices are
1899          undefined; therefore in this case the routine keeps currently
1900          assigned values of the dummy indices that involves keeping all
1901          dependent temporary results and thereby, if this domain block
1902          is not used recursively, allows improving efficiency */
1903       update_dummy_indices(mpl, block);
1904 done: return ret;
1905 }
1906 
1907 /*----------------------------------------------------------------------
1908 -- eval_within_domain - perform evaluation within domain scope.
1909 --
1910 -- This routine assigns new values (symbols) to all dummy indices of
1911 -- specified domain and calls the formal routine func, which is used to
1912 -- evaluate some code in the domain scope. Each free dummy index in the
1913 -- domain is assigned a value specified in the corresponding component
1914 -- of given n-tuple. Non-free dummy indices are assigned values, which
1915 -- are computed by this routine.
1916 --
1917 -- Number of components in the given n-tuple must be the same as number
1918 -- of free indices in the domain.
1919 --
1920 -- If the given n-tuple is not a member of the domain set, the routine
1921 -- func is not called, and non-zero code is returned.
1922 --
1923 -- For the sake of convenience it is allowed to specify domain as NULL
1924 -- (then n-tuple also must be 0-tuple, i.e. empty), in which case this
1925 -- routine just calls the routine func and returns zero.
1926 --
1927 -- This routine allows recursive calls from the routine func providing
1928 -- correct values of dummy indices for each instance.
1929 --
1930 -- NOTE: The n-tuple passed to this routine must not be changed by any
1931 --       other routines called from the formal routine func until this
1932 --       routine has returned. */
1933 
1934 struct eval_domain_info
1935 {     /* working info used by the routine eval_within_domain */
1936       DOMAIN *domain;
1937       /* domain, which has to be entered */
1938       DOMAIN_BLOCK *block;
1939       /* domain block, which is currently processed */
1940       TUPLE *tuple;
1941       /* tail of original n-tuple, whose components have to be assigned
1942          to free dummy indices in the current domain block */
1943       void *info;
1944       /* transit pointer passed to the formal routine func */
1945       void (*func)(MPL *mpl, void *info);
1946       /* routine, which has to be executed in the domain scope */
1947       int failure;
1948       /* this flag indicates that given n-tuple is not a member of the
1949          domain set */
1950 };
1951 
eval_domain_func(MPL * mpl,void * _my_info)1952 static void eval_domain_func(MPL *mpl, void *_my_info)
1953 {     /* this routine recursively enters into the domain scope and then
1954          calls the routine func */
1955       struct eval_domain_info *my_info = _my_info;
1956       if (my_info->block != NULL)
1957       {  /* the current domain block to be entered exists */
1958          DOMAIN_BLOCK *block;
1959          DOMAIN_SLOT *slot;
1960          TUPLE *tuple = NULL, *temp = NULL;
1961          /* save pointer to the current domain block */
1962          block = my_info->block;
1963          /* and get ready to enter the next block (if it exists) */
1964          my_info->block = block->next;
1965          /* construct temporary n-tuple, whose components correspond to
1966             dummy indices (slots) of the current domain; components of
1967             the temporary n-tuple that correspond to free dummy indices
1968             are assigned references (not values!) to symbols specified
1969             in the corresponding components of the given n-tuple, while
1970             other components that correspond to non-free dummy indices
1971             are assigned symbolic values computed here */
1972          for (slot = block->list; slot != NULL; slot = slot->next)
1973          {  /* create component that corresponds to the current slot */
1974             if (tuple == NULL)
1975                tuple = temp = dmp_get_atom(mpl->tuples, sizeof(TUPLE));
1976             else
1977 temp = (temp->next = dmp_get_atom(mpl->tuples, sizeof(TUPLE)));
1978             if (slot->code == NULL)
1979             {  /* dummy index is free; take reference to symbol, which
1980                   is specified in the corresponding component of given
1981                   n-tuple */
1982                xassert(my_info->tuple != NULL);
1983                temp->sym = my_info->tuple->sym;
1984                xassert(temp->sym != NULL);
1985                my_info->tuple = my_info->tuple->next;
1986             }
1987             else
1988             {  /* dummy index is non-free; compute symbolic value to be
1989                   temporarily assigned to the dummy index */
1990                temp->sym = eval_symbolic(mpl, slot->code);
1991             }
1992          }
1993          temp->next = NULL;
1994          /* enter the current domain block */
1995          if (enter_domain_block(mpl, block, tuple, my_info,
1996                eval_domain_func)) my_info->failure = 1;
1997          /* delete temporary n-tuple as well as symbols that correspond
1998             to non-free dummy indices (they were computed here) */
1999          for (slot = block->list; slot != NULL; slot = slot->next)
2000          {  xassert(tuple != NULL);
2001             temp = tuple;
2002             tuple = tuple->next;
2003             if (slot->code != NULL)
2004             {  /* dummy index is non-free; delete symbolic value */
2005                delete_symbol(mpl, temp->sym);
2006             }
2007             /* delete component that corresponds to the current slot */
2008             dmp_free_atom(mpl->tuples, temp, sizeof(TUPLE));
2009          }
2010       }
2011       else
2012       {  /* there are no more domain blocks, i.e. we have reached the
2013             domain scope */
2014          xassert(my_info->tuple == NULL);
2015          /* check optional predicate specified for the domain */
2016          if (my_info->domain->code != NULL && !eval_logical(mpl,
2017             my_info->domain->code))
2018          {  /* the predicate is false */
2019             my_info->failure = 2;
2020          }
2021          else
2022          {  /* the predicate is true; do the job */
2023             my_info->func(mpl, my_info->info);
2024          }
2025       }
2026       return;
2027 }
2028 
eval_within_domain(MPL * mpl,DOMAIN * domain,TUPLE * tuple,void * info,void (* func)(MPL * mpl,void * info))2029 int eval_within_domain
2030 (     MPL *mpl,
2031       DOMAIN *domain,         /* not changed */
2032       TUPLE *tuple,           /* not changed */
2033       void *info, void (*func)(MPL *mpl, void *info)
2034 )
2035 {     /* this routine performs evaluation within domain scope */
2036       struct eval_domain_info _my_info, *my_info = &_my_info;
2037       if (domain == NULL)
2038       {  xassert(tuple == NULL);
2039          func(mpl, info);
2040          my_info->failure = 0;
2041       }
2042       else
2043       {  xassert(tuple != NULL);
2044          my_info->domain = domain;
2045          my_info->block = domain->list;
2046          my_info->tuple = tuple;
2047          my_info->info = info;
2048          my_info->func = func;
2049          my_info->failure = 0;
2050          /* enter the very first domain block */
2051          eval_domain_func(mpl, my_info);
2052       }
2053       return my_info->failure;
2054 }
2055 
2056 /*----------------------------------------------------------------------
2057 -- loop_within_domain - perform iterations within domain scope.
2058 --
2059 -- This routine iteratively assigns new values (symbols) to the dummy
2060 -- indices of specified domain by enumerating all n-tuples, which are
2061 -- members of the domain set, and for every n-tuple it calls the formal
2062 -- routine func to evaluate some code within the domain scope.
2063 --
2064 -- If the routine func returns non-zero, enumeration within the domain
2065 -- is prematurely terminated.
2066 --
2067 -- For the sake of convenience it is allowed to specify domain as NULL,
2068 -- in which case this routine just calls the routine func only once and
2069 -- returns zero.
2070 --
2071 -- This routine allows recursive calls from the routine func providing
2072 -- correct values of dummy indices for each instance. */
2073 
2074 struct loop_domain_info
2075 {     /* working info used by the routine loop_within_domain */
2076       DOMAIN *domain;
2077       /* domain, which has to be entered */
2078       DOMAIN_BLOCK *block;
2079       /* domain block, which is currently processed */
2080       int looping;
2081       /* clearing this flag leads to terminating enumeration */
2082       void *info;
2083       /* transit pointer passed to the formal routine func */
2084       int (*func)(MPL *mpl, void *info);
2085       /* routine, which needs to be executed in the domain scope */
2086 };
2087 
loop_domain_func(MPL * mpl,void * _my_info)2088 static void loop_domain_func(MPL *mpl, void *_my_info)
2089 {     /* this routine enumerates all n-tuples in the basic set of the
2090          current domain block, enters recursively into the domain scope
2091          for every n-tuple, and then calls the routine func */
2092       struct loop_domain_info *my_info = _my_info;
2093       if (my_info->block != NULL)
2094       {  /* the current domain block to be entered exists */
2095          DOMAIN_BLOCK *block;
2096          DOMAIN_SLOT *slot;
2097          TUPLE *bound;
2098          /* save pointer to the current domain block */
2099          block = my_info->block;
2100          /* and get ready to enter the next block (if it exists) */
2101          my_info->block = block->next;
2102          /* compute symbolic values, at which non-free dummy indices of
2103             the current domain block are bound; since that values don't
2104             depend on free dummy indices of the current block, they can
2105             be computed once out of the enumeration loop */
2106          bound = create_tuple(mpl);
2107          for (slot = block->list; slot != NULL; slot = slot->next)
2108          {  if (slot->code != NULL)
2109                bound = expand_tuple(mpl, bound, eval_symbolic(mpl,
2110                   slot->code));
2111          }
2112          /* start enumeration */
2113          xassert(block->code != NULL);
2114          if (block->code->op == O_DOTS)
2115          {  /* the basic set is "arithmetic", in which case it doesn't
2116                need to be computed explicitly */
2117             TUPLE *tuple;
2118             int n, j;
2119             double t0, tf, dt;
2120             /* compute "parameters" of the basic set */
2121             t0 = eval_numeric(mpl, block->code->arg.arg.x);
2122             tf = eval_numeric(mpl, block->code->arg.arg.y);
2123             if (block->code->arg.arg.z == NULL)
2124                dt = 1.0;
2125             else
2126                dt = eval_numeric(mpl, block->code->arg.arg.z);
2127             /* determine cardinality of the basic set */
2128             n = arelset_size(mpl, t0, tf, dt);
2129             /* create dummy 1-tuple for members of the basic set */
2130             tuple = expand_tuple(mpl, create_tuple(mpl),
2131                create_symbol_num(mpl, 0.0));
2132             /* in case of "arithmetic" set there is exactly one dummy
2133                index, which cannot be non-free */
2134             xassert(bound == NULL);
2135             /* walk through 1-tuples of the basic set */
2136             for (j = 1; j <= n && my_info->looping; j++)
2137             {  /* construct dummy 1-tuple for the current member */
2138                tuple->sym->num = arelset_member(mpl, t0, tf, dt, j);
2139                /* enter the current domain block */
2140                enter_domain_block(mpl, block, tuple, my_info,
2141                   loop_domain_func);
2142             }
2143             /* delete dummy 1-tuple */
2144             delete_tuple(mpl, tuple);
2145          }
2146          else
2147          {  /* the basic set is of general kind, in which case it needs
2148                to be explicitly computed */
2149             ELEMSET *set;
2150             MEMBER *memb;
2151             TUPLE *temp1, *temp2;
2152             /* compute the basic set */
2153             set = eval_elemset(mpl, block->code);
2154             /* walk through all n-tuples of the basic set */
2155             for (memb = set->head; memb != NULL && my_info->looping;
2156                memb = memb->next)
2157             {  /* all components of the current n-tuple that correspond
2158                   to non-free dummy indices must be feasible; otherwise
2159                   the n-tuple is not in the basic set */
2160                temp1 = memb->tuple;
2161                temp2 = bound;
2162                for (slot = block->list; slot != NULL; slot = slot->next)
2163                {  xassert(temp1 != NULL);
2164                   if (slot->code != NULL)
2165                   {  /* non-free dummy index */
2166                      xassert(temp2 != NULL);
2167                      if (compare_symbols(mpl, temp1->sym, temp2->sym)
2168                         != 0)
2169                      {  /* the n-tuple is not in the basic set */
2170                         goto skip;
2171                      }
2172                      temp2 = temp2->next;
2173                   }
2174                   temp1 = temp1->next;
2175                }
2176                xassert(temp1 == NULL);
2177                xassert(temp2 == NULL);
2178                /* enter the current domain block */
2179                enter_domain_block(mpl, block, memb->tuple, my_info,
2180                   loop_domain_func);
2181 skip:          ;
2182             }
2183             /* delete the basic set */
2184             delete_elemset(mpl, set);
2185          }
2186          /* delete symbolic values binding non-free dummy indices */
2187          delete_tuple(mpl, bound);
2188          /* restore pointer to the current domain block */
2189          my_info->block = block;
2190       }
2191       else
2192       {  /* there are no more domain blocks, i.e. we have reached the
2193             domain scope */
2194          /* check optional predicate specified for the domain */
2195          if (my_info->domain->code != NULL && !eval_logical(mpl,
2196             my_info->domain->code))
2197          {  /* the predicate is false */
2198             /* nop */;
2199          }
2200          else
2201          {  /* the predicate is true; do the job */
2202             my_info->looping = !my_info->func(mpl, my_info->info);
2203          }
2204       }
2205       return;
2206 }
2207 
loop_within_domain(MPL * mpl,DOMAIN * domain,void * info,int (* func)(MPL * mpl,void * info))2208 void loop_within_domain
2209 (     MPL *mpl,
2210       DOMAIN *domain,         /* not changed */
2211       void *info, int (*func)(MPL *mpl, void *info)
2212 )
2213 {     /* this routine performs iterations within domain scope */
2214       struct loop_domain_info _my_info, *my_info = &_my_info;
2215       if (domain == NULL)
2216          func(mpl, info);
2217       else
2218       {  my_info->domain = domain;
2219          my_info->block = domain->list;
2220          my_info->looping = 1;
2221          my_info->info = info;
2222          my_info->func = func;
2223          /* enter the very first domain block */
2224          loop_domain_func(mpl, my_info);
2225       }
2226       return;
2227 }
2228 
2229 /*----------------------------------------------------------------------
2230 -- out_of_domain - raise domain exception.
2231 --
2232 -- This routine is called when a reference is made to a member of some
2233 -- model object, but its n-tuple is out of the object domain. */
2234 
out_of_domain(MPL * mpl,char * name,TUPLE * tuple)2235 void out_of_domain
2236 (     MPL *mpl,
2237       char *name,             /* not changed */
2238       TUPLE *tuple            /* not changed */
2239 )
2240 {     xassert(name != NULL);
2241       xassert(tuple != NULL);
2242       mpl_error(mpl, "%s%s out of domain", name, format_tuple(mpl, '[',
2243          tuple));
2244       /* no return */
2245 }
2246 
2247 /*----------------------------------------------------------------------
2248 -- get_domain_tuple - obtain current n-tuple from domain.
2249 --
2250 -- This routine constructs n-tuple, whose components are current values
2251 -- assigned to *free* dummy indices of specified domain.
2252 --
2253 -- For the sake of convenience it is allowed to specify domain as NULL,
2254 -- in which case this routine returns 0-tuple.
2255 --
2256 -- NOTE: This routine must not be called out of domain scope. */
2257 
get_domain_tuple(MPL * mpl,DOMAIN * domain)2258 TUPLE *get_domain_tuple
2259 (     MPL *mpl,
2260       DOMAIN *domain          /* not changed */
2261 )
2262 {     DOMAIN_BLOCK *block;
2263       DOMAIN_SLOT *slot;
2264       TUPLE *tuple;
2265       tuple = create_tuple(mpl);
2266       if (domain != NULL)
2267       {  for (block = domain->list; block != NULL; block = block->next)
2268          {  for (slot = block->list; slot != NULL; slot = slot->next)
2269             {  if (slot->code == NULL)
2270                {  xassert(slot->value != NULL);
2271                   tuple = expand_tuple(mpl, tuple, copy_symbol(mpl,
2272                      slot->value));
2273                }
2274             }
2275          }
2276       }
2277       return tuple;
2278 }
2279 
2280 /*----------------------------------------------------------------------
2281 -- clean_domain - clean domain.
2282 --
2283 -- This routine cleans specified domain that assumes deleting all stuff
2284 -- dynamically allocated during the generation phase. */
2285 
clean_domain(MPL * mpl,DOMAIN * domain)2286 void clean_domain(MPL *mpl, DOMAIN *domain)
2287 {     DOMAIN_BLOCK *block;
2288       DOMAIN_SLOT *slot;
2289       /* if no domain is specified, do nothing */
2290       if (domain == NULL) goto done;
2291       /* clean all domain blocks */
2292       for (block = domain->list; block != NULL; block = block->next)
2293       {  /* clean all domain slots */
2294          for (slot = block->list; slot != NULL; slot = slot->next)
2295          {  /* clean pseudo-code for computing bound value */
2296             clean_code(mpl, slot->code);
2297             /* delete symbolic value assigned to dummy index */
2298             if (slot->value != NULL)
2299                delete_symbol(mpl, slot->value), slot->value = NULL;
2300          }
2301          /* clean pseudo-code for computing basic set */
2302          clean_code(mpl, block->code);
2303       }
2304       /* clean pseudo-code for computing domain predicate */
2305       clean_code(mpl, domain->code);
2306 done: return;
2307 }
2308 
2309 /**********************************************************************/
2310 /* * *                         MODEL SETS                         * * */
2311 /**********************************************************************/
2312 
2313 /*----------------------------------------------------------------------
2314 -- check_elem_set - check elemental set assigned to set member.
2315 --
2316 -- This routine checks if given elemental set being assigned to member
2317 -- of specified model set satisfies to all restrictions.
2318 --
2319 -- NOTE: This routine must not be called out of domain scope. */
2320 
check_elem_set(MPL * mpl,SET * set,TUPLE * tuple,ELEMSET * refer)2321 void check_elem_set
2322 (     MPL *mpl,
2323       SET *set,               /* not changed */
2324       TUPLE *tuple,           /* not changed */
2325       ELEMSET *refer          /* not changed */
2326 )
2327 {     WITHIN *within;
2328       MEMBER *memb;
2329       int eqno;
2330       /* elemental set must be within all specified supersets */
2331       for (within = set->within, eqno = 1; within != NULL; within =
2332          within->next, eqno++)
2333       {  xassert(within->code != NULL);
2334          for (memb = refer->head; memb != NULL; memb = memb->next)
2335          {  if (!is_member(mpl, within->code, memb->tuple))
2336             {  char buf[255+1];
2337                strcpy(buf, format_tuple(mpl, '(', memb->tuple));
2338                xassert(strlen(buf) < sizeof(buf));
2339                mpl_error(mpl, "%s%s contains %s which not within specified "
2340                   "set; see (%d)", set->name, format_tuple(mpl, '[',
2341                      tuple), buf, eqno);
2342             }
2343          }
2344       }
2345       return;
2346 }
2347 
2348 /*----------------------------------------------------------------------
2349 -- take_member_set - obtain elemental set assigned to set member.
2350 --
2351 -- This routine obtains a reference to elemental set assigned to given
2352 -- member of specified model set and returns it on exit.
2353 --
2354 -- NOTE: This routine must not be called out of domain scope. */
2355 
take_member_set(MPL * mpl,SET * set,TUPLE * tuple)2356 ELEMSET *take_member_set      /* returns reference, not value */
2357 (     MPL *mpl,
2358       SET *set,               /* not changed */
2359       TUPLE *tuple            /* not changed */
2360 )
2361 {     MEMBER *memb;
2362       ELEMSET *refer;
2363       /* find member in the set array */
2364       memb = find_member(mpl, set->array, tuple);
2365       if (memb != NULL)
2366       {  /* member exists, so just take the reference */
2367          refer = memb->value.set;
2368       }
2369       else if (set->assign != NULL)
2370       {  /* compute value using assignment expression */
2371          refer = eval_elemset(mpl, set->assign);
2372 add:     /* check that the elemental set satisfies to all restrictions,
2373             assign it to new member, and add the member to the array */
2374          check_elem_set(mpl, set, tuple, refer);
2375          memb = add_member(mpl, set->array, copy_tuple(mpl, tuple));
2376          memb->value.set = refer;
2377       }
2378       else if (set->option != NULL)
2379       {  /* compute default elemental set */
2380          refer = eval_elemset(mpl, set->option);
2381          goto add;
2382       }
2383       else
2384       {  /* no value (elemental set) is provided */
2385          mpl_error(mpl, "no value for %s%s", set->name, format_tuple(mpl,
2386             '[', tuple));
2387       }
2388       return refer;
2389 }
2390 
2391 /*----------------------------------------------------------------------
2392 -- eval_member_set - evaluate elemental set assigned to set member.
2393 --
2394 -- This routine evaluates a reference to elemental set assigned to given
2395 -- member of specified model set and returns it on exit. */
2396 
2397 struct eval_set_info
2398 {     /* working info used by the routine eval_member_set */
2399       SET *set;
2400       /* model set */
2401       TUPLE *tuple;
2402       /* n-tuple, which defines set member */
2403       MEMBER *memb;
2404       /* normally this pointer is NULL; the routine uses this pointer
2405          to check data provided in the data section, in which case it
2406          points to a member currently checked; this check is performed
2407          automatically only once when a reference to any member occurs
2408          for the first time */
2409       ELEMSET *refer;
2410       /* evaluated reference to elemental set */
2411 };
2412 
eval_set_func(MPL * mpl,void * _info)2413 static void eval_set_func(MPL *mpl, void *_info)
2414 {     /* this is auxiliary routine to work within domain scope */
2415       struct eval_set_info *info = _info;
2416       if (info->memb != NULL)
2417       {  /* checking call; check elemental set being assigned */
2418          check_elem_set(mpl, info->set, info->memb->tuple,
2419             info->memb->value.set);
2420       }
2421       else
2422       {  /* normal call; evaluate member, which has given n-tuple */
2423          info->refer = take_member_set(mpl, info->set, info->tuple);
2424       }
2425       return;
2426 }
2427 
2428 #if 1 /* 12/XII-2008 */
saturate_set(MPL * mpl,SET * set)2429 static void saturate_set(MPL *mpl, SET *set)
2430 {     GADGET *gadget = set->gadget;
2431       ELEMSET *data;
2432       MEMBER *elem, *memb;
2433       TUPLE *tuple, *work[20];
2434       int i;
2435       xprintf("Generating %s...\n", set->name);
2436       eval_whole_set(mpl, gadget->set);
2437       /* gadget set must have exactly one member */
2438       xassert(gadget->set->array != NULL);
2439       xassert(gadget->set->array->head != NULL);
2440       xassert(gadget->set->array->head == gadget->set->array->tail);
2441       data = gadget->set->array->head->value.set;
2442       xassert(data->type == A_NONE);
2443       xassert(data->dim == gadget->set->dimen);
2444       /* walk thru all elements of the plain set */
2445       for (elem = data->head; elem != NULL; elem = elem->next)
2446       {  /* create a copy of n-tuple */
2447          tuple = copy_tuple(mpl, elem->tuple);
2448          /* rearrange component of the n-tuple */
2449          for (i = 0; i < gadget->set->dimen; i++)
2450             work[i] = NULL;
2451          for (i = 0; tuple != NULL; tuple = tuple->next)
2452             work[gadget->ind[i++]-1] = tuple;
2453          xassert(i == gadget->set->dimen);
2454          for (i = 0; i < gadget->set->dimen; i++)
2455          {  xassert(work[i] != NULL);
2456             work[i]->next = work[i+1];
2457          }
2458          /* construct subscript list from first set->dim components */
2459          if (set->dim == 0)
2460             tuple = NULL;
2461          else
2462             tuple = work[0], work[set->dim-1]->next = NULL;
2463          /* find corresponding member of the set to be initialized */
2464          memb = find_member(mpl, set->array, tuple);
2465          if (memb == NULL)
2466          {  /* not found; add new member to the set and assign it empty
2467                elemental set */
2468             memb = add_member(mpl, set->array, tuple);
2469             memb->value.set = create_elemset(mpl, set->dimen);
2470          }
2471          else
2472          {  /* found; free subscript list */
2473             delete_tuple(mpl, tuple);
2474          }
2475          /* construct new n-tuple from rest set->dimen components */
2476          tuple = work[set->dim];
2477          xassert(set->dim + set->dimen == gadget->set->dimen);
2478          work[gadget->set->dimen-1]->next = NULL;
2479          /* and add it to the elemental set assigned to the member
2480             (no check for duplicates is needed) */
2481          add_tuple(mpl, memb->value.set, tuple);
2482       }
2483       /* the set has been saturated with data */
2484       set->data = 1;
2485       return;
2486 }
2487 #endif
2488 
eval_member_set(MPL * mpl,SET * set,TUPLE * tuple)2489 ELEMSET *eval_member_set      /* returns reference, not value */
2490 (     MPL *mpl,
2491       SET *set,               /* not changed */
2492       TUPLE *tuple            /* not changed */
2493 )
2494 {     /* this routine evaluates set member */
2495       struct eval_set_info _info, *info = &_info;
2496       xassert(set->dim == tuple_dimen(mpl, tuple));
2497       info->set = set;
2498       info->tuple = tuple;
2499 #if 1 /* 12/XII-2008 */
2500       if (set->gadget != NULL && set->data == 0)
2501       {  /* initialize the set with data from a plain set */
2502          saturate_set(mpl, set);
2503       }
2504 #endif
2505       if (set->data == 1)
2506       {  /* check data, which are provided in the data section, but not
2507             checked yet */
2508          /* save pointer to the last array member; note that during the
2509             check new members may be added beyond the last member due to
2510             references to the same parameter from default expression as
2511             well as from expressions that define restricting supersets;
2512             however, values assigned to the new members will be checked
2513             by other routine, so we don't need to check them here */
2514          MEMBER *tail = set->array->tail;
2515          /* change the data status to prevent infinite recursive loop
2516             due to references to the same set during the check */
2517          set->data = 2;
2518          /* check elemental sets assigned to array members in the data
2519             section until the marked member has been reached */
2520          for (info->memb = set->array->head; info->memb != NULL;
2521             info->memb = info->memb->next)
2522          {  if (eval_within_domain(mpl, set->domain, info->memb->tuple,
2523                info, eval_set_func))
2524                out_of_domain(mpl, set->name, info->memb->tuple);
2525             if (info->memb == tail) break;
2526          }
2527          /* the check has been finished */
2528       }
2529       /* evaluate member, which has given n-tuple */
2530       info->memb = NULL;
2531       if (eval_within_domain(mpl, info->set->domain, info->tuple, info,
2532          eval_set_func))
2533       out_of_domain(mpl, set->name, info->tuple);
2534       /* bring evaluated reference to the calling program */
2535       return info->refer;
2536 }
2537 
2538 /*----------------------------------------------------------------------
2539 -- eval_whole_set - evaluate model set over entire domain.
2540 --
2541 -- This routine evaluates all members of specified model set over entire
2542 -- domain. */
2543 
whole_set_func(MPL * mpl,void * info)2544 static int whole_set_func(MPL *mpl, void *info)
2545 {     /* this is auxiliary routine to work within domain scope */
2546       SET *set = (SET *)info;
2547       TUPLE *tuple = get_domain_tuple(mpl, set->domain);
2548       eval_member_set(mpl, set, tuple);
2549       delete_tuple(mpl, tuple);
2550       return 0;
2551 }
2552 
eval_whole_set(MPL * mpl,SET * set)2553 void eval_whole_set(MPL *mpl, SET *set)
2554 {     loop_within_domain(mpl, set->domain, set, whole_set_func);
2555       return;
2556 }
2557 
2558 /*----------------------------------------------------------------------
2559 -- clean set - clean model set.
2560 --
2561 -- This routine cleans specified model set that assumes deleting all
2562 -- stuff dynamically allocated during the generation phase. */
2563 
clean_set(MPL * mpl,SET * set)2564 void clean_set(MPL *mpl, SET *set)
2565 {     WITHIN *within;
2566       MEMBER *memb;
2567       /* clean subscript domain */
2568       clean_domain(mpl, set->domain);
2569       /* clean pseudo-code for computing supersets */
2570       for (within = set->within; within != NULL; within = within->next)
2571          clean_code(mpl, within->code);
2572       /* clean pseudo-code for computing assigned value */
2573       clean_code(mpl, set->assign);
2574       /* clean pseudo-code for computing default value */
2575       clean_code(mpl, set->option);
2576       /* reset data status flag */
2577       set->data = 0;
2578       /* delete content array */
2579       for (memb = set->array->head; memb != NULL; memb = memb->next)
2580          delete_value(mpl, set->array->type, &memb->value);
2581       delete_array(mpl, set->array), set->array = NULL;
2582       return;
2583 }
2584 
2585 /**********************************************************************/
2586 /* * *                      MODEL PARAMETERS                      * * */
2587 /**********************************************************************/
2588 
2589 /*----------------------------------------------------------------------
2590 -- check_value_num - check numeric value assigned to parameter member.
2591 --
2592 -- This routine checks if numeric value being assigned to some member
2593 -- of specified numeric model parameter satisfies to all restrictions.
2594 --
2595 -- NOTE: This routine must not be called out of domain scope. */
2596 
check_value_num(MPL * mpl,PARAMETER * par,TUPLE * tuple,double value)2597 void check_value_num
2598 (     MPL *mpl,
2599       PARAMETER *par,         /* not changed */
2600       TUPLE *tuple,           /* not changed */
2601       double value
2602 )
2603 {     CONDITION *cond;
2604       WITHIN *in;
2605       int eqno;
2606       /* the value must satisfy to the parameter type */
2607       switch (par->type)
2608       {  case A_NUMERIC:
2609             break;
2610          case A_INTEGER:
2611             if (value != floor(value))
2612                mpl_error(mpl, "%s%s = %.*g not integer", par->name,
2613                   format_tuple(mpl, '[', tuple), DBL_DIG, value);
2614             break;
2615          case A_BINARY:
2616             if (!(value == 0.0 || value == 1.0))
2617                mpl_error(mpl, "%s%s = %.*g not binary", par->name,
2618                   format_tuple(mpl, '[', tuple), DBL_DIG, value);
2619             break;
2620          default:
2621             xassert(par != par);
2622       }
2623       /* the value must satisfy to all specified conditions */
2624       for (cond = par->cond, eqno = 1; cond != NULL; cond = cond->next,
2625          eqno++)
2626       {  double bound;
2627          char *rho;
2628          xassert(cond->code != NULL);
2629          bound = eval_numeric(mpl, cond->code);
2630          switch (cond->rho)
2631          {  case O_LT:
2632                if (!(value < bound))
2633                {  rho = "<";
2634 err:              mpl_error(mpl, "%s%s = %.*g not %s %.*g; see (%d)",
2635                      par->name, format_tuple(mpl, '[', tuple), DBL_DIG,
2636                      value, rho, DBL_DIG, bound, eqno);
2637                }
2638                break;
2639             case O_LE:
2640                if (!(value <= bound)) { rho = "<="; goto err; }
2641                break;
2642             case O_EQ:
2643                if (!(value == bound)) { rho = "="; goto err; }
2644                break;
2645             case O_GE:
2646                if (!(value >= bound)) { rho = ">="; goto err; }
2647                break;
2648             case O_GT:
2649                if (!(value > bound)) { rho = ">"; goto err; }
2650                break;
2651             case O_NE:
2652                if (!(value != bound)) { rho = "<>"; goto err; }
2653                break;
2654             default:
2655                xassert(cond != cond);
2656          }
2657       }
2658       /* the value must be in all specified supersets */
2659       for (in = par->in, eqno = 1; in != NULL; in = in->next, eqno++)
2660       {  TUPLE *dummy;
2661          xassert(in->code != NULL);
2662          xassert(in->code->dim == 1);
2663          dummy = expand_tuple(mpl, create_tuple(mpl),
2664             create_symbol_num(mpl, value));
2665          if (!is_member(mpl, in->code, dummy))
2666             mpl_error(mpl, "%s%s = %.*g not in specified set; see (%d)",
2667                par->name, format_tuple(mpl, '[', tuple), DBL_DIG,
2668                value, eqno);
2669          delete_tuple(mpl, dummy);
2670       }
2671       return;
2672 }
2673 
2674 /*----------------------------------------------------------------------
2675 -- take_member_num - obtain num. value assigned to parameter member.
2676 --
2677 -- This routine obtains a numeric value assigned to member of specified
2678 -- numeric model parameter and returns it on exit.
2679 --
2680 -- NOTE: This routine must not be called out of domain scope. */
2681 
take_member_num(MPL * mpl,PARAMETER * par,TUPLE * tuple)2682 double take_member_num
2683 (     MPL *mpl,
2684       PARAMETER *par,         /* not changed */
2685       TUPLE *tuple            /* not changed */
2686 )
2687 {     MEMBER *memb;
2688       double value;
2689       /* find member in the parameter array */
2690       memb = find_member(mpl, par->array, tuple);
2691       if (memb != NULL)
2692       {  /* member exists, so just take its value */
2693          value = memb->value.num;
2694       }
2695       else if (par->assign != NULL)
2696       {  /* compute value using assignment expression */
2697          value = eval_numeric(mpl, par->assign);
2698 add:     /* check that the value satisfies to all restrictions, assign
2699             it to new member, and add the member to the array */
2700          check_value_num(mpl, par, tuple, value);
2701          memb = add_member(mpl, par->array, copy_tuple(mpl, tuple));
2702          memb->value.num = value;
2703       }
2704       else if (par->option != NULL)
2705       {  /* compute default value */
2706          value = eval_numeric(mpl, par->option);
2707          goto add;
2708       }
2709       else if (par->defval != NULL)
2710       {  /* take default value provided in the data section */
2711          if (par->defval->str != NULL)
2712             mpl_error(mpl, "cannot convert %s to floating-point number",
2713                format_symbol(mpl, par->defval));
2714          value = par->defval->num;
2715          goto add;
2716       }
2717       else
2718       {  /* no value is provided */
2719          mpl_error(mpl, "no value for %s%s", par->name, format_tuple(mpl,
2720             '[', tuple));
2721       }
2722       return value;
2723 }
2724 
2725 /*----------------------------------------------------------------------
2726 -- eval_member_num - evaluate num. value assigned to parameter member.
2727 --
2728 -- This routine evaluates a numeric value assigned to given member of
2729 -- specified numeric model parameter and returns it on exit. */
2730 
2731 struct eval_num_info
2732 {     /* working info used by the routine eval_member_num */
2733       PARAMETER *par;
2734       /* model parameter  */
2735       TUPLE *tuple;
2736       /* n-tuple, which defines parameter member */
2737       MEMBER *memb;
2738       /* normally this pointer is NULL; the routine uses this pointer
2739          to check data provided in the data section, in which case it
2740          points to a member currently checked; this check is performed
2741          automatically only once when a reference to any member occurs
2742          for the first time */
2743       double value;
2744       /* evaluated numeric value */
2745 };
2746 
eval_num_func(MPL * mpl,void * _info)2747 static void eval_num_func(MPL *mpl, void *_info)
2748 {     /* this is auxiliary routine to work within domain scope */
2749       struct eval_num_info *info = _info;
2750       if (info->memb != NULL)
2751       {  /* checking call; check numeric value being assigned */
2752          check_value_num(mpl, info->par, info->memb->tuple,
2753             info->memb->value.num);
2754       }
2755       else
2756       {  /* normal call; evaluate member, which has given n-tuple */
2757          info->value = take_member_num(mpl, info->par, info->tuple);
2758       }
2759       return;
2760 }
2761 
eval_member_num(MPL * mpl,PARAMETER * par,TUPLE * tuple)2762 double eval_member_num
2763 (     MPL *mpl,
2764       PARAMETER *par,         /* not changed */
2765       TUPLE *tuple            /* not changed */
2766 )
2767 {     /* this routine evaluates numeric parameter member */
2768       struct eval_num_info _info, *info = &_info;
2769       xassert(par->type == A_NUMERIC || par->type == A_INTEGER ||
2770              par->type == A_BINARY);
2771       xassert(par->dim == tuple_dimen(mpl, tuple));
2772       info->par = par;
2773       info->tuple = tuple;
2774       if (par->data == 1)
2775       {  /* check data, which are provided in the data section, but not
2776             checked yet */
2777          /* save pointer to the last array member; note that during the
2778             check new members may be added beyond the last member due to
2779             references to the same parameter from default expression as
2780             well as from expressions that define restricting conditions;
2781             however, values assigned to the new members will be checked
2782             by other routine, so we don't need to check them here */
2783          MEMBER *tail = par->array->tail;
2784          /* change the data status to prevent infinite recursive loop
2785             due to references to the same parameter during the check */
2786          par->data = 2;
2787          /* check values assigned to array members in the data section
2788             until the marked member has been reached */
2789          for (info->memb = par->array->head; info->memb != NULL;
2790             info->memb = info->memb->next)
2791          {  if (eval_within_domain(mpl, par->domain, info->memb->tuple,
2792                info, eval_num_func))
2793                out_of_domain(mpl, par->name, info->memb->tuple);
2794             if (info->memb == tail) break;
2795          }
2796          /* the check has been finished */
2797       }
2798       /* evaluate member, which has given n-tuple */
2799       info->memb = NULL;
2800       if (eval_within_domain(mpl, info->par->domain, info->tuple, info,
2801          eval_num_func))
2802          out_of_domain(mpl, par->name, info->tuple);
2803       /* bring evaluated value to the calling program */
2804       return info->value;
2805 }
2806 
2807 /*----------------------------------------------------------------------
2808 -- check_value_sym - check symbolic value assigned to parameter member.
2809 --
2810 -- This routine checks if symbolic value being assigned to some member
2811 -- of specified symbolic model parameter satisfies to all restrictions.
2812 --
2813 -- NOTE: This routine must not be called out of domain scope. */
2814 
check_value_sym(MPL * mpl,PARAMETER * par,TUPLE * tuple,SYMBOL * value)2815 void check_value_sym
2816 (     MPL *mpl,
2817       PARAMETER *par,         /* not changed */
2818       TUPLE *tuple,           /* not changed */
2819       SYMBOL *value           /* not changed */
2820 )
2821 {     CONDITION *cond;
2822       WITHIN *in;
2823       int eqno;
2824       /* the value must satisfy to all specified conditions */
2825       for (cond = par->cond, eqno = 1; cond != NULL; cond = cond->next,
2826          eqno++)
2827       {  SYMBOL *bound;
2828          char buf[255+1];
2829          xassert(cond->code != NULL);
2830          bound = eval_symbolic(mpl, cond->code);
2831          switch (cond->rho)
2832          {
2833 #if 1 /* 13/VIII-2008 */
2834             case O_LT:
2835                if (!(compare_symbols(mpl, value, bound) < 0))
2836                {  strcpy(buf, format_symbol(mpl, bound));
2837                   xassert(strlen(buf) < sizeof(buf));
2838                   mpl_error(mpl, "%s%s = %s not < %s",
2839                      par->name, format_tuple(mpl, '[', tuple),
2840                      format_symbol(mpl, value), buf, eqno);
2841                }
2842                break;
2843             case O_LE:
2844                if (!(compare_symbols(mpl, value, bound) <= 0))
2845                {  strcpy(buf, format_symbol(mpl, bound));
2846                   xassert(strlen(buf) < sizeof(buf));
2847                   mpl_error(mpl, "%s%s = %s not <= %s",
2848                      par->name, format_tuple(mpl, '[', tuple),
2849                      format_symbol(mpl, value), buf, eqno);
2850                }
2851                break;
2852 #endif
2853             case O_EQ:
2854                if (!(compare_symbols(mpl, value, bound) == 0))
2855                {  strcpy(buf, format_symbol(mpl, bound));
2856                   xassert(strlen(buf) < sizeof(buf));
2857                   mpl_error(mpl, "%s%s = %s not = %s",
2858                      par->name, format_tuple(mpl, '[', tuple),
2859                      format_symbol(mpl, value), buf, eqno);
2860                }
2861                break;
2862 #if 1 /* 13/VIII-2008 */
2863             case O_GE:
2864                if (!(compare_symbols(mpl, value, bound) >= 0))
2865                {  strcpy(buf, format_symbol(mpl, bound));
2866                   xassert(strlen(buf) < sizeof(buf));
2867                   mpl_error(mpl, "%s%s = %s not >= %s",
2868                      par->name, format_tuple(mpl, '[', tuple),
2869                      format_symbol(mpl, value), buf, eqno);
2870                }
2871                break;
2872             case O_GT:
2873                if (!(compare_symbols(mpl, value, bound) > 0))
2874                {  strcpy(buf, format_symbol(mpl, bound));
2875                   xassert(strlen(buf) < sizeof(buf));
2876                   mpl_error(mpl, "%s%s = %s not > %s",
2877                      par->name, format_tuple(mpl, '[', tuple),
2878                      format_symbol(mpl, value), buf, eqno);
2879                }
2880                break;
2881 #endif
2882             case O_NE:
2883                if (!(compare_symbols(mpl, value, bound) != 0))
2884                {  strcpy(buf, format_symbol(mpl, bound));
2885                   xassert(strlen(buf) < sizeof(buf));
2886                   mpl_error(mpl, "%s%s = %s not <> %s",
2887                      par->name, format_tuple(mpl, '[', tuple),
2888                      format_symbol(mpl, value), buf, eqno);
2889                }
2890                break;
2891             default:
2892                xassert(cond != cond);
2893          }
2894          delete_symbol(mpl, bound);
2895       }
2896       /* the value must be in all specified supersets */
2897       for (in = par->in, eqno = 1; in != NULL; in = in->next, eqno++)
2898       {  TUPLE *dummy;
2899          xassert(in->code != NULL);
2900          xassert(in->code->dim == 1);
2901          dummy = expand_tuple(mpl, create_tuple(mpl), copy_symbol(mpl,
2902             value));
2903          if (!is_member(mpl, in->code, dummy))
2904             mpl_error(mpl, "%s%s = %s not in specified set; see (%d)",
2905                par->name, format_tuple(mpl, '[', tuple),
2906                format_symbol(mpl, value), eqno);
2907          delete_tuple(mpl, dummy);
2908       }
2909       return;
2910 }
2911 
2912 /*----------------------------------------------------------------------
2913 -- take_member_sym - obtain symb. value assigned to parameter member.
2914 --
2915 -- This routine obtains a symbolic value assigned to member of specified
2916 -- symbolic model parameter and returns it on exit.
2917 --
2918 -- NOTE: This routine must not be called out of domain scope. */
2919 
take_member_sym(MPL * mpl,PARAMETER * par,TUPLE * tuple)2920 SYMBOL *take_member_sym       /* returns value, not reference */
2921 (     MPL *mpl,
2922       PARAMETER *par,         /* not changed */
2923       TUPLE *tuple            /* not changed */
2924 )
2925 {     MEMBER *memb;
2926       SYMBOL *value;
2927       /* find member in the parameter array */
2928       memb = find_member(mpl, par->array, tuple);
2929       if (memb != NULL)
2930       {  /* member exists, so just take its value */
2931          value = copy_symbol(mpl, memb->value.sym);
2932       }
2933       else if (par->assign != NULL)
2934       {  /* compute value using assignment expression */
2935          value = eval_symbolic(mpl, par->assign);
2936 add:     /* check that the value satisfies to all restrictions, assign
2937             it to new member, and add the member to the array */
2938          check_value_sym(mpl, par, tuple, value);
2939          memb = add_member(mpl, par->array, copy_tuple(mpl, tuple));
2940          memb->value.sym = copy_symbol(mpl, value);
2941       }
2942       else if (par->option != NULL)
2943       {  /* compute default value */
2944          value = eval_symbolic(mpl, par->option);
2945          goto add;
2946       }
2947       else if (par->defval != NULL)
2948       {  /* take default value provided in the data section */
2949          value = copy_symbol(mpl, par->defval);
2950          goto add;
2951       }
2952       else
2953       {  /* no value is provided */
2954          mpl_error(mpl, "no value for %s%s", par->name, format_tuple(mpl,
2955             '[', tuple));
2956       }
2957       return value;
2958 }
2959 
2960 /*----------------------------------------------------------------------
2961 -- eval_member_sym - evaluate symb. value assigned to parameter member.
2962 --
2963 -- This routine evaluates a symbolic value assigned to given member of
2964 -- specified symbolic model parameter and returns it on exit. */
2965 
2966 struct eval_sym_info
2967 {     /* working info used by the routine eval_member_sym */
2968       PARAMETER *par;
2969       /* model parameter */
2970       TUPLE *tuple;
2971       /* n-tuple, which defines parameter member */
2972       MEMBER *memb;
2973       /* normally this pointer is NULL; the routine uses this pointer
2974          to check data provided in the data section, in which case it
2975          points to a member currently checked; this check is performed
2976          automatically only once when a reference to any member occurs
2977          for the first time */
2978       SYMBOL *value;
2979       /* evaluated symbolic value */
2980 };
2981 
eval_sym_func(MPL * mpl,void * _info)2982 static void eval_sym_func(MPL *mpl, void *_info)
2983 {     /* this is auxiliary routine to work within domain scope */
2984       struct eval_sym_info *info = _info;
2985       if (info->memb != NULL)
2986       {  /* checking call; check symbolic value being assigned */
2987          check_value_sym(mpl, info->par, info->memb->tuple,
2988             info->memb->value.sym);
2989       }
2990       else
2991       {  /* normal call; evaluate member, which has given n-tuple */
2992          info->value = take_member_sym(mpl, info->par, info->tuple);
2993       }
2994       return;
2995 }
2996 
eval_member_sym(MPL * mpl,PARAMETER * par,TUPLE * tuple)2997 SYMBOL *eval_member_sym       /* returns value, not reference */
2998 (     MPL *mpl,
2999       PARAMETER *par,         /* not changed */
3000       TUPLE *tuple            /* not changed */
3001 )
3002 {     /* this routine evaluates symbolic parameter member */
3003       struct eval_sym_info _info, *info = &_info;
3004       xassert(par->type == A_SYMBOLIC);
3005       xassert(par->dim == tuple_dimen(mpl, tuple));
3006       info->par = par;
3007       info->tuple = tuple;
3008       if (par->data == 1)
3009       {  /* check data, which are provided in the data section, but not
3010             checked yet */
3011          /* save pointer to the last array member; note that during the
3012             check new members may be added beyond the last member due to
3013             references to the same parameter from default expression as
3014             well as from expressions that define restricting conditions;
3015             however, values assigned to the new members will be checked
3016             by other routine, so we don't need to check them here */
3017          MEMBER *tail = par->array->tail;
3018          /* change the data status to prevent infinite recursive loop
3019             due to references to the same parameter during the check */
3020          par->data = 2;
3021          /* check values assigned to array members in the data section
3022             until the marked member has been reached */
3023          for (info->memb = par->array->head; info->memb != NULL;
3024             info->memb = info->memb->next)
3025          {  if (eval_within_domain(mpl, par->domain, info->memb->tuple,
3026                info, eval_sym_func))
3027                out_of_domain(mpl, par->name, info->memb->tuple);
3028             if (info->memb == tail) break;
3029          }
3030          /* the check has been finished */
3031       }
3032       /* evaluate member, which has given n-tuple */
3033       info->memb = NULL;
3034       if (eval_within_domain(mpl, info->par->domain, info->tuple, info,
3035          eval_sym_func))
3036          out_of_domain(mpl, par->name, info->tuple);
3037       /* bring evaluated value to the calling program */
3038       return info->value;
3039 }
3040 
3041 /*----------------------------------------------------------------------
3042 -- eval_whole_par - evaluate model parameter over entire domain.
3043 --
3044 -- This routine evaluates all members of specified model parameter over
3045 -- entire domain. */
3046 
whole_par_func(MPL * mpl,void * info)3047 static int whole_par_func(MPL *mpl, void *info)
3048 {     /* this is auxiliary routine to work within domain scope */
3049       PARAMETER *par = (PARAMETER *)info;
3050       TUPLE *tuple = get_domain_tuple(mpl, par->domain);
3051       switch (par->type)
3052       {  case A_NUMERIC:
3053          case A_INTEGER:
3054          case A_BINARY:
3055             eval_member_num(mpl, par, tuple);
3056             break;
3057          case A_SYMBOLIC:
3058             delete_symbol(mpl, eval_member_sym(mpl, par, tuple));
3059             break;
3060          default:
3061             xassert(par != par);
3062       }
3063       delete_tuple(mpl, tuple);
3064       return 0;
3065 }
3066 
eval_whole_par(MPL * mpl,PARAMETER * par)3067 void eval_whole_par(MPL *mpl, PARAMETER *par)
3068 {     loop_within_domain(mpl, par->domain, par, whole_par_func);
3069       return;
3070 }
3071 
3072 /*----------------------------------------------------------------------
3073 -- clean_parameter - clean model parameter.
3074 --
3075 -- This routine cleans specified model parameter that assumes deleting
3076 -- all stuff dynamically allocated during the generation phase. */
3077 
clean_parameter(MPL * mpl,PARAMETER * par)3078 void clean_parameter(MPL *mpl, PARAMETER *par)
3079 {     CONDITION *cond;
3080       WITHIN *in;
3081       MEMBER *memb;
3082       /* clean subscript domain */
3083       clean_domain(mpl, par->domain);
3084       /* clean pseudo-code for computing restricting conditions */
3085       for (cond = par->cond; cond != NULL; cond = cond->next)
3086          clean_code(mpl, cond->code);
3087       /* clean pseudo-code for computing restricting supersets */
3088       for (in = par->in; in != NULL; in = in->next)
3089          clean_code(mpl, in->code);
3090       /* clean pseudo-code for computing assigned value */
3091       clean_code(mpl, par->assign);
3092       /* clean pseudo-code for computing default value */
3093       clean_code(mpl, par->option);
3094       /* reset data status flag */
3095       par->data = 0;
3096       /* delete default symbolic value */
3097       if (par->defval != NULL)
3098          delete_symbol(mpl, par->defval), par->defval = NULL;
3099       /* delete content array */
3100       for (memb = par->array->head; memb != NULL; memb = memb->next)
3101          delete_value(mpl, par->array->type, &memb->value);
3102       delete_array(mpl, par->array), par->array = NULL;
3103       return;
3104 }
3105 
3106 /**********************************************************************/
3107 /* * *                      MODEL VARIABLES                       * * */
3108 /**********************************************************************/
3109 
3110 /*----------------------------------------------------------------------
3111 -- take_member_var - obtain reference to elemental variable.
3112 --
3113 -- This routine obtains a reference to elemental variable assigned to
3114 -- given member of specified model variable and returns it on exit. If
3115 -- necessary, new elemental variable is created.
3116 --
3117 -- NOTE: This routine must not be called out of domain scope. */
3118 
take_member_var(MPL * mpl,VARIABLE * var,TUPLE * tuple)3119 ELEMVAR *take_member_var      /* returns reference */
3120 (     MPL *mpl,
3121       VARIABLE *var,          /* not changed */
3122       TUPLE *tuple            /* not changed */
3123 )
3124 {     MEMBER *memb;
3125       ELEMVAR *refer;
3126       /* find member in the variable array */
3127       memb = find_member(mpl, var->array, tuple);
3128       if (memb != NULL)
3129       {  /* member exists, so just take the reference */
3130          refer = memb->value.var;
3131       }
3132       else
3133       {  /* member is referenced for the first time and therefore does
3134             not exist; create new elemental variable, assign it to new
3135             member, and add the member to the variable array */
3136          memb = add_member(mpl, var->array, copy_tuple(mpl, tuple));
3137          refer = (memb->value.var =
3138             dmp_get_atom(mpl->elemvars, sizeof(ELEMVAR)));
3139          refer->j = 0;
3140          refer->var = var;
3141          refer->memb = memb;
3142          /* compute lower bound */
3143          if (var->lbnd == NULL)
3144             refer->lbnd = 0.0;
3145          else
3146             refer->lbnd = eval_numeric(mpl, var->lbnd);
3147          /* compute upper bound */
3148          if (var->ubnd == NULL)
3149             refer->ubnd = 0.0;
3150          else if (var->ubnd == var->lbnd)
3151             refer->ubnd = refer->lbnd;
3152          else
3153             refer->ubnd = eval_numeric(mpl, var->ubnd);
3154          /* nullify working quantity */
3155          refer->temp = 0.0;
3156 #if 1 /* 15/V-2010 */
3157          /* solution has not been obtained by the solver yet */
3158          refer->stat = 0;
3159          refer->prim = refer->dual = 0.0;
3160 #endif
3161       }
3162       return refer;
3163 }
3164 
3165 /*----------------------------------------------------------------------
3166 -- eval_member_var - evaluate reference to elemental variable.
3167 --
3168 -- This routine evaluates a reference to elemental variable assigned to
3169 -- member of specified model variable and returns it on exit. */
3170 
3171 struct eval_var_info
3172 {     /* working info used by the routine eval_member_var */
3173       VARIABLE *var;
3174       /* model variable */
3175       TUPLE *tuple;
3176       /* n-tuple, which defines variable member */
3177       ELEMVAR *refer;
3178       /* evaluated reference to elemental variable */
3179 };
3180 
eval_var_func(MPL * mpl,void * _info)3181 static void eval_var_func(MPL *mpl, void *_info)
3182 {     /* this is auxiliary routine to work within domain scope */
3183       struct eval_var_info *info = _info;
3184       info->refer = take_member_var(mpl, info->var, info->tuple);
3185       return;
3186 }
3187 
eval_member_var(MPL * mpl,VARIABLE * var,TUPLE * tuple)3188 ELEMVAR *eval_member_var      /* returns reference */
3189 (     MPL *mpl,
3190       VARIABLE *var,          /* not changed */
3191       TUPLE *tuple            /* not changed */
3192 )
3193 {     /* this routine evaluates variable member */
3194       struct eval_var_info _info, *info = &_info;
3195       xassert(var->dim == tuple_dimen(mpl, tuple));
3196       info->var = var;
3197       info->tuple = tuple;
3198       /* evaluate member, which has given n-tuple */
3199       if (eval_within_domain(mpl, info->var->domain, info->tuple, info,
3200          eval_var_func))
3201          out_of_domain(mpl, var->name, info->tuple);
3202       /* bring evaluated reference to the calling program */
3203       return info->refer;
3204 }
3205 
3206 /*----------------------------------------------------------------------
3207 -- eval_whole_var - evaluate model variable over entire domain.
3208 --
3209 -- This routine evaluates all members of specified model variable over
3210 -- entire domain. */
3211 
whole_var_func(MPL * mpl,void * info)3212 static int whole_var_func(MPL *mpl, void *info)
3213 {     /* this is auxiliary routine to work within domain scope */
3214       VARIABLE *var = (VARIABLE *)info;
3215       TUPLE *tuple = get_domain_tuple(mpl, var->domain);
3216       eval_member_var(mpl, var, tuple);
3217       delete_tuple(mpl, tuple);
3218       return 0;
3219 }
3220 
eval_whole_var(MPL * mpl,VARIABLE * var)3221 void eval_whole_var(MPL *mpl, VARIABLE *var)
3222 {     loop_within_domain(mpl, var->domain, var, whole_var_func);
3223       return;
3224 }
3225 
3226 /*----------------------------------------------------------------------
3227 -- clean_variable - clean model variable.
3228 --
3229 -- This routine cleans specified model variable that assumes deleting
3230 -- all stuff dynamically allocated during the generation phase. */
3231 
clean_variable(MPL * mpl,VARIABLE * var)3232 void clean_variable(MPL *mpl, VARIABLE *var)
3233 {     MEMBER *memb;
3234       /* clean subscript domain */
3235       clean_domain(mpl, var->domain);
3236       /* clean code for computing lower bound */
3237       clean_code(mpl, var->lbnd);
3238       /* clean code for computing upper bound */
3239       if (var->ubnd != var->lbnd) clean_code(mpl, var->ubnd);
3240       /* delete content array */
3241       for (memb = var->array->head; memb != NULL; memb = memb->next)
3242          dmp_free_atom(mpl->elemvars, memb->value.var, sizeof(ELEMVAR));
3243       delete_array(mpl, var->array), var->array = NULL;
3244       return;
3245 }
3246 
3247 /**********************************************************************/
3248 /* * *              MODEL CONSTRAINTS AND OBJECTIVES              * * */
3249 /**********************************************************************/
3250 
3251 /*----------------------------------------------------------------------
3252 -- take_member_con - obtain reference to elemental constraint.
3253 --
3254 -- This routine obtains a reference to elemental constraint assigned
3255 -- to given member of specified model constraint and returns it on exit.
3256 -- If necessary, new elemental constraint is created.
3257 --
3258 -- NOTE: This routine must not be called out of domain scope. */
3259 
take_member_con(MPL * mpl,CONSTRAINT * con,TUPLE * tuple)3260 ELEMCON *take_member_con      /* returns reference */
3261 (     MPL *mpl,
3262       CONSTRAINT *con,        /* not changed */
3263       TUPLE *tuple            /* not changed */
3264 )
3265 {     MEMBER *memb;
3266       ELEMCON *refer;
3267       /* find member in the constraint array */
3268       memb = find_member(mpl, con->array, tuple);
3269       if (memb != NULL)
3270       {  /* member exists, so just take the reference */
3271          refer = memb->value.con;
3272       }
3273       else
3274       {  /* member is referenced for the first time and therefore does
3275             not exist; create new elemental constraint, assign it to new
3276             member, and add the member to the constraint array */
3277          memb = add_member(mpl, con->array, copy_tuple(mpl, tuple));
3278          refer = (memb->value.con =
3279             dmp_get_atom(mpl->elemcons, sizeof(ELEMCON)));
3280          refer->i = 0;
3281          refer->con = con;
3282          refer->memb = memb;
3283          /* compute linear form */
3284          xassert(con->code != NULL);
3285          refer->form = eval_formula(mpl, con->code);
3286          /* compute lower and upper bounds */
3287          if (con->lbnd == NULL && con->ubnd == NULL)
3288          {  /* objective has no bounds */
3289             double temp;
3290             xassert(con->type == A_MINIMIZE || con->type == A_MAXIMIZE);
3291             /* carry the constant term to the right-hand side */
3292             refer->form = remove_constant(mpl, refer->form, &temp);
3293             refer->lbnd = refer->ubnd = - temp;
3294          }
3295          else if (con->lbnd != NULL && con->ubnd == NULL)
3296          {  /* constraint a * x + b >= c * y + d is transformed to the
3297                standard form a * x - c * y >= d - b */
3298             double temp;
3299             xassert(con->type == A_CONSTRAINT);
3300             refer->form = linear_comb(mpl,
3301                +1.0, refer->form,
3302                -1.0, eval_formula(mpl, con->lbnd));
3303             refer->form = remove_constant(mpl, refer->form, &temp);
3304             refer->lbnd = - temp;
3305             refer->ubnd = 0.0;
3306          }
3307          else if (con->lbnd == NULL && con->ubnd != NULL)
3308          {  /* constraint a * x + b <= c * y + d is transformed to the
3309                standard form a * x - c * y <= d - b */
3310             double temp;
3311             xassert(con->type == A_CONSTRAINT);
3312             refer->form = linear_comb(mpl,
3313                +1.0, refer->form,
3314                -1.0, eval_formula(mpl, con->ubnd));
3315             refer->form = remove_constant(mpl, refer->form, &temp);
3316             refer->lbnd = 0.0;
3317             refer->ubnd = - temp;
3318          }
3319          else if (con->lbnd == con->ubnd)
3320          {  /* constraint a * x + b = c * y + d is transformed to the
3321                standard form a * x - c * y = d - b */
3322             double temp;
3323             xassert(con->type == A_CONSTRAINT);
3324             refer->form = linear_comb(mpl,
3325                +1.0, refer->form,
3326                -1.0, eval_formula(mpl, con->lbnd));
3327             refer->form = remove_constant(mpl, refer->form, &temp);
3328             refer->lbnd = refer->ubnd = - temp;
3329          }
3330          else
3331          {  /* ranged constraint c <= a * x + b <= d is transformed to
3332                the standard form c - b <= a * x <= d - b */
3333             double temp, temp1, temp2;
3334             xassert(con->type == A_CONSTRAINT);
3335             refer->form = remove_constant(mpl, refer->form, &temp);
3336             xassert(remove_constant(mpl, eval_formula(mpl, con->lbnd),
3337                &temp1) == NULL);
3338             xassert(remove_constant(mpl, eval_formula(mpl, con->ubnd),
3339                &temp2) == NULL);
3340             refer->lbnd = fp_sub(mpl, temp1, temp);
3341             refer->ubnd = fp_sub(mpl, temp2, temp);
3342          }
3343 #if 1 /* 15/V-2010 */
3344          /* solution has not been obtained by the solver yet */
3345          refer->stat = 0;
3346          refer->prim = refer->dual = 0.0;
3347 #endif
3348       }
3349       return refer;
3350 }
3351 
3352 /*----------------------------------------------------------------------
3353 -- eval_member_con - evaluate reference to elemental constraint.
3354 --
3355 -- This routine evaluates a reference to elemental constraint assigned
3356 -- to member of specified model constraint and returns it on exit. */
3357 
3358 struct eval_con_info
3359 {     /* working info used by the routine eval_member_con */
3360       CONSTRAINT *con;
3361       /* model constraint */
3362       TUPLE *tuple;
3363       /* n-tuple, which defines constraint member */
3364       ELEMCON *refer;
3365       /* evaluated reference to elemental constraint */
3366 };
3367 
eval_con_func(MPL * mpl,void * _info)3368 static void eval_con_func(MPL *mpl, void *_info)
3369 {     /* this is auxiliary routine to work within domain scope */
3370       struct eval_con_info *info = _info;
3371       info->refer = take_member_con(mpl, info->con, info->tuple);
3372       return;
3373 }
3374 
eval_member_con(MPL * mpl,CONSTRAINT * con,TUPLE * tuple)3375 ELEMCON *eval_member_con      /* returns reference */
3376 (     MPL *mpl,
3377       CONSTRAINT *con,        /* not changed */
3378       TUPLE *tuple            /* not changed */
3379 )
3380 {     /* this routine evaluates constraint member */
3381       struct eval_con_info _info, *info = &_info;
3382       xassert(con->dim == tuple_dimen(mpl, tuple));
3383       info->con = con;
3384       info->tuple = tuple;
3385       /* evaluate member, which has given n-tuple */
3386       if (eval_within_domain(mpl, info->con->domain, info->tuple, info,
3387          eval_con_func))
3388          out_of_domain(mpl, con->name, info->tuple);
3389       /* bring evaluated reference to the calling program */
3390       return info->refer;
3391 }
3392 
3393 /*----------------------------------------------------------------------
3394 -- eval_whole_con - evaluate model constraint over entire domain.
3395 --
3396 -- This routine evaluates all members of specified model constraint over
3397 -- entire domain. */
3398 
whole_con_func(MPL * mpl,void * info)3399 static int whole_con_func(MPL *mpl, void *info)
3400 {     /* this is auxiliary routine to work within domain scope */
3401       CONSTRAINT *con = (CONSTRAINT *)info;
3402       TUPLE *tuple = get_domain_tuple(mpl, con->domain);
3403       eval_member_con(mpl, con, tuple);
3404       delete_tuple(mpl, tuple);
3405       return 0;
3406 }
3407 
eval_whole_con(MPL * mpl,CONSTRAINT * con)3408 void eval_whole_con(MPL *mpl, CONSTRAINT *con)
3409 {     loop_within_domain(mpl, con->domain, con, whole_con_func);
3410       return;
3411 }
3412 
3413 /*----------------------------------------------------------------------
3414 -- clean_constraint - clean model constraint.
3415 --
3416 -- This routine cleans specified model constraint that assumes deleting
3417 -- all stuff dynamically allocated during the generation phase. */
3418 
clean_constraint(MPL * mpl,CONSTRAINT * con)3419 void clean_constraint(MPL *mpl, CONSTRAINT *con)
3420 {     MEMBER *memb;
3421       /* clean subscript domain */
3422       clean_domain(mpl, con->domain);
3423       /* clean code for computing main linear form */
3424       clean_code(mpl, con->code);
3425       /* clean code for computing lower bound */
3426       clean_code(mpl, con->lbnd);
3427       /* clean code for computing upper bound */
3428       if (con->ubnd != con->lbnd) clean_code(mpl, con->ubnd);
3429       /* delete content array */
3430       for (memb = con->array->head; memb != NULL; memb = memb->next)
3431       {  delete_formula(mpl, memb->value.con->form);
3432          dmp_free_atom(mpl->elemcons, memb->value.con, sizeof(ELEMCON));
3433       }
3434       delete_array(mpl, con->array), con->array = NULL;
3435       return;
3436 }
3437 
3438 /**********************************************************************/
3439 /* * *                        PSEUDO-CODE                         * * */
3440 /**********************************************************************/
3441 
3442 /*----------------------------------------------------------------------
3443 -- eval_numeric - evaluate pseudo-code to determine numeric value.
3444 --
3445 -- This routine evaluates specified pseudo-code to determine resultant
3446 -- numeric value, which is returned on exit. */
3447 
3448 struct iter_num_info
3449 {     /* working info used by the routine iter_num_func */
3450       CODE *code;
3451       /* pseudo-code for iterated operation to be performed */
3452       double value;
3453       /* resultant value */
3454 };
3455 
iter_num_func(MPL * mpl,void * _info)3456 static int iter_num_func(MPL *mpl, void *_info)
3457 {     /* this is auxiliary routine used to perform iterated operation
3458          on numeric "integrand" within domain scope */
3459       struct iter_num_info *info = _info;
3460       double temp;
3461       temp = eval_numeric(mpl, info->code->arg.loop.x);
3462       switch (info->code->op)
3463       {  case O_SUM:
3464             /* summation over domain */
3465             info->value = fp_add(mpl, info->value, temp);
3466             break;
3467          case O_PROD:
3468             /* multiplication over domain */
3469             info->value = fp_mul(mpl, info->value, temp);
3470             break;
3471          case O_MINIMUM:
3472             /* minimum over domain */
3473             if (info->value > temp) info->value = temp;
3474             break;
3475          case O_MAXIMUM:
3476             /* maximum over domain */
3477             if (info->value < temp) info->value = temp;
3478             break;
3479          default:
3480             xassert(info != info);
3481       }
3482       return 0;
3483 }
3484 
eval_numeric(MPL * mpl,CODE * code)3485 double eval_numeric(MPL *mpl, CODE *code)
3486 {     double value;
3487       xassert(code != NULL);
3488       xassert(code->type == A_NUMERIC);
3489       xassert(code->dim == 0);
3490       /* if the operation has a side effect, invalidate and delete the
3491          resultant value */
3492       if (code->vflag && code->valid)
3493       {  code->valid = 0;
3494          delete_value(mpl, code->type, &code->value);
3495       }
3496       /* if resultant value is valid, no evaluation is needed */
3497       if (code->valid)
3498       {  value = code->value.num;
3499          goto done;
3500       }
3501       /* evaluate pseudo-code recursively */
3502       switch (code->op)
3503       {  case O_NUMBER:
3504             /* take floating-point number */
3505             value = code->arg.num;
3506             break;
3507          case O_MEMNUM:
3508             /* take member of numeric parameter */
3509             {  TUPLE *tuple;
3510                ARG_LIST *e;
3511                tuple = create_tuple(mpl);
3512                for (e = code->arg.par.list; e != NULL; e = e->next)
3513                   tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl,
3514                      e->x));
3515                value = eval_member_num(mpl, code->arg.par.par, tuple);
3516                delete_tuple(mpl, tuple);
3517             }
3518             break;
3519          case O_MEMVAR:
3520             /* take computed value of elemental variable */
3521             {  TUPLE *tuple;
3522                ARG_LIST *e;
3523 #if 1 /* 15/V-2010 */
3524                ELEMVAR *var;
3525 #endif
3526                tuple = create_tuple(mpl);
3527                for (e = code->arg.var.list; e != NULL; e = e->next)
3528                   tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl,
3529                      e->x));
3530 #if 0 /* 15/V-2010 */
3531                value = eval_member_var(mpl, code->arg.var.var, tuple)
3532                   ->value;
3533 #else
3534                var = eval_member_var(mpl, code->arg.var.var, tuple);
3535                switch (code->arg.var.suff)
3536                {  case DOT_LB:
3537                      if (var->var->lbnd == NULL)
3538                         value = -DBL_MAX;
3539                      else
3540                         value = var->lbnd;
3541                      break;
3542                   case DOT_UB:
3543                      if (var->var->ubnd == NULL)
3544                         value = +DBL_MAX;
3545                      else
3546                         value = var->ubnd;
3547                      break;
3548                   case DOT_STATUS:
3549                      value = var->stat;
3550                      break;
3551                   case DOT_VAL:
3552                      value = var->prim;
3553                      break;
3554                   case DOT_DUAL:
3555                      value = var->dual;
3556                      break;
3557                   default:
3558                      xassert(code != code);
3559                }
3560 #endif
3561                delete_tuple(mpl, tuple);
3562             }
3563             break;
3564 #if 1 /* 15/V-2010 */
3565          case O_MEMCON:
3566             /* take computed value of elemental constraint */
3567             {  TUPLE *tuple;
3568                ARG_LIST *e;
3569                ELEMCON *con;
3570                tuple = create_tuple(mpl);
3571                for (e = code->arg.con.list; e != NULL; e = e->next)
3572                   tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl,
3573                      e->x));
3574                con = eval_member_con(mpl, code->arg.con.con, tuple);
3575                switch (code->arg.con.suff)
3576                {  case DOT_LB:
3577                      if (con->con->lbnd == NULL)
3578                         value = -DBL_MAX;
3579                      else
3580                         value = con->lbnd;
3581                      break;
3582                   case DOT_UB:
3583                      if (con->con->ubnd == NULL)
3584                         value = +DBL_MAX;
3585                      else
3586                         value = con->ubnd;
3587                      break;
3588                   case DOT_STATUS:
3589                      value = con->stat;
3590                      break;
3591                   case DOT_VAL:
3592                      value = con->prim;
3593                      break;
3594                   case DOT_DUAL:
3595                      value = con->dual;
3596                      break;
3597                   default:
3598                      xassert(code != code);
3599                }
3600                delete_tuple(mpl, tuple);
3601             }
3602             break;
3603 #endif
3604          case O_IRAND224:
3605             /* pseudo-random in [0, 2^24-1] */
3606             value = fp_irand224(mpl);
3607             break;
3608          case O_UNIFORM01:
3609             /* pseudo-random in [0, 1) */
3610             value = fp_uniform01(mpl);
3611             break;
3612          case O_NORMAL01:
3613             /* gaussian random, mu = 0, sigma = 1 */
3614             value = fp_normal01(mpl);
3615             break;
3616          case O_GMTIME:
3617             /* current calendar time */
3618             value = fn_gmtime(mpl);
3619             break;
3620          case O_CVTNUM:
3621             /* conversion to numeric */
3622             {  SYMBOL *sym;
3623                sym = eval_symbolic(mpl, code->arg.arg.x);
3624 #if 0 /* 23/XI-2008 */
3625                if (sym->str != NULL)
3626                   mpl_error(mpl, "cannot convert %s to floating-point numbe"
3627                      "r", format_symbol(mpl, sym));
3628                value = sym->num;
3629 #else
3630                if (sym->str == NULL)
3631                   value = sym->num;
3632                else
3633                {  if (str2num(sym->str, &value))
3634                      mpl_error(mpl, "cannot convert %s to floating-point nu"
3635                         "mber", format_symbol(mpl, sym));
3636                }
3637 #endif
3638                delete_symbol(mpl, sym);
3639             }
3640             break;
3641          case O_PLUS:
3642             /* unary plus */
3643             value = + eval_numeric(mpl, code->arg.arg.x);
3644             break;
3645          case O_MINUS:
3646             /* unary minus */
3647             value = - eval_numeric(mpl, code->arg.arg.x);
3648             break;
3649          case O_ABS:
3650             /* absolute value */
3651             value = fabs(eval_numeric(mpl, code->arg.arg.x));
3652             break;
3653          case O_CEIL:
3654             /* round upward ("ceiling of x") */
3655             value = ceil(eval_numeric(mpl, code->arg.arg.x));
3656             break;
3657          case O_FLOOR:
3658             /* round downward ("floor of x") */
3659             value = floor(eval_numeric(mpl, code->arg.arg.x));
3660             break;
3661          case O_EXP:
3662             /* base-e exponential */
3663             value = fp_exp(mpl, eval_numeric(mpl, code->arg.arg.x));
3664             break;
3665          case O_LOG:
3666             /* natural logarithm */
3667             value = fp_log(mpl, eval_numeric(mpl, code->arg.arg.x));
3668             break;
3669          case O_LOG10:
3670             /* common (decimal) logarithm */
3671             value = fp_log10(mpl, eval_numeric(mpl, code->arg.arg.x));
3672             break;
3673          case O_SQRT:
3674             /* square root */
3675             value = fp_sqrt(mpl, eval_numeric(mpl, code->arg.arg.x));
3676             break;
3677          case O_SIN:
3678             /* trigonometric sine */
3679             value = fp_sin(mpl, eval_numeric(mpl, code->arg.arg.x));
3680             break;
3681          case O_COS:
3682             /* trigonometric cosine */
3683             value = fp_cos(mpl, eval_numeric(mpl, code->arg.arg.x));
3684             break;
3685          case O_ATAN:
3686             /* trigonometric arctangent (one argument) */
3687             value = fp_atan(mpl, eval_numeric(mpl, code->arg.arg.x));
3688             break;
3689          case O_ATAN2:
3690             /* trigonometric arctangent (two arguments) */
3691             value = fp_atan2(mpl,
3692                eval_numeric(mpl, code->arg.arg.x),
3693                eval_numeric(mpl, code->arg.arg.y));
3694             break;
3695          case O_ROUND:
3696             /* round to nearest integer */
3697             value = fp_round(mpl,
3698                eval_numeric(mpl, code->arg.arg.x), 0.0);
3699             break;
3700          case O_ROUND2:
3701             /* round to n fractional digits */
3702             value = fp_round(mpl,
3703                eval_numeric(mpl, code->arg.arg.x),
3704                eval_numeric(mpl, code->arg.arg.y));
3705             break;
3706          case O_TRUNC:
3707             /* truncate to nearest integer */
3708             value = fp_trunc(mpl,
3709                eval_numeric(mpl, code->arg.arg.x), 0.0);
3710             break;
3711          case O_TRUNC2:
3712             /* truncate to n fractional digits */
3713             value = fp_trunc(mpl,
3714                eval_numeric(mpl, code->arg.arg.x),
3715                eval_numeric(mpl, code->arg.arg.y));
3716             break;
3717          case O_ADD:
3718             /* addition */
3719             value = fp_add(mpl,
3720                eval_numeric(mpl, code->arg.arg.x),
3721                eval_numeric(mpl, code->arg.arg.y));
3722             break;
3723          case O_SUB:
3724             /* subtraction */
3725             value = fp_sub(mpl,
3726                eval_numeric(mpl, code->arg.arg.x),
3727                eval_numeric(mpl, code->arg.arg.y));
3728             break;
3729          case O_LESS:
3730             /* non-negative subtraction */
3731             value = fp_less(mpl,
3732                eval_numeric(mpl, code->arg.arg.x),
3733                eval_numeric(mpl, code->arg.arg.y));
3734             break;
3735          case O_MUL:
3736             /* multiplication */
3737             value = fp_mul(mpl,
3738                eval_numeric(mpl, code->arg.arg.x),
3739                eval_numeric(mpl, code->arg.arg.y));
3740             break;
3741          case O_DIV:
3742             /* division */
3743             value = fp_div(mpl,
3744                eval_numeric(mpl, code->arg.arg.x),
3745                eval_numeric(mpl, code->arg.arg.y));
3746             break;
3747          case O_IDIV:
3748             /* quotient of exact division */
3749             value = fp_idiv(mpl,
3750                eval_numeric(mpl, code->arg.arg.x),
3751                eval_numeric(mpl, code->arg.arg.y));
3752             break;
3753          case O_MOD:
3754             /* remainder of exact division */
3755             value = fp_mod(mpl,
3756                eval_numeric(mpl, code->arg.arg.x),
3757                eval_numeric(mpl, code->arg.arg.y));
3758             break;
3759          case O_POWER:
3760             /* exponentiation (raise to power) */
3761             value = fp_power(mpl,
3762                eval_numeric(mpl, code->arg.arg.x),
3763                eval_numeric(mpl, code->arg.arg.y));
3764             break;
3765          case O_UNIFORM:
3766             /* pseudo-random in [a, b) */
3767             value = fp_uniform(mpl,
3768                eval_numeric(mpl, code->arg.arg.x),
3769                eval_numeric(mpl, code->arg.arg.y));
3770             break;
3771          case O_NORMAL:
3772             /* gaussian random, given mu and sigma */
3773             value = fp_normal(mpl,
3774                eval_numeric(mpl, code->arg.arg.x),
3775                eval_numeric(mpl, code->arg.arg.y));
3776             break;
3777          case O_CARD:
3778             {  ELEMSET *set;
3779                set = eval_elemset(mpl, code->arg.arg.x);
3780                value = set->size;
3781                delete_array(mpl, set);
3782             }
3783             break;
3784          case O_LENGTH:
3785             {  SYMBOL *sym;
3786                char str[MAX_LENGTH+1];
3787                sym = eval_symbolic(mpl, code->arg.arg.x);
3788                if (sym->str == NULL)
3789                   sprintf(str, "%.*g", DBL_DIG, sym->num);
3790                else
3791                   fetch_string(mpl, sym->str, str);
3792                delete_symbol(mpl, sym);
3793                value = strlen(str);
3794             }
3795             break;
3796          case O_STR2TIME:
3797             {  SYMBOL *sym;
3798                char str[MAX_LENGTH+1], fmt[MAX_LENGTH+1];
3799                sym = eval_symbolic(mpl, code->arg.arg.x);
3800                if (sym->str == NULL)
3801                   sprintf(str, "%.*g", DBL_DIG, sym->num);
3802                else
3803                   fetch_string(mpl, sym->str, str);
3804                delete_symbol(mpl, sym);
3805                sym = eval_symbolic(mpl, code->arg.arg.y);
3806                if (sym->str == NULL)
3807                   sprintf(fmt, "%.*g", DBL_DIG, sym->num);
3808                else
3809                   fetch_string(mpl, sym->str, fmt);
3810                delete_symbol(mpl, sym);
3811                value = fn_str2time(mpl, str, fmt);
3812             }
3813             break;
3814          case O_FORK:
3815             /* if-then-else */
3816             if (eval_logical(mpl, code->arg.arg.x))
3817                value = eval_numeric(mpl, code->arg.arg.y);
3818             else if (code->arg.arg.z == NULL)
3819                value = 0.0;
3820             else
3821                value = eval_numeric(mpl, code->arg.arg.z);
3822             break;
3823          case O_MIN:
3824             /* minimal value (n-ary) */
3825             {  ARG_LIST *e;
3826                double temp;
3827                value = +DBL_MAX;
3828                for (e = code->arg.list; e != NULL; e = e->next)
3829                {  temp = eval_numeric(mpl, e->x);
3830                   if (value > temp) value = temp;
3831                }
3832             }
3833             break;
3834          case O_MAX:
3835             /* maximal value (n-ary) */
3836             {  ARG_LIST *e;
3837                double temp;
3838                value = -DBL_MAX;
3839                for (e = code->arg.list; e != NULL; e = e->next)
3840                {  temp = eval_numeric(mpl, e->x);
3841                   if (value < temp) value = temp;
3842                }
3843             }
3844             break;
3845          case O_SUM:
3846             /* summation over domain */
3847             {  struct iter_num_info _info, *info = &_info;
3848                info->code = code;
3849                info->value = 0.0;
3850                loop_within_domain(mpl, code->arg.loop.domain, info,
3851                   iter_num_func);
3852                value = info->value;
3853             }
3854             break;
3855          case O_PROD:
3856             /* multiplication over domain */
3857             {  struct iter_num_info _info, *info = &_info;
3858                info->code = code;
3859                info->value = 1.0;
3860                loop_within_domain(mpl, code->arg.loop.domain, info,
3861                   iter_num_func);
3862                value = info->value;
3863             }
3864             break;
3865          case O_MINIMUM:
3866             /* minimum over domain */
3867             {  struct iter_num_info _info, *info = &_info;
3868                info->code = code;
3869                info->value = +DBL_MAX;
3870                loop_within_domain(mpl, code->arg.loop.domain, info,
3871                   iter_num_func);
3872                if (info->value == +DBL_MAX)
3873                   mpl_error(mpl, "min{} over empty set; result undefined");
3874                value = info->value;
3875             }
3876             break;
3877          case O_MAXIMUM:
3878             /* maximum over domain */
3879             {  struct iter_num_info _info, *info = &_info;
3880                info->code = code;
3881                info->value = -DBL_MAX;
3882                loop_within_domain(mpl, code->arg.loop.domain, info,
3883                   iter_num_func);
3884                if (info->value == -DBL_MAX)
3885                   mpl_error(mpl, "max{} over empty set; result undefined");
3886                value = info->value;
3887             }
3888             break;
3889          default:
3890             xassert(code != code);
3891       }
3892       /* save resultant value */
3893       xassert(!code->valid);
3894       code->valid = 1;
3895       code->value.num = value;
3896 done: return value;
3897 }
3898 
3899 /*----------------------------------------------------------------------
3900 -- eval_symbolic - evaluate pseudo-code to determine symbolic value.
3901 --
3902 -- This routine evaluates specified pseudo-code to determine resultant
3903 -- symbolic value, which is returned on exit. */
3904 
eval_symbolic(MPL * mpl,CODE * code)3905 SYMBOL *eval_symbolic(MPL *mpl, CODE *code)
3906 {     SYMBOL *value;
3907       xassert(code != NULL);
3908       xassert(code->type == A_SYMBOLIC);
3909       xassert(code->dim == 0);
3910       /* if the operation has a side effect, invalidate and delete the
3911          resultant value */
3912       if (code->vflag && code->valid)
3913       {  code->valid = 0;
3914          delete_value(mpl, code->type, &code->value);
3915       }
3916       /* if resultant value is valid, no evaluation is needed */
3917       if (code->valid)
3918       {  value = copy_symbol(mpl, code->value.sym);
3919          goto done;
3920       }
3921       /* evaluate pseudo-code recursively */
3922       switch (code->op)
3923       {  case O_STRING:
3924             /* take character string */
3925             value = create_symbol_str(mpl, create_string(mpl,
3926                code->arg.str));
3927             break;
3928          case O_INDEX:
3929             /* take dummy index */
3930             xassert(code->arg.index.slot->value != NULL);
3931             value = copy_symbol(mpl, code->arg.index.slot->value);
3932             break;
3933          case O_MEMSYM:
3934             /* take member of symbolic parameter */
3935             {  TUPLE *tuple;
3936                ARG_LIST *e;
3937                tuple = create_tuple(mpl);
3938                for (e = code->arg.par.list; e != NULL; e = e->next)
3939                   tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl,
3940                      e->x));
3941                value = eval_member_sym(mpl, code->arg.par.par, tuple);
3942                delete_tuple(mpl, tuple);
3943             }
3944             break;
3945          case O_CVTSYM:
3946             /* conversion to symbolic */
3947             value = create_symbol_num(mpl, eval_numeric(mpl,
3948                code->arg.arg.x));
3949             break;
3950          case O_CONCAT:
3951             /* concatenation */
3952             value = concat_symbols(mpl,
3953                eval_symbolic(mpl, code->arg.arg.x),
3954                eval_symbolic(mpl, code->arg.arg.y));
3955             break;
3956          case O_FORK:
3957             /* if-then-else */
3958             if (eval_logical(mpl, code->arg.arg.x))
3959                value = eval_symbolic(mpl, code->arg.arg.y);
3960             else if (code->arg.arg.z == NULL)
3961                value = create_symbol_num(mpl, 0.0);
3962             else
3963                value = eval_symbolic(mpl, code->arg.arg.z);
3964             break;
3965          case O_SUBSTR:
3966          case O_SUBSTR3:
3967             {  double pos, len;
3968                char str[MAX_LENGTH+1];
3969                value = eval_symbolic(mpl, code->arg.arg.x);
3970                if (value->str == NULL)
3971                   sprintf(str, "%.*g", DBL_DIG, value->num);
3972                else
3973                   fetch_string(mpl, value->str, str);
3974                delete_symbol(mpl, value);
3975                if (code->op == O_SUBSTR)
3976                {  pos = eval_numeric(mpl, code->arg.arg.y);
3977                   if (pos != floor(pos))
3978                      mpl_error(mpl, "substr('...', %.*g); non-integer secon"
3979                         "d argument", DBL_DIG, pos);
3980                   if (pos < 1 || pos > strlen(str) + 1)
3981                      mpl_error(mpl, "substr('...', %.*g); substring out of "
3982                         "range", DBL_DIG, pos);
3983                }
3984                else
3985                {  pos = eval_numeric(mpl, code->arg.arg.y);
3986                   len = eval_numeric(mpl, code->arg.arg.z);
3987                   if (pos != floor(pos) || len != floor(len))
3988                      mpl_error(mpl, "substr('...', %.*g, %.*g); non-integer"
3989                         " second and/or third argument", DBL_DIG, pos,
3990                         DBL_DIG, len);
3991                   if (pos < 1 || len < 0 || pos + len > strlen(str) + 1)
3992                      mpl_error(mpl, "substr('...', %.*g, %.*g); substring o"
3993                         "ut of range", DBL_DIG, pos, DBL_DIG, len);
3994                   str[(int)pos + (int)len - 1] = '\0';
3995                }
3996                value = create_symbol_str(mpl, create_string(mpl, str +
3997                   (int)pos - 1));
3998             }
3999             break;
4000          case O_TIME2STR:
4001             {  double num;
4002                SYMBOL *sym;
4003                char str[MAX_LENGTH+1], fmt[MAX_LENGTH+1];
4004                num = eval_numeric(mpl, code->arg.arg.x);
4005                sym = eval_symbolic(mpl, code->arg.arg.y);
4006                if (sym->str == NULL)
4007                   sprintf(fmt, "%.*g", DBL_DIG, sym->num);
4008                else
4009                   fetch_string(mpl, sym->str, fmt);
4010                delete_symbol(mpl, sym);
4011                fn_time2str(mpl, str, num, fmt);
4012                value = create_symbol_str(mpl, create_string(mpl, str));
4013             }
4014             break;
4015          default:
4016             xassert(code != code);
4017       }
4018       /* save resultant value */
4019       xassert(!code->valid);
4020       code->valid = 1;
4021       code->value.sym = copy_symbol(mpl, value);
4022 done: return value;
4023 }
4024 
4025 /*----------------------------------------------------------------------
4026 -- eval_logical - evaluate pseudo-code to determine logical value.
4027 --
4028 -- This routine evaluates specified pseudo-code to determine resultant
4029 -- logical value, which is returned on exit. */
4030 
4031 struct iter_log_info
4032 {     /* working info used by the routine iter_log_func */
4033       CODE *code;
4034       /* pseudo-code for iterated operation to be performed */
4035       int value;
4036       /* resultant value */
4037 };
4038 
iter_log_func(MPL * mpl,void * _info)4039 static int iter_log_func(MPL *mpl, void *_info)
4040 {     /* this is auxiliary routine used to perform iterated operation
4041          on logical "integrand" within domain scope */
4042       struct iter_log_info *info = _info;
4043       int ret = 0;
4044       switch (info->code->op)
4045       {  case O_FORALL:
4046             /* conjunction over domain */
4047             info->value &= eval_logical(mpl, info->code->arg.loop.x);
4048             if (!info->value) ret = 1;
4049             break;
4050          case O_EXISTS:
4051             /* disjunction over domain */
4052             info->value |= eval_logical(mpl, info->code->arg.loop.x);
4053             if (info->value) ret = 1;
4054             break;
4055          default:
4056             xassert(info != info);
4057       }
4058       return ret;
4059 }
4060 
eval_logical(MPL * mpl,CODE * code)4061 int eval_logical(MPL *mpl, CODE *code)
4062 {     int value;
4063       xassert(code->type == A_LOGICAL);
4064       xassert(code->dim == 0);
4065       /* if the operation has a side effect, invalidate and delete the
4066          resultant value */
4067       if (code->vflag && code->valid)
4068       {  code->valid = 0;
4069          delete_value(mpl, code->type, &code->value);
4070       }
4071       /* if resultant value is valid, no evaluation is needed */
4072       if (code->valid)
4073       {  value = code->value.bit;
4074          goto done;
4075       }
4076       /* evaluate pseudo-code recursively */
4077       switch (code->op)
4078       {  case O_CVTLOG:
4079             /* conversion to logical */
4080             value = (eval_numeric(mpl, code->arg.arg.x) != 0.0);
4081             break;
4082          case O_NOT:
4083             /* negation (logical "not") */
4084             value = !eval_logical(mpl, code->arg.arg.x);
4085             break;
4086          case O_LT:
4087             /* comparison on 'less than' */
4088 #if 0 /* 02/VIII-2008 */
4089             value = (eval_numeric(mpl, code->arg.arg.x) <
4090                      eval_numeric(mpl, code->arg.arg.y));
4091 #else
4092             xassert(code->arg.arg.x != NULL);
4093             if (code->arg.arg.x->type == A_NUMERIC)
4094                value = (eval_numeric(mpl, code->arg.arg.x) <
4095                         eval_numeric(mpl, code->arg.arg.y));
4096             else
4097             {  SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x);
4098                SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y);
4099                value = (compare_symbols(mpl, sym1, sym2) < 0);
4100                delete_symbol(mpl, sym1);
4101                delete_symbol(mpl, sym2);
4102             }
4103 #endif
4104             break;
4105          case O_LE:
4106             /* comparison on 'not greater than' */
4107 #if 0 /* 02/VIII-2008 */
4108             value = (eval_numeric(mpl, code->arg.arg.x) <=
4109                      eval_numeric(mpl, code->arg.arg.y));
4110 #else
4111             xassert(code->arg.arg.x != NULL);
4112             if (code->arg.arg.x->type == A_NUMERIC)
4113                value = (eval_numeric(mpl, code->arg.arg.x) <=
4114                         eval_numeric(mpl, code->arg.arg.y));
4115             else
4116             {  SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x);
4117                SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y);
4118                value = (compare_symbols(mpl, sym1, sym2) <= 0);
4119                delete_symbol(mpl, sym1);
4120                delete_symbol(mpl, sym2);
4121             }
4122 #endif
4123             break;
4124          case O_EQ:
4125             /* comparison on 'equal to' */
4126             xassert(code->arg.arg.x != NULL);
4127             if (code->arg.arg.x->type == A_NUMERIC)
4128                value = (eval_numeric(mpl, code->arg.arg.x) ==
4129                         eval_numeric(mpl, code->arg.arg.y));
4130             else
4131             {  SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x);
4132                SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y);
4133                value = (compare_symbols(mpl, sym1, sym2) == 0);
4134                delete_symbol(mpl, sym1);
4135                delete_symbol(mpl, sym2);
4136             }
4137             break;
4138          case O_GE:
4139             /* comparison on 'not less than' */
4140 #if 0 /* 02/VIII-2008 */
4141             value = (eval_numeric(mpl, code->arg.arg.x) >=
4142                      eval_numeric(mpl, code->arg.arg.y));
4143 #else
4144             xassert(code->arg.arg.x != NULL);
4145             if (code->arg.arg.x->type == A_NUMERIC)
4146                value = (eval_numeric(mpl, code->arg.arg.x) >=
4147                         eval_numeric(mpl, code->arg.arg.y));
4148             else
4149             {  SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x);
4150                SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y);
4151                value = (compare_symbols(mpl, sym1, sym2) >= 0);
4152                delete_symbol(mpl, sym1);
4153                delete_symbol(mpl, sym2);
4154             }
4155 #endif
4156             break;
4157          case O_GT:
4158             /* comparison on 'greater than' */
4159 #if 0 /* 02/VIII-2008 */
4160             value = (eval_numeric(mpl, code->arg.arg.x) >
4161                      eval_numeric(mpl, code->arg.arg.y));
4162 #else
4163             xassert(code->arg.arg.x != NULL);
4164             if (code->arg.arg.x->type == A_NUMERIC)
4165                value = (eval_numeric(mpl, code->arg.arg.x) >
4166                         eval_numeric(mpl, code->arg.arg.y));
4167             else
4168             {  SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x);
4169                SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y);
4170                value = (compare_symbols(mpl, sym1, sym2) > 0);
4171                delete_symbol(mpl, sym1);
4172                delete_symbol(mpl, sym2);
4173             }
4174 #endif
4175             break;
4176          case O_NE:
4177             /* comparison on 'not equal to' */
4178             xassert(code->arg.arg.x != NULL);
4179             if (code->arg.arg.x->type == A_NUMERIC)
4180                value = (eval_numeric(mpl, code->arg.arg.x) !=
4181                         eval_numeric(mpl, code->arg.arg.y));
4182             else
4183             {  SYMBOL *sym1 = eval_symbolic(mpl, code->arg.arg.x);
4184                SYMBOL *sym2 = eval_symbolic(mpl, code->arg.arg.y);
4185                value = (compare_symbols(mpl, sym1, sym2) != 0);
4186                delete_symbol(mpl, sym1);
4187                delete_symbol(mpl, sym2);
4188             }
4189             break;
4190          case O_AND:
4191             /* conjunction (logical "and") */
4192             value = eval_logical(mpl, code->arg.arg.x) &&
4193                     eval_logical(mpl, code->arg.arg.y);
4194             break;
4195          case O_OR:
4196             /* disjunction (logical "or") */
4197             value = eval_logical(mpl, code->arg.arg.x) ||
4198                     eval_logical(mpl, code->arg.arg.y);
4199             break;
4200          case O_IN:
4201             /* test on 'x in Y' */
4202             {  TUPLE *tuple;
4203                tuple = eval_tuple(mpl, code->arg.arg.x);
4204                value = is_member(mpl, code->arg.arg.y, tuple);
4205                delete_tuple(mpl, tuple);
4206             }
4207             break;
4208          case O_NOTIN:
4209             /* test on 'x not in Y' */
4210             {  TUPLE *tuple;
4211                tuple = eval_tuple(mpl, code->arg.arg.x);
4212                value = !is_member(mpl, code->arg.arg.y, tuple);
4213                delete_tuple(mpl, tuple);
4214             }
4215             break;
4216          case O_WITHIN:
4217             /* test on 'X within Y' */
4218             {  ELEMSET *set;
4219                MEMBER *memb;
4220                set = eval_elemset(mpl, code->arg.arg.x);
4221                value = 1;
4222                for (memb = set->head; memb != NULL; memb = memb->next)
4223                {  if (!is_member(mpl, code->arg.arg.y, memb->tuple))
4224                   {  value = 0;
4225                      break;
4226                   }
4227                }
4228                delete_elemset(mpl, set);
4229             }
4230             break;
4231          case O_NOTWITHIN:
4232             /* test on 'X not within Y' */
4233             {  ELEMSET *set;
4234                MEMBER *memb;
4235                set = eval_elemset(mpl, code->arg.arg.x);
4236                value = 1;
4237                for (memb = set->head; memb != NULL; memb = memb->next)
4238                {  if (is_member(mpl, code->arg.arg.y, memb->tuple))
4239                   {  value = 0;
4240                      break;
4241                   }
4242                }
4243                delete_elemset(mpl, set);
4244             }
4245             break;
4246          case O_FORALL:
4247             /* conjunction (A-quantification) */
4248             {  struct iter_log_info _info, *info = &_info;
4249                info->code = code;
4250                info->value = 1;
4251                loop_within_domain(mpl, code->arg.loop.domain, info,
4252                   iter_log_func);
4253                value = info->value;
4254             }
4255             break;
4256          case O_EXISTS:
4257             /* disjunction (E-quantification) */
4258             {  struct iter_log_info _info, *info = &_info;
4259                info->code = code;
4260                info->value = 0;
4261                loop_within_domain(mpl, code->arg.loop.domain, info,
4262                   iter_log_func);
4263                value = info->value;
4264             }
4265             break;
4266          default:
4267             xassert(code != code);
4268       }
4269       /* save resultant value */
4270       xassert(!code->valid);
4271       code->valid = 1;
4272       code->value.bit = value;
4273 done: return value;
4274 }
4275 
4276 /*----------------------------------------------------------------------
4277 -- eval_tuple - evaluate pseudo-code to construct n-tuple.
4278 --
4279 -- This routine evaluates specified pseudo-code to construct resultant
4280 -- n-tuple, which is returned on exit. */
4281 
eval_tuple(MPL * mpl,CODE * code)4282 TUPLE *eval_tuple(MPL *mpl, CODE *code)
4283 {     TUPLE *value;
4284       xassert(code != NULL);
4285       xassert(code->type == A_TUPLE);
4286       xassert(code->dim > 0);
4287       /* if the operation has a side effect, invalidate and delete the
4288          resultant value */
4289       if (code->vflag && code->valid)
4290       {  code->valid = 0;
4291          delete_value(mpl, code->type, &code->value);
4292       }
4293       /* if resultant value is valid, no evaluation is needed */
4294       if (code->valid)
4295       {  value = copy_tuple(mpl, code->value.tuple);
4296          goto done;
4297       }
4298       /* evaluate pseudo-code recursively */
4299       switch (code->op)
4300       {  case O_TUPLE:
4301             /* make n-tuple */
4302             {  ARG_LIST *e;
4303                value = create_tuple(mpl);
4304                for (e = code->arg.list; e != NULL; e = e->next)
4305                   value = expand_tuple(mpl, value, eval_symbolic(mpl,
4306                      e->x));
4307             }
4308             break;
4309          case O_CVTTUP:
4310             /* convert to 1-tuple */
4311             value = expand_tuple(mpl, create_tuple(mpl),
4312                eval_symbolic(mpl, code->arg.arg.x));
4313             break;
4314          default:
4315             xassert(code != code);
4316       }
4317       /* save resultant value */
4318       xassert(!code->valid);
4319       code->valid = 1;
4320       code->value.tuple = copy_tuple(mpl, value);
4321 done: return value;
4322 }
4323 
4324 /*----------------------------------------------------------------------
4325 -- eval_elemset - evaluate pseudo-code to construct elemental set.
4326 --
4327 -- This routine evaluates specified pseudo-code to construct resultant
4328 -- elemental set, which is returned on exit. */
4329 
4330 struct iter_set_info
4331 {     /* working info used by the routine iter_set_func */
4332       CODE *code;
4333       /* pseudo-code for iterated operation to be performed */
4334       ELEMSET *value;
4335       /* resultant value */
4336 };
4337 
iter_set_func(MPL * mpl,void * _info)4338 static int iter_set_func(MPL *mpl, void *_info)
4339 {     /* this is auxiliary routine used to perform iterated operation
4340          on n-tuple "integrand" within domain scope */
4341       struct iter_set_info *info = _info;
4342       TUPLE *tuple;
4343       switch (info->code->op)
4344       {  case O_SETOF:
4345             /* compute next n-tuple and add it to the set; in this case
4346                duplicate n-tuples are silently ignored */
4347             tuple = eval_tuple(mpl, info->code->arg.loop.x);
4348             if (find_tuple(mpl, info->value, tuple) == NULL)
4349                add_tuple(mpl, info->value, tuple);
4350             else
4351                delete_tuple(mpl, tuple);
4352             break;
4353          case O_BUILD:
4354             /* construct next n-tuple using current values assigned to
4355                *free* dummy indices as its components and add it to the
4356                set; in this case duplicate n-tuples cannot appear */
4357             add_tuple(mpl, info->value, get_domain_tuple(mpl,
4358                info->code->arg.loop.domain));
4359             break;
4360          default:
4361             xassert(info != info);
4362       }
4363       return 0;
4364 }
4365 
eval_elemset(MPL * mpl,CODE * code)4366 ELEMSET *eval_elemset(MPL *mpl, CODE *code)
4367 {     ELEMSET *value;
4368       xassert(code != NULL);
4369       xassert(code->type == A_ELEMSET);
4370       xassert(code->dim > 0);
4371       /* if the operation has a side effect, invalidate and delete the
4372          resultant value */
4373       if (code->vflag && code->valid)
4374       {  code->valid = 0;
4375          delete_value(mpl, code->type, &code->value);
4376       }
4377       /* if resultant value is valid, no evaluation is needed */
4378       if (code->valid)
4379       {  value = copy_elemset(mpl, code->value.set);
4380          goto done;
4381       }
4382       /* evaluate pseudo-code recursively */
4383       switch (code->op)
4384       {  case O_MEMSET:
4385             /* take member of set */
4386             {  TUPLE *tuple;
4387                ARG_LIST *e;
4388                tuple = create_tuple(mpl);
4389                for (e = code->arg.set.list; e != NULL; e = e->next)
4390                   tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl,
4391                      e->x));
4392                value = copy_elemset(mpl,
4393                   eval_member_set(mpl, code->arg.set.set, tuple));
4394                delete_tuple(mpl, tuple);
4395             }
4396             break;
4397          case O_MAKE:
4398             /* make elemental set of n-tuples */
4399             {  ARG_LIST *e;
4400                value = create_elemset(mpl, code->dim);
4401                for (e = code->arg.list; e != NULL; e = e->next)
4402                   check_then_add(mpl, value, eval_tuple(mpl, e->x));
4403             }
4404             break;
4405          case O_UNION:
4406             /* union of two elemental sets */
4407             value = set_union(mpl,
4408                eval_elemset(mpl, code->arg.arg.x),
4409                eval_elemset(mpl, code->arg.arg.y));
4410             break;
4411          case O_DIFF:
4412             /* difference between two elemental sets */
4413             value = set_diff(mpl,
4414                eval_elemset(mpl, code->arg.arg.x),
4415                eval_elemset(mpl, code->arg.arg.y));
4416             break;
4417          case O_SYMDIFF:
4418             /* symmetric difference between two elemental sets */
4419             value = set_symdiff(mpl,
4420                eval_elemset(mpl, code->arg.arg.x),
4421                eval_elemset(mpl, code->arg.arg.y));
4422             break;
4423          case O_INTER:
4424             /* intersection of two elemental sets */
4425             value = set_inter(mpl,
4426                eval_elemset(mpl, code->arg.arg.x),
4427                eval_elemset(mpl, code->arg.arg.y));
4428             break;
4429          case O_CROSS:
4430             /* cross (Cartesian) product of two elemental sets */
4431             value = set_cross(mpl,
4432                eval_elemset(mpl, code->arg.arg.x),
4433                eval_elemset(mpl, code->arg.arg.y));
4434             break;
4435          case O_DOTS:
4436             /* build "arithmetic" elemental set */
4437             value = create_arelset(mpl,
4438                eval_numeric(mpl, code->arg.arg.x),
4439                eval_numeric(mpl, code->arg.arg.y),
4440                code->arg.arg.z == NULL ? 1.0 : eval_numeric(mpl,
4441                   code->arg.arg.z));
4442             break;
4443          case O_FORK:
4444             /* if-then-else */
4445             if (eval_logical(mpl, code->arg.arg.x))
4446                value = eval_elemset(mpl, code->arg.arg.y);
4447             else
4448                value = eval_elemset(mpl, code->arg.arg.z);
4449             break;
4450          case O_SETOF:
4451             /* compute elemental set */
4452             {  struct iter_set_info _info, *info = &_info;
4453                info->code = code;
4454                info->value = create_elemset(mpl, code->dim);
4455                loop_within_domain(mpl, code->arg.loop.domain, info,
4456                   iter_set_func);
4457                value = info->value;
4458             }
4459             break;
4460          case O_BUILD:
4461             /* build elemental set identical to domain set */
4462             {  struct iter_set_info _info, *info = &_info;
4463                info->code = code;
4464                info->value = create_elemset(mpl, code->dim);
4465                loop_within_domain(mpl, code->arg.loop.domain, info,
4466                   iter_set_func);
4467                value = info->value;
4468             }
4469             break;
4470          default:
4471             xassert(code != code);
4472       }
4473       /* save resultant value */
4474       xassert(!code->valid);
4475       code->valid = 1;
4476       code->value.set = copy_elemset(mpl, value);
4477 done: return value;
4478 }
4479 
4480 /*----------------------------------------------------------------------
4481 -- is_member - check if n-tuple is in set specified by pseudo-code.
4482 --
4483 -- This routine checks if given n-tuple is a member of elemental set
4484 -- specified in the form of pseudo-code (i.e. by expression).
4485 --
4486 -- The n-tuple may have more components that dimension of the elemental
4487 -- set, in which case the extra components are ignored. */
4488 
null_func(MPL * mpl,void * info)4489 static void null_func(MPL *mpl, void *info)
4490 {     /* this is dummy routine used to enter the domain scope */
4491       xassert(mpl == mpl);
4492       xassert(info == NULL);
4493       return;
4494 }
4495 
is_member(MPL * mpl,CODE * code,TUPLE * tuple)4496 int is_member(MPL *mpl, CODE *code, TUPLE *tuple)
4497 {     int value;
4498       xassert(code != NULL);
4499       xassert(code->type == A_ELEMSET);
4500       xassert(code->dim > 0);
4501       xassert(tuple != NULL);
4502       switch (code->op)
4503       {  case O_MEMSET:
4504             /* check if given n-tuple is member of elemental set, which
4505                is assigned to member of model set */
4506             {  ARG_LIST *e;
4507                TUPLE *temp;
4508                ELEMSET *set;
4509                /* evaluate reference to elemental set */
4510                temp = create_tuple(mpl);
4511                for (e = code->arg.set.list; e != NULL; e = e->next)
4512                   temp = expand_tuple(mpl, temp, eval_symbolic(mpl,
4513                      e->x));
4514                set = eval_member_set(mpl, code->arg.set.set, temp);
4515                delete_tuple(mpl, temp);
4516                /* check if the n-tuple is contained in the set array */
4517                temp = build_subtuple(mpl, tuple, set->dim);
4518                value = (find_tuple(mpl, set, temp) != NULL);
4519                delete_tuple(mpl, temp);
4520             }
4521             break;
4522          case O_MAKE:
4523             /* check if given n-tuple is member of literal set */
4524             {  ARG_LIST *e;
4525                TUPLE *temp, *that;
4526                value = 0;
4527                temp = build_subtuple(mpl, tuple, code->dim);
4528                for (e = code->arg.list; e != NULL; e = e->next)
4529                {  that = eval_tuple(mpl, e->x);
4530                   value = (compare_tuples(mpl, temp, that) == 0);
4531                   delete_tuple(mpl, that);
4532                   if (value) break;
4533                }
4534                delete_tuple(mpl, temp);
4535             }
4536             break;
4537          case O_UNION:
4538             value = is_member(mpl, code->arg.arg.x, tuple) ||
4539                     is_member(mpl, code->arg.arg.y, tuple);
4540             break;
4541          case O_DIFF:
4542             value = is_member(mpl, code->arg.arg.x, tuple) &&
4543                    !is_member(mpl, code->arg.arg.y, tuple);
4544             break;
4545          case O_SYMDIFF:
4546             {  int in1 = is_member(mpl, code->arg.arg.x, tuple);
4547                int in2 = is_member(mpl, code->arg.arg.y, tuple);
4548                value = (in1 && !in2) || (!in1 && in2);
4549             }
4550             break;
4551          case O_INTER:
4552             value = is_member(mpl, code->arg.arg.x, tuple) &&
4553                     is_member(mpl, code->arg.arg.y, tuple);
4554             break;
4555          case O_CROSS:
4556             {  int j;
4557                value = is_member(mpl, code->arg.arg.x, tuple);
4558                if (value)
4559                {  for (j = 1; j <= code->arg.arg.x->dim; j++)
4560                   {  xassert(tuple != NULL);
4561                      tuple = tuple->next;
4562                   }
4563                   value = is_member(mpl, code->arg.arg.y, tuple);
4564                }
4565             }
4566             break;
4567          case O_DOTS:
4568             /* check if given 1-tuple is member of "arithmetic" set */
4569             {  int j;
4570                double x, t0, tf, dt;
4571                xassert(code->dim == 1);
4572                /* compute "parameters" of the "arithmetic" set */
4573                t0 = eval_numeric(mpl, code->arg.arg.x);
4574                tf = eval_numeric(mpl, code->arg.arg.y);
4575                if (code->arg.arg.z == NULL)
4576                   dt = 1.0;
4577                else
4578                   dt = eval_numeric(mpl, code->arg.arg.z);
4579                /* make sure the parameters are correct */
4580                arelset_size(mpl, t0, tf, dt);
4581                /* if component of 1-tuple is symbolic, not numeric, the
4582                   1-tuple cannot be member of "arithmetic" set */
4583                xassert(tuple->sym != NULL);
4584                if (tuple->sym->str != NULL)
4585                {  value = 0;
4586                   break;
4587                }
4588                /* determine numeric value of the component */
4589                x = tuple->sym->num;
4590                /* if the component value is out of the set range, the
4591                   1-tuple is not in the set */
4592                if (dt > 0.0 && !(t0 <= x && x <= tf) ||
4593                    dt < 0.0 && !(tf <= x && x <= t0))
4594                {  value = 0;
4595                   break;
4596                }
4597                /* estimate ordinal number of the 1-tuple in the set */
4598                j = (int)(((x - t0) / dt) + 0.5) + 1;
4599                /* perform the main check */
4600                value = (arelset_member(mpl, t0, tf, dt, j) == x);
4601             }
4602             break;
4603          case O_FORK:
4604             /* check if given n-tuple is member of conditional set */
4605             if (eval_logical(mpl, code->arg.arg.x))
4606                value = is_member(mpl, code->arg.arg.y, tuple);
4607             else
4608                value = is_member(mpl, code->arg.arg.z, tuple);
4609             break;
4610          case O_SETOF:
4611             /* check if given n-tuple is member of computed set */
4612             /* it is not clear how to efficiently perform the check not
4613                computing the entire elemental set :+( */
4614             mpl_error(mpl, "implementation restriction; in/within setof{} n"
4615                "ot allowed");
4616             break;
4617          case O_BUILD:
4618             /* check if given n-tuple is member of domain set */
4619             {  TUPLE *temp;
4620                temp = build_subtuple(mpl, tuple, code->dim);
4621                /* try to enter the domain scope; if it is successful,
4622                   the n-tuple is in the domain set */
4623                value = (eval_within_domain(mpl, code->arg.loop.domain,
4624                   temp, NULL, null_func) == 0);
4625                delete_tuple(mpl, temp);
4626             }
4627             break;
4628          default:
4629             xassert(code != code);
4630       }
4631       return value;
4632 }
4633 
4634 /*----------------------------------------------------------------------
4635 -- eval_formula - evaluate pseudo-code to construct linear form.
4636 --
4637 -- This routine evaluates specified pseudo-code to construct resultant
4638 -- linear form, which is returned on exit. */
4639 
4640 struct iter_form_info
4641 {     /* working info used by the routine iter_form_func */
4642       CODE *code;
4643       /* pseudo-code for iterated operation to be performed */
4644       FORMULA *value;
4645       /* resultant value */
4646       FORMULA *tail;
4647       /* pointer to the last term */
4648 };
4649 
iter_form_func(MPL * mpl,void * _info)4650 static int iter_form_func(MPL *mpl, void *_info)
4651 {     /* this is auxiliary routine used to perform iterated operation
4652          on linear form "integrand" within domain scope */
4653       struct iter_form_info *info = _info;
4654       switch (info->code->op)
4655       {  case O_SUM:
4656             /* summation over domain */
4657 #if 0
4658             info->value =
4659                linear_comb(mpl,
4660                   +1.0, info->value,
4661                   +1.0, eval_formula(mpl, info->code->arg.loop.x));
4662 #else
4663             /* the routine linear_comb needs to look through all terms
4664                of both linear forms to reduce identical terms, so using
4665                it here is not a good idea (for example, evaluation of
4666                sum{i in 1..n} x[i] required quadratic time); the better
4667                idea is to gather all terms of the integrand in one list
4668                and reduce identical terms only once after all terms of
4669                the resultant linear form have been evaluated */
4670             {  FORMULA *form, *term;
4671                form = eval_formula(mpl, info->code->arg.loop.x);
4672                if (info->value == NULL)
4673                {  xassert(info->tail == NULL);
4674                   info->value = form;
4675                }
4676                else
4677                {  xassert(info->tail != NULL);
4678                   info->tail->next = form;
4679                }
4680                for (term = form; term != NULL; term = term->next)
4681                   info->tail = term;
4682             }
4683 #endif
4684             break;
4685          default:
4686             xassert(info != info);
4687       }
4688       return 0;
4689 }
4690 
eval_formula(MPL * mpl,CODE * code)4691 FORMULA *eval_formula(MPL *mpl, CODE *code)
4692 {     FORMULA *value;
4693       xassert(code != NULL);
4694       xassert(code->type == A_FORMULA);
4695       xassert(code->dim == 0);
4696       /* if the operation has a side effect, invalidate and delete the
4697          resultant value */
4698       if (code->vflag && code->valid)
4699       {  code->valid = 0;
4700          delete_value(mpl, code->type, &code->value);
4701       }
4702       /* if resultant value is valid, no evaluation is needed */
4703       if (code->valid)
4704       {  value = copy_formula(mpl, code->value.form);
4705          goto done;
4706       }
4707       /* evaluate pseudo-code recursively */
4708       switch (code->op)
4709       {  case O_MEMVAR:
4710             /* take member of variable */
4711             {  TUPLE *tuple;
4712                ARG_LIST *e;
4713                tuple = create_tuple(mpl);
4714                for (e = code->arg.var.list; e != NULL; e = e->next)
4715                   tuple = expand_tuple(mpl, tuple, eval_symbolic(mpl,
4716                      e->x));
4717 #if 1 /* 15/V-2010 */
4718                xassert(code->arg.var.suff == DOT_NONE);
4719 #endif
4720                value = single_variable(mpl,
4721                   eval_member_var(mpl, code->arg.var.var, tuple));
4722                delete_tuple(mpl, tuple);
4723             }
4724             break;
4725          case O_CVTLFM:
4726             /* convert to linear form */
4727             value = constant_term(mpl, eval_numeric(mpl,
4728                code->arg.arg.x));
4729             break;
4730          case O_PLUS:
4731             /* unary plus */
4732             value = linear_comb(mpl,
4733                 0.0, constant_term(mpl, 0.0),
4734                +1.0, eval_formula(mpl, code->arg.arg.x));
4735             break;
4736          case O_MINUS:
4737             /* unary minus */
4738             value = linear_comb(mpl,
4739                 0.0, constant_term(mpl, 0.0),
4740                -1.0, eval_formula(mpl, code->arg.arg.x));
4741             break;
4742          case O_ADD:
4743             /* addition */
4744             value = linear_comb(mpl,
4745                +1.0, eval_formula(mpl, code->arg.arg.x),
4746                +1.0, eval_formula(mpl, code->arg.arg.y));
4747             break;
4748          case O_SUB:
4749             /* subtraction */
4750             value = linear_comb(mpl,
4751                +1.0, eval_formula(mpl, code->arg.arg.x),
4752                -1.0, eval_formula(mpl, code->arg.arg.y));
4753             break;
4754          case O_MUL:
4755             /* multiplication */
4756             xassert(code->arg.arg.x != NULL);
4757             xassert(code->arg.arg.y != NULL);
4758             if (code->arg.arg.x->type == A_NUMERIC)
4759             {  xassert(code->arg.arg.y->type == A_FORMULA);
4760                value = linear_comb(mpl,
4761                   eval_numeric(mpl, code->arg.arg.x),
4762                   eval_formula(mpl, code->arg.arg.y),
4763                   0.0, constant_term(mpl, 0.0));
4764             }
4765             else
4766             {  xassert(code->arg.arg.x->type == A_FORMULA);
4767                xassert(code->arg.arg.y->type == A_NUMERIC);
4768                value = linear_comb(mpl,
4769                   eval_numeric(mpl, code->arg.arg.y),
4770                   eval_formula(mpl, code->arg.arg.x),
4771                   0.0, constant_term(mpl, 0.0));
4772             }
4773             break;
4774          case O_DIV:
4775             /* division */
4776             value = linear_comb(mpl,
4777                fp_div(mpl, 1.0, eval_numeric(mpl, code->arg.arg.y)),
4778                eval_formula(mpl, code->arg.arg.x),
4779                0.0, constant_term(mpl, 0.0));
4780             break;
4781          case O_FORK:
4782             /* if-then-else */
4783             if (eval_logical(mpl, code->arg.arg.x))
4784                value = eval_formula(mpl, code->arg.arg.y);
4785             else if (code->arg.arg.z == NULL)
4786                value = constant_term(mpl, 0.0);
4787             else
4788                value = eval_formula(mpl, code->arg.arg.z);
4789             break;
4790          case O_SUM:
4791             /* summation over domain */
4792             {  struct iter_form_info _info, *info = &_info;
4793                info->code = code;
4794                info->value = constant_term(mpl, 0.0);
4795                info->tail = NULL;
4796                loop_within_domain(mpl, code->arg.loop.domain, info,
4797                   iter_form_func);
4798                value = reduce_terms(mpl, info->value);
4799             }
4800             break;
4801          default:
4802             xassert(code != code);
4803       }
4804       /* save resultant value */
4805       xassert(!code->valid);
4806       code->valid = 1;
4807       code->value.form = copy_formula(mpl, value);
4808 done: return value;
4809 }
4810 
4811 /*----------------------------------------------------------------------
4812 -- clean_code - clean pseudo-code.
4813 --
4814 -- This routine recursively cleans specified pseudo-code that assumes
4815 -- deleting all temporary resultant values. */
4816 
clean_code(MPL * mpl,CODE * code)4817 void clean_code(MPL *mpl, CODE *code)
4818 {     ARG_LIST *e;
4819       /* if no pseudo-code is specified, do nothing */
4820       if (code == NULL) goto done;
4821       /* if resultant value is valid (exists), delete it */
4822       if (code->valid)
4823       {  code->valid = 0;
4824          delete_value(mpl, code->type, &code->value);
4825       }
4826       /* recursively clean pseudo-code for operands */
4827       switch (code->op)
4828       {  case O_NUMBER:
4829          case O_STRING:
4830          case O_INDEX:
4831             break;
4832          case O_MEMNUM:
4833          case O_MEMSYM:
4834             for (e = code->arg.par.list; e != NULL; e = e->next)
4835                clean_code(mpl, e->x);
4836             break;
4837          case O_MEMSET:
4838             for (e = code->arg.set.list; e != NULL; e = e->next)
4839                clean_code(mpl, e->x);
4840             break;
4841          case O_MEMVAR:
4842             for (e = code->arg.var.list; e != NULL; e = e->next)
4843                clean_code(mpl, e->x);
4844             break;
4845 #if 1 /* 15/V-2010 */
4846          case O_MEMCON:
4847             for (e = code->arg.con.list; e != NULL; e = e->next)
4848                clean_code(mpl, e->x);
4849             break;
4850 #endif
4851          case O_TUPLE:
4852          case O_MAKE:
4853             for (e = code->arg.list; e != NULL; e = e->next)
4854                clean_code(mpl, e->x);
4855             break;
4856          case O_SLICE:
4857             xassert(code != code);
4858          case O_IRAND224:
4859          case O_UNIFORM01:
4860          case O_NORMAL01:
4861          case O_GMTIME:
4862             break;
4863          case O_CVTNUM:
4864          case O_CVTSYM:
4865          case O_CVTLOG:
4866          case O_CVTTUP:
4867          case O_CVTLFM:
4868          case O_PLUS:
4869          case O_MINUS:
4870          case O_NOT:
4871          case O_ABS:
4872          case O_CEIL:
4873          case O_FLOOR:
4874          case O_EXP:
4875          case O_LOG:
4876          case O_LOG10:
4877          case O_SQRT:
4878          case O_SIN:
4879          case O_COS:
4880          case O_ATAN:
4881          case O_ROUND:
4882          case O_TRUNC:
4883          case O_CARD:
4884          case O_LENGTH:
4885             /* unary operation */
4886             clean_code(mpl, code->arg.arg.x);
4887             break;
4888          case O_ADD:
4889          case O_SUB:
4890          case O_LESS:
4891          case O_MUL:
4892          case O_DIV:
4893          case O_IDIV:
4894          case O_MOD:
4895          case O_POWER:
4896          case O_ATAN2:
4897          case O_ROUND2:
4898          case O_TRUNC2:
4899          case O_UNIFORM:
4900          case O_NORMAL:
4901          case O_CONCAT:
4902          case O_LT:
4903          case O_LE:
4904          case O_EQ:
4905          case O_GE:
4906          case O_GT:
4907          case O_NE:
4908          case O_AND:
4909          case O_OR:
4910          case O_UNION:
4911          case O_DIFF:
4912          case O_SYMDIFF:
4913          case O_INTER:
4914          case O_CROSS:
4915          case O_IN:
4916          case O_NOTIN:
4917          case O_WITHIN:
4918          case O_NOTWITHIN:
4919          case O_SUBSTR:
4920          case O_STR2TIME:
4921          case O_TIME2STR:
4922             /* binary operation */
4923             clean_code(mpl, code->arg.arg.x);
4924             clean_code(mpl, code->arg.arg.y);
4925             break;
4926          case O_DOTS:
4927          case O_FORK:
4928          case O_SUBSTR3:
4929             /* ternary operation */
4930             clean_code(mpl, code->arg.arg.x);
4931             clean_code(mpl, code->arg.arg.y);
4932             clean_code(mpl, code->arg.arg.z);
4933             break;
4934          case O_MIN:
4935          case O_MAX:
4936             /* n-ary operation */
4937             for (e = code->arg.list; e != NULL; e = e->next)
4938                clean_code(mpl, e->x);
4939             break;
4940          case O_SUM:
4941          case O_PROD:
4942          case O_MINIMUM:
4943          case O_MAXIMUM:
4944          case O_FORALL:
4945          case O_EXISTS:
4946          case O_SETOF:
4947          case O_BUILD:
4948             /* iterated operation */
4949             clean_domain(mpl, code->arg.loop.domain);
4950             clean_code(mpl, code->arg.loop.x);
4951             break;
4952          default:
4953             xassert(code->op != code->op);
4954       }
4955 done: return;
4956 }
4957 
4958 #if 1 /* 11/II-2008 */
4959 /**********************************************************************/
4960 /* * *                        DATA TABLES                         * * */
4961 /**********************************************************************/
4962 
mpl_tab_num_args(TABDCA * dca)4963 int mpl_tab_num_args(TABDCA *dca)
4964 {     /* returns the number of arguments */
4965       return dca->na;
4966 }
4967 
mpl_tab_get_arg(TABDCA * dca,int k)4968 const char *mpl_tab_get_arg(TABDCA *dca, int k)
4969 {     /* returns pointer to k-th argument */
4970       xassert(1 <= k && k <= dca->na);
4971       return dca->arg[k];
4972 }
4973 
mpl_tab_num_flds(TABDCA * dca)4974 int mpl_tab_num_flds(TABDCA *dca)
4975 {     /* returns the number of fields */
4976       return dca->nf;
4977 }
4978 
mpl_tab_get_name(TABDCA * dca,int k)4979 const char *mpl_tab_get_name(TABDCA *dca, int k)
4980 {     /* returns pointer to name of k-th field */
4981       xassert(1 <= k && k <= dca->nf);
4982       return dca->name[k];
4983 }
4984 
mpl_tab_get_type(TABDCA * dca,int k)4985 int mpl_tab_get_type(TABDCA *dca, int k)
4986 {     /* returns type of k-th field */
4987       xassert(1 <= k && k <= dca->nf);
4988       return dca->type[k];
4989 }
4990 
mpl_tab_get_num(TABDCA * dca,int k)4991 double mpl_tab_get_num(TABDCA *dca, int k)
4992 {     /* returns numeric value of k-th field */
4993       xassert(1 <= k && k <= dca->nf);
4994       xassert(dca->type[k] == 'N');
4995       return dca->num[k];
4996 }
4997 
mpl_tab_get_str(TABDCA * dca,int k)4998 const char *mpl_tab_get_str(TABDCA *dca, int k)
4999 {     /* returns pointer to string value of k-th field */
5000       xassert(1 <= k && k <= dca->nf);
5001       xassert(dca->type[k] == 'S');
5002       xassert(dca->str[k] != NULL);
5003       return dca->str[k];
5004 }
5005 
mpl_tab_set_num(TABDCA * dca,int k,double num)5006 void mpl_tab_set_num(TABDCA *dca, int k, double num)
5007 {     /* assign numeric value to k-th field */
5008       xassert(1 <= k && k <= dca->nf);
5009       xassert(dca->type[k] == '?');
5010       dca->type[k] = 'N';
5011       dca->num[k] = num;
5012       return;
5013 }
5014 
mpl_tab_set_str(TABDCA * dca,int k,const char * str)5015 void mpl_tab_set_str(TABDCA *dca, int k, const char *str)
5016 {     /* assign string value to k-th field */
5017       xassert(1 <= k && k <= dca->nf);
5018       xassert(dca->type[k] == '?');
5019       xassert(strlen(str) <= MAX_LENGTH);
5020       xassert(dca->str[k] != NULL);
5021       dca->type[k] = 'S';
5022       strcpy(dca->str[k], str);
5023       return;
5024 }
5025 
write_func(MPL * mpl,void * info)5026 static int write_func(MPL *mpl, void *info)
5027 {     /* this is auxiliary routine to work within domain scope */
5028       TABLE *tab = info;
5029       TABDCA *dca = mpl->dca;
5030       TABOUT *out;
5031       SYMBOL *sym;
5032       int k;
5033       char buf[MAX_LENGTH+1];
5034       /* evaluate field values */
5035       k = 0;
5036       for (out = tab->u.out.list; out != NULL; out = out->next)
5037       {  k++;
5038          switch (out->code->type)
5039          {  case A_NUMERIC:
5040                dca->type[k] = 'N';
5041                dca->num[k] = eval_numeric(mpl, out->code);
5042                dca->str[k][0] = '\0';
5043                break;
5044             case A_SYMBOLIC:
5045                sym = eval_symbolic(mpl, out->code);
5046                if (sym->str == NULL)
5047                {  dca->type[k] = 'N';
5048                   dca->num[k] = sym->num;
5049                   dca->str[k][0] = '\0';
5050                }
5051                else
5052                {  dca->type[k] = 'S';
5053                   dca->num[k] = 0.0;
5054                   fetch_string(mpl, sym->str, buf);
5055                   strcpy(dca->str[k], buf);
5056                }
5057                delete_symbol(mpl, sym);
5058                break;
5059             default:
5060                xassert(out != out);
5061          }
5062       }
5063       /* write record to output table */
5064       mpl_tab_drv_write(mpl);
5065       return 0;
5066 }
5067 
execute_table(MPL * mpl,TABLE * tab)5068 void execute_table(MPL *mpl, TABLE *tab)
5069 {     /* execute table statement */
5070       TABARG *arg;
5071       TABFLD *fld;
5072       TABIN *in;
5073       TABOUT *out;
5074       TABDCA *dca;
5075       SET *set;
5076       int k;
5077       char buf[MAX_LENGTH+1];
5078       /* allocate table driver communication area */
5079       xassert(mpl->dca == NULL);
5080       mpl->dca = dca = xmalloc(sizeof(TABDCA));
5081       dca->id = 0;
5082       dca->link = NULL;
5083       dca->na = 0;
5084       dca->arg = NULL;
5085       dca->nf = 0;
5086       dca->name = NULL;
5087       dca->type = NULL;
5088       dca->num = NULL;
5089       dca->str = NULL;
5090       /* allocate arguments */
5091       xassert(dca->na == 0);
5092       for (arg = tab->arg; arg != NULL; arg = arg->next)
5093          dca->na++;
5094       dca->arg = xcalloc(1+dca->na, sizeof(char *));
5095 #if 1 /* 28/IX-2008 */
5096       for (k = 1; k <= dca->na; k++) dca->arg[k] = NULL;
5097 #endif
5098       /* evaluate argument values */
5099       k = 0;
5100       for (arg = tab->arg; arg != NULL; arg = arg->next)
5101       {  SYMBOL *sym;
5102          k++;
5103          xassert(arg->code->type == A_SYMBOLIC);
5104          sym = eval_symbolic(mpl, arg->code);
5105          if (sym->str == NULL)
5106             sprintf(buf, "%.*g", DBL_DIG, sym->num);
5107          else
5108             fetch_string(mpl, sym->str, buf);
5109          delete_symbol(mpl, sym);
5110          dca->arg[k] = xmalloc(strlen(buf)+1);
5111          strcpy(dca->arg[k], buf);
5112       }
5113       /* perform table input/output */
5114       switch (tab->type)
5115       {  case A_INPUT:  goto read_table;
5116          case A_OUTPUT: goto write_table;
5117          default:       xassert(tab != tab);
5118       }
5119 read_table:
5120       /* read data from input table */
5121       /* add the only member to the control set and assign it empty
5122          elemental set */
5123       set = tab->u.in.set;
5124       if (set != NULL)
5125       {  if (set->data)
5126             mpl_error(mpl, "%s already provided with data", set->name);
5127          xassert(set->array->head == NULL);
5128          add_member(mpl, set->array, NULL)->value.set =
5129             create_elemset(mpl, set->dimen);
5130          set->data = 1;
5131       }
5132       /* check parameters specified in the input list */
5133       for (in = tab->u.in.list; in != NULL; in = in->next)
5134       {  if (in->par->data)
5135             mpl_error(mpl, "%s already provided with data", in->par->name);
5136          in->par->data = 1;
5137       }
5138       /* allocate and initialize fields */
5139       xassert(dca->nf == 0);
5140       for (fld = tab->u.in.fld; fld != NULL; fld = fld->next)
5141          dca->nf++;
5142       for (in = tab->u.in.list; in != NULL; in = in->next)
5143          dca->nf++;
5144       dca->name = xcalloc(1+dca->nf, sizeof(char *));
5145       dca->type = xcalloc(1+dca->nf, sizeof(int));
5146       dca->num = xcalloc(1+dca->nf, sizeof(double));
5147       dca->str = xcalloc(1+dca->nf, sizeof(char *));
5148       k = 0;
5149       for (fld = tab->u.in.fld; fld != NULL; fld = fld->next)
5150       {  k++;
5151          dca->name[k] = fld->name;
5152          dca->type[k] = '?';
5153          dca->num[k] = 0.0;
5154          dca->str[k] = xmalloc(MAX_LENGTH+1);
5155          dca->str[k][0] = '\0';
5156       }
5157       for (in = tab->u.in.list; in != NULL; in = in->next)
5158       {  k++;
5159          dca->name[k] = in->name;
5160          dca->type[k] = '?';
5161          dca->num[k] = 0.0;
5162          dca->str[k] = xmalloc(MAX_LENGTH+1);
5163          dca->str[k][0] = '\0';
5164       }
5165       /* open input table */
5166       mpl_tab_drv_open(mpl, 'R');
5167       /* read and process records */
5168       for (;;)
5169       {  TUPLE *tup;
5170          /* reset field types */
5171          for (k = 1; k <= dca->nf; k++)
5172             dca->type[k] = '?';
5173          /* read next record */
5174          if (mpl_tab_drv_read(mpl)) break;
5175          /* all fields must be set by the driver */
5176          for (k = 1; k <= dca->nf; k++)
5177          {  if (dca->type[k] == '?')
5178                mpl_error(mpl, "field %s missing in input table",
5179                   dca->name[k]);
5180          }
5181          /* construct n-tuple */
5182          tup = create_tuple(mpl);
5183          k = 0;
5184          for (fld = tab->u.in.fld; fld != NULL; fld = fld->next)
5185          {  k++;
5186             xassert(k <= dca->nf);
5187             switch (dca->type[k])
5188             {  case 'N':
5189                   tup = expand_tuple(mpl, tup, create_symbol_num(mpl,
5190                      dca->num[k]));
5191                   break;
5192                case 'S':
5193                   xassert(strlen(dca->str[k]) <= MAX_LENGTH);
5194                   tup = expand_tuple(mpl, tup, create_symbol_str(mpl,
5195                      create_string(mpl, dca->str[k])));
5196                   break;
5197                default:
5198                   xassert(dca != dca);
5199             }
5200          }
5201          /* add n-tuple just read to the control set */
5202          if (tab->u.in.set != NULL)
5203             check_then_add(mpl, tab->u.in.set->array->head->value.set,
5204                copy_tuple(mpl, tup));
5205          /* assign values to the parameters in the input list */
5206          for (in = tab->u.in.list; in != NULL; in = in->next)
5207          {  MEMBER *memb;
5208             k++;
5209             xassert(k <= dca->nf);
5210             /* there must be no member with the same n-tuple */
5211             if (find_member(mpl, in->par->array, tup) != NULL)
5212                mpl_error(mpl, "%s%s already defined", in->par->name,
5213                format_tuple(mpl, '[', tup));
5214             /* create new parameter member with given n-tuple */
5215             memb = add_member(mpl, in->par->array, copy_tuple(mpl, tup))
5216                ;
5217             /* assign value to the parameter member */
5218             switch (in->par->type)
5219             {  case A_NUMERIC:
5220                case A_INTEGER:
5221                case A_BINARY:
5222                   if (dca->type[k] != 'N')
5223                      mpl_error(mpl, "%s requires numeric data",
5224                         in->par->name);
5225                   memb->value.num = dca->num[k];
5226                   break;
5227                case A_SYMBOLIC:
5228                   switch (dca->type[k])
5229                   {  case 'N':
5230                         memb->value.sym = create_symbol_num(mpl,
5231                            dca->num[k]);
5232                         break;
5233                      case 'S':
5234                         xassert(strlen(dca->str[k]) <= MAX_LENGTH);
5235                         memb->value.sym = create_symbol_str(mpl,
5236                            create_string(mpl,dca->str[k]));
5237                         break;
5238                      default:
5239                         xassert(dca != dca);
5240                   }
5241                   break;
5242                default:
5243                   xassert(in != in);
5244             }
5245          }
5246          /* n-tuple is no more needed */
5247          delete_tuple(mpl, tup);
5248       }
5249       /* close input table */
5250       mpl_tab_drv_close(mpl);
5251       goto done;
5252 write_table:
5253       /* write data to output table */
5254       /* allocate and initialize fields */
5255       xassert(dca->nf == 0);
5256       for (out = tab->u.out.list; out != NULL; out = out->next)
5257          dca->nf++;
5258       dca->name = xcalloc(1+dca->nf, sizeof(char *));
5259       dca->type = xcalloc(1+dca->nf, sizeof(int));
5260       dca->num = xcalloc(1+dca->nf, sizeof(double));
5261       dca->str = xcalloc(1+dca->nf, sizeof(char *));
5262       k = 0;
5263       for (out = tab->u.out.list; out != NULL; out = out->next)
5264       {  k++;
5265          dca->name[k] = out->name;
5266          dca->type[k] = '?';
5267          dca->num[k] = 0.0;
5268          dca->str[k] = xmalloc(MAX_LENGTH+1);
5269          dca->str[k][0] = '\0';
5270       }
5271       /* open output table */
5272       mpl_tab_drv_open(mpl, 'W');
5273       /* evaluate fields and write records */
5274       loop_within_domain(mpl, tab->u.out.domain, tab, write_func);
5275       /* close output table */
5276       mpl_tab_drv_close(mpl);
5277 done: /* free table driver communication area */
5278       free_dca(mpl);
5279       return;
5280 }
5281 
free_dca(MPL * mpl)5282 void free_dca(MPL *mpl)
5283 {     /* free table driver communucation area */
5284       TABDCA *dca = mpl->dca;
5285       int k;
5286       if (dca != NULL)
5287       {  if (dca->link != NULL)
5288             mpl_tab_drv_close(mpl);
5289          if (dca->arg != NULL)
5290          {  for (k = 1; k <= dca->na; k++)
5291 #if 1 /* 28/IX-2008 */
5292                if (dca->arg[k] != NULL)
5293 #endif
5294                xfree(dca->arg[k]);
5295             xfree(dca->arg);
5296          }
5297          if (dca->name != NULL) xfree(dca->name);
5298          if (dca->type != NULL) xfree(dca->type);
5299          if (dca->num != NULL) xfree(dca->num);
5300          if (dca->str != NULL)
5301          {  for (k = 1; k <= dca->nf; k++)
5302                xfree(dca->str[k]);
5303             xfree(dca->str);
5304          }
5305          xfree(dca), mpl->dca = NULL;
5306       }
5307       return;
5308 }
5309 
clean_table(MPL * mpl,TABLE * tab)5310 void clean_table(MPL *mpl, TABLE *tab)
5311 {     /* clean table statement */
5312       TABARG *arg;
5313       TABOUT *out;
5314       /* clean string list */
5315       for (arg = tab->arg; arg != NULL; arg = arg->next)
5316          clean_code(mpl, arg->code);
5317       switch (tab->type)
5318       {  case A_INPUT:
5319             break;
5320          case A_OUTPUT:
5321             /* clean subscript domain */
5322             clean_domain(mpl, tab->u.out.domain);
5323             /* clean output list */
5324             for (out = tab->u.out.list; out != NULL; out = out->next)
5325                clean_code(mpl, out->code);
5326             break;
5327          default:
5328             xassert(tab != tab);
5329       }
5330       return;
5331 }
5332 #endif
5333 
5334 /**********************************************************************/
5335 /* * *                      MODEL STATEMENTS                      * * */
5336 /**********************************************************************/
5337 
5338 /*----------------------------------------------------------------------
5339 -- execute_check - execute check statement.
5340 --
5341 -- This routine executes specified check statement. */
5342 
check_func(MPL * mpl,void * info)5343 static int check_func(MPL *mpl, void *info)
5344 {     /* this is auxiliary routine to work within domain scope */
5345       CHECK *chk = (CHECK *)info;
5346       if (!eval_logical(mpl, chk->code))
5347          mpl_error(mpl, "check%s failed", format_tuple(mpl, '[',
5348             get_domain_tuple(mpl, chk->domain)));
5349       return 0;
5350 }
5351 
execute_check(MPL * mpl,CHECK * chk)5352 void execute_check(MPL *mpl, CHECK *chk)
5353 {     loop_within_domain(mpl, chk->domain, chk, check_func);
5354       return;
5355 }
5356 
5357 /*----------------------------------------------------------------------
5358 -- clean_check - clean check statement.
5359 --
5360 -- This routine cleans specified check statement that assumes deleting
5361 -- all stuff dynamically allocated on generating/postsolving phase. */
5362 
clean_check(MPL * mpl,CHECK * chk)5363 void clean_check(MPL *mpl, CHECK *chk)
5364 {     /* clean subscript domain */
5365       clean_domain(mpl, chk->domain);
5366       /* clean pseudo-code for computing predicate */
5367       clean_code(mpl, chk->code);
5368       return;
5369 }
5370 
5371 /*----------------------------------------------------------------------
5372 -- execute_display - execute display statement.
5373 --
5374 -- This routine executes specified display statement. */
5375 
display_set(MPL * mpl,SET * set,MEMBER * memb)5376 static void display_set(MPL *mpl, SET *set, MEMBER *memb)
5377 {     /* display member of model set */
5378       ELEMSET *s = memb->value.set;
5379       MEMBER *m;
5380       write_text(mpl, "%s%s%s\n", set->name,
5381          format_tuple(mpl, '[', memb->tuple),
5382          s->head == NULL ? " is empty" : ":");
5383       for (m = s->head; m != NULL; m = m->next)
5384          write_text(mpl, "   %s\n", format_tuple(mpl, '(', m->tuple));
5385       return;
5386 }
5387 
display_par(MPL * mpl,PARAMETER * par,MEMBER * memb)5388 static void display_par(MPL *mpl, PARAMETER *par, MEMBER *memb)
5389 {     /* display member of model parameter */
5390       switch (par->type)
5391       {  case A_NUMERIC:
5392          case A_INTEGER:
5393          case A_BINARY:
5394             write_text(mpl, "%s%s = %.*g\n", par->name,
5395                format_tuple(mpl, '[', memb->tuple),
5396                DBL_DIG, memb->value.num);
5397             break;
5398          case A_SYMBOLIC:
5399             write_text(mpl, "%s%s = %s\n", par->name,
5400                format_tuple(mpl, '[', memb->tuple),
5401                format_symbol(mpl, memb->value.sym));
5402             break;
5403          default:
5404             xassert(par != par);
5405       }
5406       return;
5407 }
5408 
5409 #if 1 /* 15/V-2010 */
display_var(MPL * mpl,VARIABLE * var,MEMBER * memb,int suff)5410 static void display_var(MPL *mpl, VARIABLE *var, MEMBER *memb,
5411       int suff)
5412 {     /* display member of model variable */
5413       if (suff == DOT_NONE || suff == DOT_VAL)
5414          write_text(mpl, "%s%s.val = %.*g\n", var->name,
5415             format_tuple(mpl, '[', memb->tuple), DBL_DIG,
5416             memb->value.var->prim);
5417       else if (suff == DOT_LB)
5418          write_text(mpl, "%s%s.lb = %.*g\n", var->name,
5419             format_tuple(mpl, '[', memb->tuple), DBL_DIG,
5420             memb->value.var->var->lbnd == NULL ? -DBL_MAX :
5421             memb->value.var->lbnd);
5422       else if (suff == DOT_UB)
5423          write_text(mpl, "%s%s.ub = %.*g\n", var->name,
5424             format_tuple(mpl, '[', memb->tuple), DBL_DIG,
5425             memb->value.var->var->ubnd == NULL ? +DBL_MAX :
5426             memb->value.var->ubnd);
5427       else if (suff == DOT_STATUS)
5428          write_text(mpl, "%s%s.status = %d\n", var->name, format_tuple
5429             (mpl, '[', memb->tuple), memb->value.var->stat);
5430       else if (suff == DOT_DUAL)
5431          write_text(mpl, "%s%s.dual = %.*g\n", var->name,
5432             format_tuple(mpl, '[', memb->tuple), DBL_DIG,
5433             memb->value.var->dual);
5434       else
5435          xassert(suff != suff);
5436       return;
5437 }
5438 #endif
5439 
5440 #if 1 /* 15/V-2010 */
display_con(MPL * mpl,CONSTRAINT * con,MEMBER * memb,int suff)5441 static void display_con(MPL *mpl, CONSTRAINT *con, MEMBER *memb,
5442       int suff)
5443 {     /* display member of model constraint */
5444       if (suff == DOT_NONE || suff == DOT_VAL)
5445          write_text(mpl, "%s%s.val = %.*g\n", con->name,
5446             format_tuple(mpl, '[', memb->tuple), DBL_DIG,
5447             memb->value.con->prim);
5448       else if (suff == DOT_LB)
5449          write_text(mpl, "%s%s.lb = %.*g\n", con->name,
5450             format_tuple(mpl, '[', memb->tuple), DBL_DIG,
5451             memb->value.con->con->lbnd == NULL ? -DBL_MAX :
5452             memb->value.con->lbnd);
5453       else if (suff == DOT_UB)
5454          write_text(mpl, "%s%s.ub = %.*g\n", con->name,
5455             format_tuple(mpl, '[', memb->tuple), DBL_DIG,
5456             memb->value.con->con->ubnd == NULL ? +DBL_MAX :
5457             memb->value.con->ubnd);
5458       else if (suff == DOT_STATUS)
5459          write_text(mpl, "%s%s.status = %d\n", con->name, format_tuple
5460             (mpl, '[', memb->tuple), memb->value.con->stat);
5461       else if (suff == DOT_DUAL)
5462          write_text(mpl, "%s%s.dual = %.*g\n", con->name,
5463             format_tuple(mpl, '[', memb->tuple), DBL_DIG,
5464             memb->value.con->dual);
5465       else
5466          xassert(suff != suff);
5467       return;
5468 }
5469 #endif
5470 
display_memb(MPL * mpl,CODE * code)5471 static void display_memb(MPL *mpl, CODE *code)
5472 {     /* display member specified by pseudo-code */
5473       MEMBER memb;
5474       ARG_LIST *e;
5475       xassert(code->op == O_MEMNUM || code->op == O_MEMSYM
5476          || code->op == O_MEMSET || code->op == O_MEMVAR
5477          || code->op == O_MEMCON);
5478       memb.tuple = create_tuple(mpl);
5479       for (e = code->arg.par.list; e != NULL; e = e->next)
5480          memb.tuple = expand_tuple(mpl, memb.tuple, eval_symbolic(mpl,
5481             e->x));
5482       switch (code->op)
5483       {  case O_MEMNUM:
5484             memb.value.num = eval_member_num(mpl, code->arg.par.par,
5485                memb.tuple);
5486             display_par(mpl, code->arg.par.par, &memb);
5487             break;
5488          case O_MEMSYM:
5489             memb.value.sym = eval_member_sym(mpl, code->arg.par.par,
5490                memb.tuple);
5491             display_par(mpl, code->arg.par.par, &memb);
5492             delete_symbol(mpl, memb.value.sym);
5493             break;
5494          case O_MEMSET:
5495             memb.value.set = eval_member_set(mpl, code->arg.set.set,
5496                memb.tuple);
5497             display_set(mpl, code->arg.set.set, &memb);
5498             break;
5499          case O_MEMVAR:
5500             memb.value.var = eval_member_var(mpl, code->arg.var.var,
5501                memb.tuple);
5502             display_var
5503                (mpl, code->arg.var.var, &memb, code->arg.var.suff);
5504             break;
5505          case O_MEMCON:
5506             memb.value.con = eval_member_con(mpl, code->arg.con.con,
5507                memb.tuple);
5508             display_con
5509                (mpl, code->arg.con.con, &memb, code->arg.con.suff);
5510             break;
5511          default:
5512             xassert(code != code);
5513       }
5514       delete_tuple(mpl, memb.tuple);
5515       return;
5516 }
5517 
display_code(MPL * mpl,CODE * code)5518 static void display_code(MPL *mpl, CODE *code)
5519 {     /* display value of expression */
5520       switch (code->type)
5521       {  case A_NUMERIC:
5522             /* numeric value */
5523             {  double num;
5524                num = eval_numeric(mpl, code);
5525                write_text(mpl, "%.*g\n", DBL_DIG, num);
5526             }
5527             break;
5528          case A_SYMBOLIC:
5529             /* symbolic value */
5530             {  SYMBOL *sym;
5531                sym = eval_symbolic(mpl, code);
5532                write_text(mpl, "%s\n", format_symbol(mpl, sym));
5533                delete_symbol(mpl, sym);
5534             }
5535             break;
5536          case A_LOGICAL:
5537             /* logical value */
5538             {  int bit;
5539                bit = eval_logical(mpl, code);
5540                write_text(mpl, "%s\n", bit ? "true" : "false");
5541             }
5542             break;
5543          case A_TUPLE:
5544             /* n-tuple */
5545             {  TUPLE *tuple;
5546                tuple = eval_tuple(mpl, code);
5547                write_text(mpl, "%s\n", format_tuple(mpl, '(', tuple));
5548                delete_tuple(mpl, tuple);
5549             }
5550             break;
5551          case A_ELEMSET:
5552             /* elemental set */
5553             {  ELEMSET *set;
5554                MEMBER *memb;
5555                set = eval_elemset(mpl, code);
5556                if (set->head == 0)
5557                   write_text(mpl, "set is empty\n");
5558                for (memb = set->head; memb != NULL; memb = memb->next)
5559                   write_text(mpl, "   %s\n", format_tuple(mpl, '(',
5560                      memb->tuple));
5561                delete_elemset(mpl, set);
5562             }
5563             break;
5564          case A_FORMULA:
5565             /* linear form */
5566             {  FORMULA *form, *term;
5567                form = eval_formula(mpl, code);
5568                if (form == NULL)
5569                   write_text(mpl, "linear form is empty\n");
5570                for (term = form; term != NULL; term = term->next)
5571                {  if (term->var == NULL)
5572                      write_text(mpl, "   %.*g\n", term->coef);
5573                   else
5574                      write_text(mpl, "   %.*g %s%s\n", DBL_DIG,
5575                         term->coef, term->var->var->name,
5576                         format_tuple(mpl, '[', term->var->memb->tuple));
5577                }
5578                delete_formula(mpl, form);
5579             }
5580             break;
5581          default:
5582             xassert(code != code);
5583       }
5584       return;
5585 }
5586 
display_func(MPL * mpl,void * info)5587 static int display_func(MPL *mpl, void *info)
5588 {     /* this is auxiliary routine to work within domain scope */
5589       DISPLAY *dpy = (DISPLAY *)info;
5590       DISPLAY1 *entry;
5591       for (entry = dpy->list; entry != NULL; entry = entry->next)
5592       {  if (entry->type == A_INDEX)
5593          {  /* dummy index */
5594             DOMAIN_SLOT *slot = entry->u.slot;
5595             write_text(mpl, "%s = %s\n", slot->name,
5596             format_symbol(mpl, slot->value));
5597          }
5598          else if (entry->type == A_SET)
5599          {  /* model set */
5600             SET *set = entry->u.set;
5601             MEMBER *memb;
5602             if (set->assign != NULL)
5603             {  /* the set has assignment expression; evaluate all its
5604                   members over entire domain */
5605                eval_whole_set(mpl, set);
5606             }
5607             else
5608             {  /* the set has no assignment expression; refer to its
5609                   any existing member ignoring resultant value to check
5610                   the data provided the data section */
5611 #if 1 /* 12/XII-2008 */
5612                if (set->gadget != NULL && set->data == 0)
5613                {  /* initialize the set with data from a plain set */
5614                   saturate_set(mpl, set);
5615                }
5616 #endif
5617                if (set->array->head != NULL)
5618                   eval_member_set(mpl, set, set->array->head->tuple);
5619             }
5620             /* display all members of the set array */
5621             if (set->array->head == NULL)
5622                write_text(mpl, "%s has empty content\n", set->name);
5623             for (memb = set->array->head; memb != NULL; memb =
5624                memb->next) display_set(mpl, set, memb);
5625          }
5626          else if (entry->type == A_PARAMETER)
5627          {  /* model parameter */
5628             PARAMETER *par = entry->u.par;
5629             MEMBER *memb;
5630             if (par->assign != NULL)
5631             {  /* the parameter has an assignment expression; evaluate
5632                   all its member over entire domain */
5633                eval_whole_par(mpl, par);
5634             }
5635             else
5636             {  /* the parameter has no assignment expression; refer to
5637                   its any existing member ignoring resultant value to
5638                   check the data provided in the data section */
5639                if (par->array->head != NULL)
5640                {  if (par->type != A_SYMBOLIC)
5641                      eval_member_num(mpl, par, par->array->head->tuple);
5642                   else
5643                      delete_symbol(mpl, eval_member_sym(mpl, par,
5644                         par->array->head->tuple));
5645                }
5646             }
5647             /* display all members of the parameter array */
5648             if (par->array->head == NULL)
5649                write_text(mpl, "%s has empty content\n", par->name);
5650             for (memb = par->array->head; memb != NULL; memb =
5651                memb->next) display_par(mpl, par, memb);
5652          }
5653          else if (entry->type == A_VARIABLE)
5654          {  /* model variable */
5655             VARIABLE *var = entry->u.var;
5656             MEMBER *memb;
5657             xassert(mpl->flag_p);
5658             /* display all members of the variable array */
5659             if (var->array->head == NULL)
5660                write_text(mpl, "%s has empty content\n", var->name);
5661             for (memb = var->array->head; memb != NULL; memb =
5662                memb->next) display_var(mpl, var, memb, DOT_NONE);
5663          }
5664          else if (entry->type == A_CONSTRAINT)
5665          {  /* model constraint */
5666             CONSTRAINT *con = entry->u.con;
5667             MEMBER *memb;
5668             xassert(mpl->flag_p);
5669             /* display all members of the constraint array */
5670             if (con->array->head == NULL)
5671                write_text(mpl, "%s has empty content\n", con->name);
5672             for (memb = con->array->head; memb != NULL; memb =
5673                memb->next) display_con(mpl, con, memb, DOT_NONE);
5674          }
5675          else if (entry->type == A_EXPRESSION)
5676          {  /* expression */
5677             CODE *code = entry->u.code;
5678             if (code->op == O_MEMNUM || code->op == O_MEMSYM ||
5679                 code->op == O_MEMSET || code->op == O_MEMVAR ||
5680                 code->op == O_MEMCON)
5681                display_memb(mpl, code);
5682             else
5683                display_code(mpl, code);
5684          }
5685          else
5686             xassert(entry != entry);
5687       }
5688       return 0;
5689 }
5690 
execute_display(MPL * mpl,DISPLAY * dpy)5691 void execute_display(MPL *mpl, DISPLAY *dpy)
5692 {     loop_within_domain(mpl, dpy->domain, dpy, display_func);
5693       return;
5694 }
5695 
5696 /*----------------------------------------------------------------------
5697 -- clean_display - clean display statement.
5698 --
5699 -- This routine cleans specified display statement that assumes deleting
5700 -- all stuff dynamically allocated on generating/postsolving phase. */
5701 
clean_display(MPL * mpl,DISPLAY * dpy)5702 void clean_display(MPL *mpl, DISPLAY *dpy)
5703 {     DISPLAY1 *d;
5704 #if 0 /* 15/V-2010 */
5705       ARG_LIST *e;
5706 #endif
5707       /* clean subscript domain */
5708       clean_domain(mpl, dpy->domain);
5709       /* clean display list */
5710       for (d = dpy->list; d != NULL; d = d->next)
5711       {  /* clean pseudo-code for computing expression */
5712          if (d->type == A_EXPRESSION)
5713             clean_code(mpl, d->u.code);
5714 #if 0 /* 15/V-2010 */
5715          /* clean pseudo-code for computing subscripts */
5716          for (e = d->list; e != NULL; e = e->next)
5717             clean_code(mpl, e->x);
5718 #endif
5719       }
5720       return;
5721 }
5722 
5723 /*----------------------------------------------------------------------
5724 -- execute_printf - execute printf statement.
5725 --
5726 -- This routine executes specified printf statement. */
5727 
5728 #if 1 /* 14/VII-2006 */
print_char(MPL * mpl,int c)5729 static void print_char(MPL *mpl, int c)
5730 {     if (mpl->prt_fp == NULL)
5731          write_char(mpl, c);
5732       else
5733          xfputc(c, mpl->prt_fp);
5734       return;
5735 }
5736 
print_text(MPL * mpl,char * fmt,...)5737 static void print_text(MPL *mpl, char *fmt, ...)
5738 {     va_list arg;
5739       char buf[OUTBUF_SIZE], *c;
5740       va_start(arg, fmt);
5741       vsprintf(buf, fmt, arg);
5742       xassert(strlen(buf) < sizeof(buf));
5743       va_end(arg);
5744       for (c = buf; *c != '\0'; c++) print_char(mpl, *c);
5745       return;
5746 }
5747 #endif
5748 
printf_func(MPL * mpl,void * info)5749 static int printf_func(MPL *mpl, void *info)
5750 {     /* this is auxiliary routine to work within domain scope */
5751       PRINTF *prt = (PRINTF *)info;
5752       PRINTF1 *entry;
5753       SYMBOL *sym;
5754       char fmt[MAX_LENGTH+1], *c, *from, save;
5755       /* evaluate format control string */
5756       sym = eval_symbolic(mpl, prt->fmt);
5757       if (sym->str == NULL)
5758          sprintf(fmt, "%.*g", DBL_DIG, sym->num);
5759       else
5760          fetch_string(mpl, sym->str, fmt);
5761       delete_symbol(mpl, sym);
5762       /* scan format control string and perform formatting output */
5763       entry = prt->list;
5764       for (c = fmt; *c != '\0'; c++)
5765       {  if (*c == '%')
5766          {  /* scan format specifier */
5767             from = c++;
5768             if (*c == '%')
5769             {  print_char(mpl, '%');
5770                continue;
5771             }
5772             if (entry == NULL) break;
5773             /* scan optional flags */
5774             while (*c == '-' || *c == '+' || *c == ' ' || *c == '#' ||
5775                    *c == '0') c++;
5776             /* scan optional minimum field width */
5777             while (isdigit((unsigned char)*c)) c++;
5778             /* scan optional precision */
5779             if (*c == '.')
5780             {  c++;
5781                while (isdigit((unsigned char)*c)) c++;
5782             }
5783             /* scan conversion specifier and perform formatting */
5784             save = *(c+1), *(c+1) = '\0';
5785             if (*c == 'd' || *c == 'i' || *c == 'e' || *c == 'E' ||
5786                 *c == 'f' || *c == 'F' || *c == 'g' || *c == 'G')
5787             {  /* the specifier requires numeric value */
5788                double value;
5789                xassert(entry != NULL);
5790                switch (entry->code->type)
5791                {  case A_NUMERIC:
5792                      value = eval_numeric(mpl, entry->code);
5793                      break;
5794                   case A_SYMBOLIC:
5795                      sym = eval_symbolic(mpl, entry->code);
5796                      if (sym->str != NULL)
5797                         mpl_error(mpl, "cannot convert %s to floating-point"
5798                            " number", format_symbol(mpl, sym));
5799                      value = sym->num;
5800                      delete_symbol(mpl, sym);
5801                      break;
5802                   case A_LOGICAL:
5803                      if (eval_logical(mpl, entry->code))
5804                         value = 1.0;
5805                      else
5806                         value = 0.0;
5807                      break;
5808                   default:
5809                      xassert(entry != entry);
5810                }
5811                if (*c == 'd' || *c == 'i')
5812                {  double int_max = (double)INT_MAX;
5813                   if (!(-int_max <= value && value <= +int_max))
5814                      mpl_error(mpl, "cannot convert %.*g to integer",
5815                         DBL_DIG, value);
5816                   print_text(mpl, from, (int)floor(value + 0.5));
5817                }
5818                else
5819                   print_text(mpl, from, value);
5820             }
5821             else if (*c == 's')
5822             {  /* the specifier requires symbolic value */
5823                char value[MAX_LENGTH+1];
5824                switch (entry->code->type)
5825                {  case A_NUMERIC:
5826                      sprintf(value, "%.*g", DBL_DIG, eval_numeric(mpl,
5827                         entry->code));
5828                      break;
5829                   case A_LOGICAL:
5830                      if (eval_logical(mpl, entry->code))
5831                         strcpy(value, "T");
5832                      else
5833                         strcpy(value, "F");
5834                      break;
5835                   case A_SYMBOLIC:
5836                      sym = eval_symbolic(mpl, entry->code);
5837                      if (sym->str == NULL)
5838                         sprintf(value, "%.*g", DBL_DIG, sym->num);
5839                      else
5840                         fetch_string(mpl, sym->str, value);
5841                      delete_symbol(mpl, sym);
5842                      break;
5843                   default:
5844                      xassert(entry != entry);
5845                }
5846                print_text(mpl, from, value);
5847             }
5848             else
5849                mpl_error(mpl, "format specifier missing or invalid");
5850             *(c+1) = save;
5851             entry = entry->next;
5852          }
5853          else if (*c == '\\')
5854          {  /* write some control character */
5855             c++;
5856             if (*c == 't')
5857                print_char(mpl, '\t');
5858             else if (*c == 'n')
5859                print_char(mpl, '\n');
5860             else
5861                print_char(mpl, *c);
5862          }
5863          else
5864          {  /* write character without formatting */
5865             print_char(mpl, *c);
5866          }
5867       }
5868       return 0;
5869 }
5870 
5871 #if 0 /* 14/VII-2006 */
5872 void execute_printf(MPL *mpl, PRINTF *prt)
5873 {     loop_within_domain(mpl, prt->domain, prt, printf_func);
5874       return;
5875 }
5876 #else
execute_printf(MPL * mpl,PRINTF * prt)5877 void execute_printf(MPL *mpl, PRINTF *prt)
5878 {     if (prt->fname == NULL)
5879       {  /* switch to the standard output */
5880          if (mpl->prt_fp != NULL)
5881          {  xfclose(mpl->prt_fp), mpl->prt_fp = NULL;
5882             xfree(mpl->prt_file), mpl->prt_file = NULL;
5883          }
5884       }
5885       else
5886       {  /* evaluate file name string */
5887          SYMBOL *sym;
5888          char fname[MAX_LENGTH+1];
5889          sym = eval_symbolic(mpl, prt->fname);
5890          if (sym->str == NULL)
5891             sprintf(fname, "%.*g", DBL_DIG, sym->num);
5892          else
5893             fetch_string(mpl, sym->str, fname);
5894          delete_symbol(mpl, sym);
5895          /* close the current print file, if necessary */
5896          if (mpl->prt_fp != NULL &&
5897             (!prt->app || strcmp(mpl->prt_file, fname) != 0))
5898          {  xfclose(mpl->prt_fp), mpl->prt_fp = NULL;
5899             xfree(mpl->prt_file), mpl->prt_file = NULL;
5900          }
5901          /* open the specified print file, if necessary */
5902          if (mpl->prt_fp == NULL)
5903          {  mpl->prt_fp = xfopen(fname, prt->app ? "a" : "w");
5904             if (mpl->prt_fp == NULL)
5905                mpl_error(mpl, "unable to open `%s' for writing - %s",
5906                   fname, xerrmsg());
5907             mpl->prt_file = xmalloc(strlen(fname)+1);
5908             strcpy(mpl->prt_file, fname);
5909          }
5910       }
5911       loop_within_domain(mpl, prt->domain, prt, printf_func);
5912       if (mpl->prt_fp != NULL)
5913       {  xfflush(mpl->prt_fp);
5914          if (xferror(mpl->prt_fp))
5915             mpl_error(mpl, "writing error to `%s' - %s", mpl->prt_file,
5916                xerrmsg());
5917       }
5918       return;
5919 }
5920 #endif
5921 
5922 /*----------------------------------------------------------------------
5923 -- clean_printf - clean printf statement.
5924 --
5925 -- This routine cleans specified printf statement that assumes deleting
5926 -- all stuff dynamically allocated on generating/postsolving phase. */
5927 
clean_printf(MPL * mpl,PRINTF * prt)5928 void clean_printf(MPL *mpl, PRINTF *prt)
5929 {     PRINTF1 *p;
5930       /* clean subscript domain */
5931       clean_domain(mpl, prt->domain);
5932       /* clean pseudo-code for computing format string */
5933       clean_code(mpl, prt->fmt);
5934       /* clean printf list */
5935       for (p = prt->list; p != NULL; p = p->next)
5936       {  /* clean pseudo-code for computing value to be printed */
5937          clean_code(mpl, p->code);
5938       }
5939 #if 1 /* 14/VII-2006 */
5940       /* clean pseudo-code for computing file name string */
5941       clean_code(mpl, prt->fname);
5942 #endif
5943       return;
5944 }
5945 
5946 /*----------------------------------------------------------------------
5947 -- execute_for - execute for statement.
5948 --
5949 -- This routine executes specified for statement. */
5950 
for_func(MPL * mpl,void * info)5951 static int for_func(MPL *mpl, void *info)
5952 {     /* this is auxiliary routine to work within domain scope */
5953       FOR *fur = (FOR *)info;
5954       STATEMENT *stmt, *save;
5955       save = mpl->stmt;
5956       for (stmt = fur->list; stmt != NULL; stmt = stmt->next)
5957          execute_statement(mpl, stmt);
5958       mpl->stmt = save;
5959       return 0;
5960 }
5961 
execute_for(MPL * mpl,FOR * fur)5962 void execute_for(MPL *mpl, FOR *fur)
5963 {     loop_within_domain(mpl, fur->domain, fur, for_func);
5964       return;
5965 }
5966 
5967 /*----------------------------------------------------------------------
5968 -- clean_for - clean for statement.
5969 --
5970 -- This routine cleans specified for statement that assumes deleting all
5971 -- stuff dynamically allocated on generating/postsolving phase. */
5972 
clean_for(MPL * mpl,FOR * fur)5973 void clean_for(MPL *mpl, FOR *fur)
5974 {     STATEMENT *stmt;
5975       /* clean subscript domain */
5976       clean_domain(mpl, fur->domain);
5977       /* clean all sub-statements */
5978       for (stmt = fur->list; stmt != NULL; stmt = stmt->next)
5979          clean_statement(mpl, stmt);
5980       return;
5981 }
5982 
5983 /*----------------------------------------------------------------------
5984 -- execute_statement - execute specified model statement.
5985 --
5986 -- This routine executes specified model statement. */
5987 
execute_statement(MPL * mpl,STATEMENT * stmt)5988 void execute_statement(MPL *mpl, STATEMENT *stmt)
5989 {     mpl->stmt = stmt;
5990       switch (stmt->type)
5991       {  case A_SET:
5992          case A_PARAMETER:
5993          case A_VARIABLE:
5994             break;
5995          case A_CONSTRAINT:
5996             xprintf("Generating %s...\n", stmt->u.con->name);
5997             eval_whole_con(mpl, stmt->u.con);
5998             break;
5999          case A_TABLE:
6000             switch (stmt->u.tab->type)
6001             {  case A_INPUT:
6002                   xprintf("Reading %s...\n", stmt->u.tab->name);
6003                   break;
6004                case A_OUTPUT:
6005                   xprintf("Writing %s...\n", stmt->u.tab->name);
6006                   break;
6007                default:
6008                   xassert(stmt != stmt);
6009             }
6010             execute_table(mpl, stmt->u.tab);
6011             break;
6012          case A_SOLVE:
6013             break;
6014          case A_CHECK:
6015             xprintf("Checking (line %d)...\n", stmt->line);
6016             execute_check(mpl, stmt->u.chk);
6017             break;
6018          case A_DISPLAY:
6019             write_text(mpl, "Display statement at line %d\n",
6020                stmt->line);
6021             execute_display(mpl, stmt->u.dpy);
6022             break;
6023          case A_PRINTF:
6024             execute_printf(mpl, stmt->u.prt);
6025             break;
6026          case A_FOR:
6027             execute_for(mpl, stmt->u.fur);
6028             break;
6029          default:
6030             xassert(stmt != stmt);
6031       }
6032       return;
6033 }
6034 
6035 /*----------------------------------------------------------------------
6036 -- clean_statement - clean specified model statement.
6037 --
6038 -- This routine cleans specified model statement that assumes deleting
6039 -- all stuff dynamically allocated on generating/postsolving phase. */
6040 
clean_statement(MPL * mpl,STATEMENT * stmt)6041 void clean_statement(MPL *mpl, STATEMENT *stmt)
6042 {     switch(stmt->type)
6043       {  case A_SET:
6044             clean_set(mpl, stmt->u.set); break;
6045          case A_PARAMETER:
6046             clean_parameter(mpl, stmt->u.par); break;
6047          case A_VARIABLE:
6048             clean_variable(mpl, stmt->u.var); break;
6049          case A_CONSTRAINT:
6050             clean_constraint(mpl, stmt->u.con); break;
6051 #if 1 /* 11/II-2008 */
6052          case A_TABLE:
6053             clean_table(mpl, stmt->u.tab); break;
6054 #endif
6055          case A_SOLVE:
6056             break;
6057          case A_CHECK:
6058             clean_check(mpl, stmt->u.chk); break;
6059          case A_DISPLAY:
6060             clean_display(mpl, stmt->u.dpy); break;
6061          case A_PRINTF:
6062             clean_printf(mpl, stmt->u.prt); break;
6063          case A_FOR:
6064             clean_for(mpl, stmt->u.fur); break;
6065          default:
6066             xassert(stmt != stmt);
6067       }
6068       return;
6069 }
6070 
6071 /* eof */
6072