summaryrefslogtreecommitdiff
path: root/tools/metaMPFR/metaMPFR_straightforwardAlgo.mpl
blob: bfa8aca0d85d2a53c00244f179070c5921770d49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
read("metaMPFR_common.mpl"):

FUNCTION_SERIES := 0:
FUNCTION_SERIES_RATIONAL := %+1:
CONSTANT_SERIES := %+1:

###############################################################################
######################### We can now generate the code ########################
###############################################################################
# This procedure generate code for a straightforward evaluation of the series
# corresponding to a recurrence.
#   rec is a recurrence with inital conditions
#   type can take three values, depending on which series should be evaluated:
#     FUNCTION_SERIES -> produces code for evaluating sum( a(i)*x^i )
#     FUNCTION_SERIES_RATIONAL -> produces code for evaluating sum( a(i)*(p/q)^i )
#     CONSTANT_SERIES -> produces code for evaluating sum( a(i) )
#   name is the name the should be given to the produced procedure.
#   fofx is an optional parameter. It is the function being implemented.
#     -> if provided, this argument will be used to (heuristically) find the limits
#        of the function at +/-oo and find its asymptotical behavior.
#   *IMPORTANT NOTE*: it must be a function of the variable 'x'.
#                     Moreover, neither _f nor x shall be assigned at the time of
#                     calling the function
#    
generateStraightforwardAlgo := proc(rec, aofn, type, name, filename, fofx := _f(x))
  local a, b, n, fd, init_cond, nc, d, exponents, formalRec, c, s1, s2, p, q, hardconstant, f0, i0, ri0, i, j, var, var1, var2, var3, guard_bits, required_mulsi, required_divsi, temp, error_counter, error_in_loop, maxofti, additional_error:

  getname(aofn, a, n):

  required_mulsi := {}:
  required_divsi := {}:
  fd := fopen(filename, WRITE):

  # We check that we have a recurrence of the form a(n+d)=r(n)*a(n)
  # and we extract its canonical form:
  #    a(n+d) = c * s1(n)/s1(n+d) * s2(n+d)/s2(n) * p(n)/q(n) * a(n)
  #
  formalRec := checkOneTermRecurrence(rec, a(n)):
  decomposeOneTermRecurrence(formalRec, c, s1, s2, p, q):
  d := formalRec:-order:

  # We keep only non-trivial inital conditions
  init_cond := removeTrivialConditions(rec, a(n));
  nc := nops(init_cond):
  exponents := {1, d}:
  for i from 1 to nc do
    exponents := { op(exponents), init_cond[i][1] }
  od:
  exponents := findFixpointOfDifferences(exponents):
  exponents := exponents minus {0}:

  error_counter := []:

  fprintf(fd, "/* Evaluation by a straightforward algorithm */\n"):
  fprintf(fd, "/* Code automatically generated by metaMPFR. */\n"):

  fprintf(fd, "static int\n"):
  if (type=FUNCTION_SERIES)
    then fprintf(fd, "mpfr_%a (mpfr_ptr res, mpfr_srcptr x, mpfr_rnd_t rnd)\n", name):
  elif (type=FUNCTION_SERIES_RATIONAL)
    then fprintf(fd, "mpfr_%a (mpfr_ptr res, int u, int v, mpfr_rnd_t rnd)\n", name):
  elif (type=CONSTANT_SERIES)
    then fprintf(fd, "mpfr_%a (mpfr_ptr res, mpfr_rnd_t rnd)\n", name):
  fi:
  fprintf(fd, "{\n"):


  ######################################################
  #################### Declarations ####################
  ######################################################

  fprintf(fd, "  MPFR_ZIV_DECL (loop);\n"):
  fprintf(fd, "  MPFR_SAVE_EXPO_DECL (expo);\n"):
  fprintf(fd, "  mpfr_prec_t wprec;             /* working precision */\n"):
  fprintf(fd, "  mpfr_prec_t prec;              /* target precision */\n"):
  fprintf(fd, "  mpfr_prec_t err;               /* used to estimate the evaluation error */\n"):
  fprintf(fd, "  mpfr_prec_t correctBits;       /* estimates the number of correct bits*/\n"):
  fprintf(fd, "  unsigned long int k;\n"):
  fprintf(fd, "  unsigned long int conditionNumber;        /* condition number of the series */\n"):
  fprintf(fd, "  unsigned assumed_exponent;     /* used as a lowerbound of -EXP(f(x)) */\n"):
  fprintf(fd, "  int r;                         /* returned ternary value */\n"):
  fprintf(fd, "  mpfr_t s;                      /* used to store the partial sum */\n"):
  
  if (whattype(c) = 'fraction') or (whattype(c) = 'integer')
    then hardconstant := 0
    else hardconstant := 1
  fi:
  if (type=FUNCTION_SERIES) then hardconstant := 1 fi:

  if (hardconstant=1)
  then
    if (type=CONSTANT_SERIES)
      then fprintf(fd, "  mpfr_t x%d;                     /* used to store %a */\n", d, c):
    elif (type=FUNCTION_SERIES_RATIONAL)
      then fprintf(fd, "  mpfr_t x%d;                     /* used to store %a */\n", d, c*(u/v)^d):
    elif (type=FUNCTION_SERIES)
      then fprintf(fd, "  mpfr_t x%d;                     /* used to store %a */\n", d, c*x^d):
    fi:
  fi:

  if (type=FUNCTION_SERIES)
  then
    fprintf(fd, "  mpfr_t tmp;\n"):
    if (nops(exponents)-1 >= 1) then fprintf(fd, "  mpfr_t ") fi:
    for i from 1 to nops(exponents)-1 do
      fprintf(fd, "x%d", exponents[i]):
      if (i<nops(exponents)-1) then fprintf(fd, ", ") else fprintf(fd, ";                     /* used to store x^i */\n") fi
    od:
  fi:

  fprintf(fd, "  mpfr_t ");
  for i from 1 to nc do
    fprintf(fd, "tip%d", init_cond[i][1]):
    if (i<nc) then fprintf(fd, ", ") else fprintf(fd, "; /* used to store successive values of t_i */\n") fi:
  od:
  fprintf(fd, "  int "):
  for i from 1 to nc do
    fprintf(fd, "test%d", init_cond[i][1]):
    if (i<nc) then fprintf(fd, ", ") else fprintf(fd, ";\n") fi:
  od:
  fprintf(fd, "  int global_test;               /* used to test when the sum can be stopped */"):

  fprintf(fd, "\n"):
  fprintf(fd, "  /* Logging */\n"):
  if (type=FUNCTION_SERIES)
    then fprintf(fd, "  MPFR_LOG_FUNC ( (\"x[%%#R]=%%R rnd=%%d\", x, x, rnd), (\"res[%%#R]=%%R\", res, res) );\n\n")
  elif (type=FUNCTION_SERIES_RATIONAL)
    then fprintf(fd, "  MPFR_LOG_FUNC ( (\"x=u/v with u=%%d and v=%%d, rnd=%%d\", u, v, rnd), (\"res[%%#R]=%%R\", res, res) );\n\n")
  else fprintf(fd, "  MPFR_LOG_FUNC ( (\"rnd=%%d\", rnd), (\"res[%%#R]=%%R\", res, res) );\n\n")
  fi:


  ######################################################
  #################### Special cases ###################
  ######################################################

  if ( (type=FUNCTION_SERIES) or (type=FUNCTION_SERIES_RATIONAL) )
  then
    fprintf(fd, "  /* Special cases */\n"):
 
    if (type=FUNCTION_SERIES)
      then fprintf(fd, "  if (MPFR_UNLIKELY (MPFR_IS_NAN (x)))\n")
      else fprintf(fd, "  if (MPFR_UNLIKELY (v==0))\n")
    fi:
    fprintf(fd, "    {\n"):
    fprintf(fd, "      MPFR_SET_NAN (res);\n"):
    fprintf(fd, "      MPFR_RET_NAN;\n"):
    fprintf(fd, "    }\n"):

    if (init_cond[1][1] > 0) then f0 := 0 else f0 := init_cond[1][2] fi:
    if (type=FUNCTION_SERIES)
      then fprintf(fd, "  if (MPFR_UNLIKELY (MPFR_IS_ZERO (x)))\n")
      else fprintf(fd, "  if (MPFR_UNLIKELY (u==0))\n")
    fi:
    if (whattype(f0) = 'integer')
    then
      fprintf(fd, "    {\n"):
      fprintf(fd, "      return mpfr_set_si (res, %a, rnd);\n", f0):
      fprintf(fd, "    }\n")
    else
      printf("You need to provide a function mpfr_%a0(mpfr_t, mpfr_rnd_t) that evaluates %a with correct rounding\n", name, f0):
      fprintf(fd, "    {\n"):
      fprintf(fd, "      return mpfr_%a0 (res, rnd);\n", name):
      fprintf(fd, "    }\n")
    fi:

    if (type=FUNCTION_SERIES)
    then
      fprintf(fd, "  if (MPFR_UNLIKELY (MPFR_IS_INF (x)))\n"):
      fprintf(fd, "    {\n"):
      for i from -1 to 1 by 2 do  # Trick to handle both -oo and +oo
        if (i<0)
          then fprintf(fd, "      if (MPFR_IS_NEG (x))\n"):
          else fprintf(fd, "      else\n")
        fi:
        fprintf(fd, "        {\n"):
        if (i<0) then f0 := "m" else f0 := "p" fi:
        if (fofx <> _f(x))
        then
          f0 := limit(fofx, x=i*infinity):
          if (whattype(f0)='integer')
          then fprintf(fd, "          return mpfr_set_si (res, %a, rnd);\n", f0):
          elif (f0 = infinity) or (f0 = -infinity)
          then
            fprintf(fd, "          MPFR_SET_INF(res);\n"):
            if (f0>0)
            then fprintf(fd, "          MPFR_SET_POS(res);\n")
            else fprintf(fd, "          MPFR_SET_NEG(res);\n")
            fi:
            fprintf(fd, "          MPFR_RET(0);\n"):
          else
            if (i<0) then f0 := "m" else f0 := "p" fi:
          fi: 
        fi:
        if ((f0 = "p") or (f0 = "m"))
        then
          printf("You need to provide a function mpfr_%a%sinf(mpfr_t, mpfr_rnd_t) that evaluates lim(f(x), x=", name, f0):
          if (f0 = "m") then printf("-") fi:
          printf("inf) with correct rounding.\n"):
          fprintf(fd, "          return mpfr_%a%sinf (res, rnd);\n", name, f0)
        fi:
        fprintf(fd, "        }\n")
      od:
      fprintf(fd, "    }\n")
    fi:
  fi:


  ######################################################
  ################## Precomputations ###################
  ######################################################

  fprintf(fd, "\n"):
  fprintf(fd, "  /* Save current exponents range */\n"):
  fprintf(fd, "  MPFR_SAVE_EXPO_MARK (expo);\n\n"):

  if ( (type=FUNCTION_SERIES) or (type=FUNCTION_SERIES_RATIONAL) )
  then  fprintf(fd, "  /* FIXME: special case for large values of |x| ? */\n\n")
  fi:

  # Note : prec is the value such that we will try to compute an approximation
  # with relative error smaller than 2^(1-prec).
  # Several things may happen:
  #   1) We do not achieve the intended error: this is because we badly estimated the exponent of the result
  #   2) We achieve the error, but it is not sufficient to decide correct rounding (Ziv's bad case)
  # Against 1), we can try to make our estimate of the exponent better with any heuristic.
  # Against 2), we can consider more guard bits. 11 guard bits seem a good value for the beginning
  #   (statistically, we expect to fail in less than 0.1 % of the cases)
  # wprec is the precision used during the computation, in order to ensure the final relative error 2^(1-prec)
  #
  fprintf(fd, "  /* We begin with 11 guard bits */\n"):
  fprintf(fd, "  prec = MPFR_PREC (res) + 11;\n"):
  fprintf(fd, "  MPFR_ZIV_INIT (loop, prec);\n"):

  # TODO: the value here can be chosen completely heursitically. We could do something much better
  # when fofx is known by using asympt(fofx, x, 1). A clean implementation appears to be complex though.
  # We must catch errors if the development does not exist (e.g. AiryAi(-x));
  # We must find a separation after which the asymptotic behavior is valid (e.g. x>1)
  printf("The code contains a variable assumed_exponent arbitrarily set to 10. You can put any value heuristically chosen. The closer it is to -log_2(|f(x)|), the better it is.\n"):
  fprintf(fd, "  assumed_exponent = 10; /* TIP: You can put any value heuristically chosen. The closer it is to -log_2(|f(x)|), the better it is */\n"):

  # TODO: find a way of putting a rigorous value here.
  # This value *must* be rigorous: the safety of the implementation relies on it.
  # Precisely, we need to have sum(|a(i)*x^i|) <= 2^conditionNumber
  fprintf(fd, "  conditionNumber = xxx; /* FIXME: set a value such that sum(|a(i)*x^i|) <= 2^conditionNumber */\n"):
  printf("The code contains a variable conditionNumber that you must manually set to a suitable value, in order to ensure that sum_{i=0}^{infinity} |a(i)*x^i| <= 2^conditionNumber\n"):
  fprintf(fd, "  wprec = prec + ERRORANALYSISPREC + conditionNumber + assumed_exponent;\n"):


  ######################################################
  ################## Initialisations ###################
  ######################################################

  if (hardconstant=1)
  then
    fprintf(fd, "  mpfr_init (x%d);\n", d):
  fi:
  if (type = FUNCTION_SERIES)
  then
    fprintf(fd, "  mpfr_init (tmp);\n"):
    for i from 1 to nops(exponents)-1 do
      fprintf(fd, "  mpfr_init (x%d);\n", exponents[i])
    od:
  fi:

  for i from 1 to nc do
    fprintf(fd, "  mpfr_init (tip%d);\n", init_cond[i][1])
  od:
  fprintf(fd, "  mpfr_init (s);\n\n"):


  ######################################################
  ########## Ziv' loop: setting the precision ##########
  ######################################################

  fprintf(fd, "  /* ZIV' loop */\n"):
  fprintf(fd, "  for (;;)\n"):
  fprintf(fd, "    {\n"):

  fprintf(fd, "      MPFR_LOG_MSG ((\"Working precision: %%d\\n\", wprec, 0));\n\n"):
  if (hardconstant=1)
  then
    fprintf(fd, "      mpfr_set_prec (x%d, wprec);\n", d):
  fi:
  if (type = FUNCTION_SERIES)
  then
    fprintf(fd, "      mpfr_set_prec (tmp, wprec);\n"):
    fprintf(fd, "      if(mpfr_get_prec (x) > wprec)\n"):
    fprintf(fd, "        mpfr_set_prec (x1, wprec);\n"):
    fprintf(fd, "      else\n"):
    fprintf(fd, "        mpfr_set_prec (x1, mpfr_get_prec (x));\n"):
    for i from 2 to nops(exponents)-1 do
      fprintf(fd, "      mpfr_set_prec (x%d, wprec);\n", exponents[i])
    od:
  fi:

  for i from 1 to nc do
    fprintf(fd, "      mpfr_set_prec (tip%d, wprec);\n", init_cond[i][1])
  od:
  fprintf(fd, "      mpfr_set_prec (s, wprec);\n\n"):


  ######################################################
  ############ Ziv' loop: initial conditions ###########
  ######################################################

  fprintf(fd, "      mpfr_set_ui (s, 0, MPFR_RNDN);\n"):

  if (type = FUNCTION_SERIES)
  then
    fprintf(fd, "      mpfr_set (x1, x, MPFR_RNDN);\n"):
    error_counter := init_error_counter("x1", error_counter):
    for i from 2 to nops(exponents) do
        fprintf(fd, "      mpfr_mul (x%d, x%d, x%d, MPFR_RNDN);\n", exponents[i], exponents[i-1], exponents[i]-exponents[i-1]):
	var1 := sprintf("x%d", exponents[i]):
        var2 := sprintf("x%d", exponents[i-1]):
        var3 := sprintf("x%d", exponents[i]-exponents[i-1]):
	error_counter := error_counter_of_a_multiplication(var1, var2, var3, error_counter):
    od:
  fi:

  for i from 1 to nc do
    i0 := init_cond[i][1]:
    ri0 := init_cond[i][2]:  # We implement t_{i0} <- ri0
    if (whattype(ri0)='integer') or (whattype(ri0)='fraction')
    then
      if (type = FUNCTION_SERIES) and (i0 <> 0)
        then 
          var :=  sprintf("      mpfr_mul_si (tip%d, x%d, ", i0, i0):
          var1 := sprintf("tip%d", i0):
          var2 := sprintf("x%d", i0):
          error_counter := error_counter_of_a_multiplication(var1, var2, "", error_counter):
        else 
          var := sprintf("      mpfr_set_si (tip%d, ", i0):
          var1 := sprintf("tip%d", i0):
          error_counter := init_error_counter(var1, error_counter):
      fi:
      fprintf(fd, "%s%d, MPFR_RNDN);\n", var, numer(ri0)):
      if (whattype(ri0)='fraction')
      then
        fprintf(fd, "      mpfr_div_si (tip%d, tip%d, %d, MPFR_RNDN);\n", i0, i0, denom(ri0)):
        var1 := sprintf("tip%d", i0):
        error_counter := error_counter_of_a_multiplication(var1, var1, "", error_counter):
      fi:
    else
      printf("You need to provide a function mpfr_%a%d (mpfr_t, mpfr_rnd_t) that evaluates %a with faithful rounding.\n", name, i0, ri0): 
      fprintf(fd, "      mpfr_%a%d (tip%d, MPFR_RNDN);\n", name, i0, i0):
      var1 := sprintf("tip%d", i0):
      error_counter := init_error_counter(var1, error_counter):
      if (type = FUNCTION_SERIES) and (i0 <> 0)
      then
        fprintf(fd, "      mpfr_mul (tip%d, tip%d, x%d, MPFR_RNDN);\n", i0, i0, i0):
        var1 := sprintf("tip%d", i0):
        var2 := sprintf("x%d", i0):
        error_counter := error_counter_of_a_multiplication(var1, var1, var2, error_counter):
      fi:
    fi:

    if (type = FUNCTION_SERIES_RATIONAL) and (i0 <> 0)
    then
      var1 := sprintf("tip%d", i0):
      if (i0 = 1)
      then fprintf(fd, "      mpfr_mul_si (tip%d, tip%d, ", i0, i0):
      else
        required_mulsi := { op(required_mulsi), i0 }:
        fprintf(fd, "      mpfr_mul_si%d (tip%d, tip%d, ", i0, i0, i0):
      fi:
      for j from 1 to i0 do
        fprintf(fd, "u, "):
        error_counter := error_counter_of_a_multiplication(var1, var1, "", error_counter):
      od:
      fprintf(fd, "MPFR_RNDN);\n"):
      if (i0 = 1)
      then fprintf(fd, "      mpfr_div_si (tip%d, tip%d, ", i0, i0):
      else
        required_divsi := { op(required_divsi), i0 }:
        fprintf(fd, "      mpfr_div_si%d (tip%d, tip%d, ", i0, i0, i0):
      fi:
      for j from 1 to i0 do
        fprintf(fd, "v, "):
        error_counter := error_counter_of_a_multiplication(var1, var1, "", error_counter):
      od:
      fprintf(fd, "MPFR_RNDN);\n"):
    fi:

    fprintf(fd, "      mpfr_add (s, s, tip%d, MPFR_RNDN);\n\n", i0):
  od:

  if (whattype(c) = 'integer') or (whattype(c) = 'fraction')
  then
    if (type = FUNCTION_SERIES) and (c = -1)
    then
      fprintf(fd, "      MPFR_CHANGE_SIGN (x%d);\n", d):
    elif (type = FUNCTION_SERIES) and (c <> 1)
    then
      fprintf(fd, "      mpfr_mul_si (x%d, x%d, %d, MPFR_RNDN);\n", d, d, numer(c)):
      var1 := sprintf("x%d", d):
      error_counter := error_counter_of_a_multiplication(var1, var1, "", error_counter):
      if (whattype(c) = 'fraction')
      then
        fprintf(fd, "      mpfr_div_si (x%d, x%d, %d, MPFR_RNDN);\n", d, d, denom(c)):
        error_counter := error_counter_of_a_multiplication(var1, var1, "", error_counter):
      fi:
    fi:
  else
    printf("You need to provide a function mpfr_%a_cste (mpfr_t, mpfr_rnd_t) that evaluates %a with faithful rounding.\n", name, c):
    if (type = CONSTANT_SERIES)
    then
      fprintf(fd, "      mpfr_%a_cste (x%d, MPFR_RNDN);\n", name, d):
      var1 := sprintf("x%d", d):
      error_counter := init_error_counter(var1, error_counter):
    elif (type = FUNCTION_SERIES) then
      fprintf(fd, "      mpfr_%a_cste (tmp, MPFR_RNDN);\n", name):
      error_counter := init_error_counter("tmp", error_counter):
      fprintf(fd, "      mpfr_mul (x%d, tmp, x%d, MPFR_RNDN);\n", d, d):
      var1 := sprintf("x%d", d):
      error_counter := error_counter_of_a_multiplication(var1, "tmp", var1, error_counter):
    elif (type = FUNCTION_SERIES_RATIONAL) then
      fprintf(fd, "      mpfr_%a_cste (x%d, MPFR_RNDN);\n", name, d):
      var1 := sprintf("x%d", d):
      error_counter := init_error_counter(var1, error_counter):
      if (d = 1)
      then fprintf(fd, "      mpfr_mul_si (x%d, x%d, ", d, d):
      else
        required_mulsi := { op(required_mulsi), d }:
        fprintf(fd, "      mpfr_mul_si%d (x%d, x%d, ", d, d, d):
      fi:
      for j from 1 to d do
        fprintf(fd, "u, "):
        error_counter := error_counter_of_a_multiplication(var1, var1, "", error_counter):
      od:
      fprintf(fd, "MPFR_RNDN);\n"):
      var1 := sprintf("x%d", d):
      if (d = 1)
      then fprintf(fd, "      mpfr_div_si (x%d, x%d, ", d, d):
      else
        required_divsi := { op(required_divsi), d }:
        fprintf(fd, "      mpfr_div_si%d (x%d, x%d, ", d, d, d):
      fi:
      for j from 1 to d do
        fprintf(fd, "v, "):
        error_counter := error_counter_of_a_multiplication(var1, var1, "", error_counter):
      od:
      fprintf(fd, "MPFR_RNDN);\n"): 
    fi:
  fi:

  ######################################################
  ######### Ziv' loop: evaluation of the series ########
  ######################################################

  fprintf(fd, "\n"):
  fprintf(fd, "      /* Evaluation of the series */\n"):
  fprintf(fd, "      k = %d;\n", d):
  fprintf(fd, "      for (;;)\n"):
  fprintf(fd, "        {\n"):
  if (init_cond[1][1] <> 0) then fprintf(fd, "          k += %d;\n", init_cond[1][1]) fi:

  for i from 1 to nc do
    error_in_loop := 0:
    i0 := init_cond[i][1]:
    if (hardconstant = 1)
    then 
      fprintf(fd, "          mpfr_mul (tip%d, tip%d, x%d, MPFR_RNDN);\n", i0, i0, d):
      var1 := sprintf("x%d", d):
      error_in_loop := error_in_loop + 1 + find_in_error_counter(var1, error_counter):
    else
      var := sprintf("tip%d", i0):
      temp := generate_multiply_rational(fd, var, var, c, [[var, error_in_loop]], "          "):
      error_in_loop := find_in_error_counter(var, temp):
      if (type = FUNCTION_SERIES_RATIONAL)
      then
        if (d=1)
        then
          fprintf(fd, "          mpfr_mul_si (tip%d, tip%d, u, MPFR_RNDN);\n", i0, i0):
          fprintf(fd, "          mpfr_div_si (tip%d, tip%d, v, MPFR_RNDN);\n", i0, i0):
	  error_in_loop := error_in_loop + 2:
        else
          required_mulsi := { op(required_mulsi), d }:
          fprintf(fd, "          mpfr_mul_si%d (tip%d, tip%d", d, i0, i0):
          for j from 1 to d do fprintf(fd, ", u") od:
          fprintf(fd, ", MPFR_RNDN);\n"):
	  error_in_loop := error_in_loop + d:

          required_divsi := { op(required_divsi), d }:
          fprintf(fd, "          mpfr_div_si%d (tip%d, tip%d", d, i0, i0):
          for j from 1 to d do fprintf(fd, ", v") od:
          fprintf(fd, ", MPFR_RNDN);\n" ):
	  error_in_loop := error_in_loop + d:
        fi
      fi
    fi:
    var := sprintf("tip%d", i0):

    temp := generate_multiply_poly(fd, var, var, subs(n=k-d, p/q), [[var, error_in_loop]], "          "):
    required_mulsi := { op(required_mulsi), op(temp[1]) }:
    required_divsi := { op(required_divsi), op(temp[2]) }:
    error_in_loop := find_in_error_counter(var, temp[3]):
    temp := generate_multiply_poly(fd, "tmp", var, subs(n=k, s2/s1), [[var, error_in_loop]], "          "):
    required_mulsi := { op(required_mulsi), op(temp[1]) }:
    required_divsi := { op(required_divsi), op(temp[2]) }:

    fprintf(fd, "          mpfr_add (s, s, tmp, MPFR_RNDN);\n"):
    
    if (i<nc) then fprintf(fd, "\n          k += %d;\n", init_cond[i+1][1]-i0)
    else  fprintf(fd, "\n          k += %d;\n", d-i0)
    fi:
  od:


  ######################################################
  ################### Error analysis ###################
  ######################################################

  maxofti := 0: # store the maximum of the error counters of the initial conditions
  for i from 1 to nc do
    var := sprintf("tip%d", init_cond[i][1]):
    if find_in_error_counter(var, error_counter) > maxofti
      then maxofti := find_in_error_counter(var, error_counter):
    fi:
  od:
  additional_error := find_in_error_counter("tmp", temp[3]) - error_in_loop:


  ######################################################
  #### Ziv' loop: stopping criterion for the series ####
  ######################################################

  # The first neglected term is tk, so the remainder is made by
  # tk + t(k+d) + t(k+2d).... and the corresponding series
  # beginning with t(k+1), t(k+2), etc. up to t(k+d-1).
  #
  # We have t(k0+d) = c*s1(k0)/s1(k0+d) * s2(k0+d)/s2(k0)* p(k0)/q(k0) * x^d t(k0)
  # (where x=u/v or x=1 in cases of rational series or constant series)
  # So it suffices that:
  # forall k0>=k-d, |c * s1(k0)/s1(k0+d) * s2(k0+d)/s2(k0) * p(k0)/q(k0) * x^d| <= 1/2     (1)
  #
  # If this is true, |tk| = |c*s1(k-d)/s1(k) * s2(k)/s2(k-d)* p(k-d)/q(k-d) * x^d t(k-d)| <= t(k-d)/2
  # This is also true for larger values of k, so we can bound |tk + t(k+d) + t(k+2d) + ...| by |t(k-d)|.
  # And the same holds for |t(k+1) + ...|, |t(k+2) + ...|, etc. up to |t(k+d-1)+...|.
  #
  # global_test depends on k and we must satisfy:
  #  "if (global_test) then (1) holds".
  #
  # the total remainder is bounded by 2*nc*tk.

  fprintf(fd, "          global_test = xxx; /* FIXME: set the value in order to ensure that, whenever global_test is true, we have: forall k'>=k, |r(k')*x^d| <= 1/2, where r is the fraction such that a(n)=r(n)a(n-d)*/\n"):
  printf("The code contains a variable global_test that you must manually set to a suitable value, in order to ensure that when global_test is true, the following holds:\n"):
  printf("        forall k'>=k, |r(k')*x^d| <= 1/2, where r is the fraction such that a(n)=r(n)a(n-d)\n"):
  guard_bits := 1+1+ceil(log[2](nc)):
  for i from 1 to nc do
    i0 := init_cond[i][1]:
    fprintf(fd, "          test%d = ( (!MPFR_IS_ZERO(s))\n", i0):
    fprintf(fd, "                    && ( MPFR_IS_ZERO(tip%d)\n", i0):
    fprintf(fd, "                         || (MPFR_EXP(tip%d) + (mp_exp_t)prec + %d <= MPFR_EXP(s))\n", i0, guard_bits):
    fprintf(fd, "                         )\n"):
    fprintf(fd, "                    );\n"):
  od:
  fprintf(fd, "          if (");
  for i from 1 to nc do
    fprintf(fd, "test%d && ", init_cond[i][1]):
  od:
  fprintf(fd, "global_test)\n"):
  fprintf(fd, "            break;\n"):
  fprintf(fd, "        }\n\n"):


  ######################################################
  ############## Ziv' loop: testing final ##############
  ######################################################

  fprintf(fd, "      MPFR_LOG_MSG ((\"Truncation rank: %%lu\\n\", k));\n\n"):
  fprintf(fd, "      err = ERRORANALYSISK + conditionNumber - MPFR_GET_EXP (s);\n\n"):
  fprintf(fd, "      /* err is the number of bits lost due to the evaluation error */\n"):
  fprintf(fd, "      /* wprec-(prec+1): number of bits lost due to the approximation error */\n"):
  fprintf(fd, "      MPFR_LOG_MSG ((\"Roundoff error: %%Pu\\n\", err));\n"):
  fprintf(fd, "      MPFR_LOG_MSG ((\"Approxim error: %%Pu\\n\", wprec-prec-1));\n\n"):
  fprintf(fd, "      if (wprec < err+1)\n"):
  fprintf(fd, "        correct_bits=0;\n"):
  fprintf(fd, "      else\n"):
  fprintf(fd, "        {\n"):
  fprintf(fd, "          if (wprec < err+prec+1)\n"):
  fprintf(fd, "            correct_bits =  wprec - err - 1;\n"):
  fprintf(fd, "          else\n"):
  fprintf(fd, "            correct_bits = prec;\n"):
  fprintf(fd, "        }\n\n"):
  fprintf(fd, "      if (MPFR_LIKELY (MPFR_CAN_ROUND (s, correct_bits, MPFR_PREC (y), rnd)))\n"):
  fprintf(fd, "        break;\n\n"):

  fprintf(fd, "      if (correct_bits == 0)\n"):
  fprintf(fd, "        {\n"):
  fprintf(fd, "          assumed_exponent *= 2;\n"):
  fprintf(fd, "          MPFR_LOG_MSG ((\"Not a single bit correct (assumed_exponent=%%lu)\\n\",\n"):
  fprintf(fd, "                         assumed_exponent));\n"):
  fprintf(fd, "          wprec = prec + ERRORANALYSISK + conditionNumber + assumed_exponent;\n"):
  fprintf(fd, "        }\n"):
  fprintf(fd, "      else\n"):
  fprintf(fd, "        {\n"):
  fprintf(fd, "          if (correct_bits < prec)\n"):
  fprintf(fd, "            { /* The precision was badly chosen */\n"):
  fprintf(fd, "              MPFR_LOG_MSG ((\"Bad assumption on the exponent of %s(x)\", 0));\n", name):
  fprintf(fd, "              MPFR_LOG_MSG ((\" (E=%%ld)\\n\", (long) MPFR_GET_EXP (s)));\n"):
  fprintf(fd, "              wprec = prec + err + 1;\n"):
  fprintf(fd, "            }\n"):
  fprintf(fd, "          else\n"):
  fprintf(fd, "            { /* We are really in a bad case of the TMD */\n"):
  fprintf(fd, "              MPFR_ZIV_NEXT (loop, prec);\n\n"):

  fprintf(fd, "              /* We update wprec */\n"):
  fprintf(fd, "              /* We assume that K will not be multiplied by more than 4 */\n"):
  fprintf(fd, "              wprec = prec + ERRORANALYSIS4K + conditionNumber\n"):
  fprintf(fd, "                - MPFR_GET_EXP (s);\n"):
  fprintf(fd, "            }\n"):
  fprintf(fd, "        }\n\n"):

  fprintf(fd, "    } /* End of ZIV loop */\n\n"):
  fprintf(fd, "  MPFR_ZIV_FREE (loop);\n\n"):
  fprintf(fd, "  r = mpfr_set (res, s, rnd);\n\n"):


  ######################################################
  ################ Clearing everything #################
  ######################################################

  fprintf(fd, "  mpfr_clear (s);\n"):
  if (hardconstant=1)
  then
    fprintf(fd, "  mpfr_clear (x%d);\n", d):
  fi:
  if (type = FUNCTION_SERIES)
  then
    fprintf(fd, "  mpfr_clear (tmp);\n"):
    for i from 1 to nops(exponents)-1 do
      fprintf(fd, "  mpfr_clear (x%d);\n", exponents[i]):
    od:
  fi:

  for i from 1 to nc do
    fprintf(fd, "  mpfr_clear (tip%d);\n", init_cond[i][1]):
  od:

  fprintf(fd, "\n"):
  fprintf(fd, "  MPFR_SAVE_EXPO_FREE (expo);\n"):
  fprintf(fd, "  return mpfr_check_range (res, r, rnd);\n"):
  fprintf(fd, "}\n"):
  
  fclose(fd):

  for i from 1 to nops(required_mulsi) do
    printf("You need to provide a mpfr_mul_si%d function.\n", required_mulsi[i]):
    printf("  -> This can be achieved by a call to generate_muldivsin(\"mul\", %d):\n",  required_mulsi[i]):
  od:
  for i from 1 to nops(required_divsi) do
    printf("You need to provide a mpfr_div_si%d function.\n", required_divsi[i]):
    printf("  -> This can be achieved by a call to generate_muldivsin(\"div\", %d):\n",  required_divsi[i]):
  od:


  ######################################################
  ################## Error analysis ####################
  ######################################################
  
  printf("\n\n"):
  printf("Before the loop, we have "):
  for i from 1 to nc do
    var := sprintf("tip%d", init_cond[i][1]):
    printf("%s {%d}", var, find_in_error_counter(var, error_counter)):
    if (i <> nc) then printf(", ") else printf("\n") fi:
  od:
  printf("Each step of the loop adds another {%d}\n", error_in_loop):
  if (additional_error <> 0)
  then printf("Moreover, the multiplication by %a adds another {%d} to each term before it is summed.\n", subs(n=k, s2/s1), additional_error)
  fi:
  printf("Finally, we have s = sum_(i=0)^(k-1) ( ti{%d + %dk} )\n", maxofti + additional_error + 1 - error_in_loop, error_in_loop):
  printf("We bound it by {(k+%d)*2^(%d)}\n", ceil( (maxofti + additional_error + 1 - error_in_loop)/error_in_loop), ceil(log[2](error_in_loop))):

  a := ceil( (maxofti + additional_error + 1 - error_in_loop)/error_in_loop):
  b := ceil(log[2](error_in_loop)):

  if (a > 0)
    then var := sprintf("MPFR_INT_CEIL_LOG2 (prec + %d)", a)
    elif (a=0) then  var := sprintf("MPFR_INT_CEIL_LOG2 (prec)")
    else sprintf("MPFR_INT_CEIL_LOG2 (prec - %d)", -a)
  fi:
  if (b > 0) then var := sprintf("%s + %d", var, b+2) fi:
  var := sprintf("sed -n -i 's/ERRORANALYSISPREC/%s/g;p' %s", var, filename):
  system(var):

  if (a > 0)
    then var := sprintf("MPFR_INT_CEIL_LOG2 (k + %d)", a)
    elif (a=0) then  var := sprintf("MPFR_INT_CEIL_LOG2 (k)")
    else sprintf("MPFR_INT_CEIL_LOG2 (k - %d)", -a)
  fi:
  if (b > 0) then var := sprintf("%s + %d", var, b+2) fi:
  var := sprintf("sed -n -i 's/ERRORANALYSISK/%s/g;p' %s", var, filename):
  system(var):

  if (a > 0)
    then var := sprintf("MPFR_INT_CEIL_LOG2 (k + %d)", a)
    elif (a=0) then  var := sprintf("MPFR_INT_CEIL_LOG2 (k)")
    else sprintf("MPFR_INT_CEIL_LOG2 (k - %d)", -a)
  fi:
  var := sprintf("%s + %d", var, b+4):
  var := sprintf("sed -n -i 's/ERRORANALYSIS4K/%s/g;p' %s", var, filename):
  system(var):

end proc: