1/* mpfr_cache -- cache interface for multiple-precision constants in MPFR.
2
3Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
4Contributed by the AriC and Caramel projects, INRIA.
5
6This file is part of the GNU MPFR Library.
7
8The GNU MPFR Library is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as published by
10the Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
12
13The GNU MPFR Library is distributed in the hope that it will be useful, but
14WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16License for more details.
17
18You should have received a copy of the GNU Lesser General Public License
19along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
20http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
2151 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22
23#include "mpfr-impl.h"
24
25#if 0 /* this function is not used/documented/tested so far, it could be
26         useful if some user wants to add a new constant to mpfr, and
27         implement a cache mechanism for that constant */
28void
29mpfr_init_cache (mpfr_cache_t cache, int (*func)(mpfr_ptr, mpfr_rnd_t))
30{
31  MPFR_PREC (cache->x) = 0; /* Invalid prec to detect that the cache is not
32                               valid. Maybe add a flag? */
33  cache->func = func;
34}
35#endif
36
37void
38mpfr_clear_cache (mpfr_cache_t cache)
39{
40  if (MPFR_PREC (cache->x) != 0)
41    mpfr_clear (cache->x);
42  MPFR_PREC (cache->x) = 0;
43}
44
45int
46mpfr_cache (mpfr_ptr dest, mpfr_cache_t cache, mpfr_rnd_t rnd)
47{
48  mpfr_prec_t prec = MPFR_PREC (dest);
49  mpfr_prec_t pold = MPFR_PREC (cache->x);
50  int inexact, sign;
51  MPFR_SAVE_EXPO_DECL (expo);
52
53  MPFR_SAVE_EXPO_MARK (expo);
54
55  if (MPFR_UNLIKELY (prec > pold))
56    {
57      /* No previous result in the cache or the precision of the
58         previous result is not sufficient. */
59
60      if (MPFR_UNLIKELY (pold == 0))  /* No previous result. */
61        mpfr_init2 (cache->x, prec);
62
63      /* Update the cache. */
64      pold = prec;
65      /* no need to keep the previous value */
66      mpfr_set_prec (cache->x, pold);
67      cache->inexact = (*cache->func) (cache->x, MPFR_RNDN);
68    }
69
70  /* now pold >= prec is the precision of cache->x */
71
72  /* First, check if the cache has the exact value (unlikely).
73     Else the exact value is between (assuming x=cache->x > 0):
74       x and x+ulp(x) if cache->inexact < 0,
75       x-ulp(x) and x if cache->inexact > 0,
76     and abs(x-exact) <= ulp(x)/2. */
77
78  /* we assume all cached constants are positive */
79  MPFR_ASSERTN (MPFR_IS_POS (cache->x)); /* TODO... */
80  sign = MPFR_SIGN (cache->x);
81  MPFR_SET_EXP (dest, MPFR_GET_EXP (cache->x));
82  MPFR_SET_SIGN (dest, sign);
83
84  /* round cache->x from precision pold down to precision prec */
85  MPFR_RNDRAW_GEN (inexact, dest,
86                   MPFR_MANT (cache->x), pold, rnd, sign,
87                   if (MPFR_UNLIKELY (cache->inexact == 0))
88                     {
89                       if ((_sp[0] & _ulp) == 0)
90                         {
91                           inexact = -sign;
92                           goto trunc_doit;
93                         }
94                       else
95                         goto addoneulp;
96                     }
97                   else if (cache->inexact < 0)
98                     goto addoneulp;
99                   else /* cache->inexact > 0 */
100                     {
101                       inexact = -sign;
102                       goto trunc_doit;
103                     },
104                   if (MPFR_UNLIKELY (++MPFR_EXP (dest) > __gmpfr_emax))
105                     mpfr_overflow (dest, rnd, sign);
106                  );
107
108  if (MPFR_LIKELY (cache->inexact != 0))
109    {
110      switch (rnd)
111        {
112        case MPFR_RNDZ:
113        case MPFR_RNDD:
114          if (MPFR_UNLIKELY (inexact == 0))
115            {
116              inexact = cache->inexact;
117              if (inexact > 0)
118                {
119                  mpfr_nextbelow (dest);
120                  inexact = -inexact;
121                }
122            }
123          break;
124        case MPFR_RNDU:
125        case MPFR_RNDA:
126          if (MPFR_UNLIKELY (inexact == 0))
127            {
128              inexact = cache->inexact;
129              if (inexact < 0)
130                {
131                  mpfr_nextabove (dest);
132                  inexact = -inexact;
133                }
134            }
135          break;
136        default: /* MPFR_RNDN */
137          if (MPFR_UNLIKELY(inexact == 0))
138            inexact = cache->inexact;
139          break;
140        }
141    }
142
143  MPFR_SAVE_EXPO_FREE (expo);
144  return mpfr_check_range (dest, inexact, rnd);
145}
146