1 /*
2  * (C) Copyright 2005- ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  *
7  * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
8  * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
9  */
10 #include "grib_api_internal.h"
11 
12 
13 /*
14  * C Implementation: gaussian_reduced
15  *
16  * Description: computes the number of points within the range
17  *              lon_first->lon_last and the zero based indexes
18  *              ilon_first,ilon_last of the first and last point
19  *              given the number of points along a parallel (pl)
20  *
21  */
22 #define EFDEBUG 0
23 
24 #ifndef LLONG_MAX
25 #define LLONG_MAX LONG_MAX
26 #endif
27 #ifndef ULLONG_MAX
28 #define ULLONG_MAX ULONG_MAX
29 #endif
30 
31 
32 typedef long long Fraction_value_type;
33 
34 typedef struct Fraction_type
35 {
36     Fraction_value_type top_;
37     Fraction_value_type bottom_;
38 } Fraction_type;
39 
40 const Fraction_value_type MAX_DENOM = 3037000499; /* sqrt(LLONG_MAX) */
41 
fraction_gcd(Fraction_value_type a,Fraction_value_type b)42 static Fraction_value_type fraction_gcd(Fraction_value_type a, Fraction_value_type b)
43 {
44     while (b != 0) {
45         Fraction_value_type r = a % b;
46         a                     = b;
47         b                     = r;
48     }
49     return a;
50 }
fraction_construct(Fraction_value_type top,Fraction_value_type bottom)51 static Fraction_type fraction_construct(Fraction_value_type top, Fraction_value_type bottom)
52 {
53     Fraction_type result;
54 
55     /* @note in theory we also assume that numerator and denominator are both representable in
56      * double without loss
57      *   ASSERT(top == Fraction_value_type(double(top)));
58      *   ASSERT(bottom == Fraction_value_type(double(bottom)));
59      */
60     Fraction_value_type g;
61     Fraction_value_type sign = 1;
62     Assert(bottom != 0);
63     if (top < 0) {
64         top  = -top;
65         sign = -sign;
66     }
67 
68     if (bottom < 0) {
69         bottom = -bottom;
70         sign   = -sign;
71     }
72 
73     g = fraction_gcd(top, bottom);
74     if (g != 0) {
75         top    = top / g;
76         bottom = bottom / g;
77     }
78 
79     result.top_    = sign * top;
80     result.bottom_ = bottom;
81     return result;
82 }
83 
fraction_construct_from_double(double x)84 static Fraction_type fraction_construct_from_double(double x)
85 {
86     Fraction_type result;
87     double value             = x;
88     Fraction_value_type sign = 1;
89     Fraction_value_type m00 = 1, m11 = 1, m01 = 0, m10 = 0;
90     Fraction_value_type a = x;
91     Fraction_value_type t2, top, bottom, g;
92     size_t cnt = 0;
93 
94     /*Assert(x != NAN);*/
95     Assert(fabs(x) < 1e30);
96 
97     if (x < 0) {
98         sign = -sign;
99         x    = -x;
100     }
101 
102     t2 = m10 * a + m11;
103 
104     while (t2 <= MAX_DENOM) {
105         Fraction_value_type t1 = m00 * a + m01;
106         m01                    = m00;
107         m00                    = t1;
108 
109         m11 = m10;
110         m10 = t2;
111 
112         if (x == a) {
113             break;
114         }
115 
116         x = 1.0 / (x - a);
117 
118         if (x > LLONG_MAX) {
119             break;
120         }
121 
122         a  = x;
123         t2 = m10 * a + m11;
124 
125         if (cnt++ > 10000) {
126             fprintf(stderr, "Cannot compute fraction from %g\n", value);
127         }
128     }
129 
130     while (m10 >= MAX_DENOM || m00 >= MAX_DENOM) {
131         m00 >>= 1;
132         m10 >>= 1;
133     }
134 
135     top    = m00;
136     bottom = m10;
137 
138     g      = fraction_gcd(top, bottom);
139     top    = top / g;
140     bottom = bottom / g;
141 
142     result.top_    = sign * top;
143     result.bottom_ = bottom;
144     return result;
145 }
146 
fraction_integralPart(const Fraction_type frac)147 static Fraction_value_type fraction_integralPart(const Fraction_type frac)
148 {
149     Assert(frac.bottom_);
150     if (frac.bottom_ == 0) return frac.top_;
151     return frac.top_ / frac.bottom_;
152 }
153 
154 /*static int fraction_operator_equality(Fraction_type self, Fraction_type other)
155 {
156     return self.top_ == other.top_ && self.bottom_ == other.bottom_;
157 }*/
158 
fraction_operator_double(Fraction_type self)159 static double fraction_operator_double(Fraction_type self)
160 {
161     return (double)self.top_ / (double)self.bottom_;
162 }
163 
fraction_mul(int * overflow,Fraction_value_type a,Fraction_value_type b)164 static Fraction_value_type fraction_mul(int* overflow, Fraction_value_type a, Fraction_value_type b)
165 {
166     if (*overflow) {
167         return 0;
168     }
169 
170     if (b != 0) {
171         *overflow = llabs(a) > (ULLONG_MAX / llabs(b));
172     }
173     return a * b;
174 }
175 
176 /* Fraction Fraction::operator/(const Fraction& other) */
fraction_operator_divide(Fraction_type self,Fraction_type other)177 static Fraction_type fraction_operator_divide(Fraction_type self, Fraction_type other)
178 {
179     int overflow = 0; /*boolean*/
180 
181     Fraction_value_type top    = fraction_mul(&overflow, self.top_, other.bottom_);
182     Fraction_value_type bottom = fraction_mul(&overflow, self.bottom_, other.top_);
183 
184     if (!overflow) {
185         return fraction_construct(top, bottom);
186     }
187     else {
188         /*Fallback option*/
189         /*return Fraction(double(*this) / double(other));*/
190         double d1        = fraction_operator_double(self);
191         double d2        = fraction_operator_double(other);
192         Fraction_type f1 = fraction_construct_from_double(d1 / d2);
193         return f1;
194     }
195 }
196 
197 /* Fraction Fraction::operator*(const Fraction& other) */
fraction_operator_multiply(Fraction_type self,Fraction_type other)198 static Fraction_type fraction_operator_multiply(Fraction_type self, Fraction_type other)
199 {
200     int overflow = 0; /*boolean*/
201 
202     Fraction_value_type top    = fraction_mul(&overflow, self.top_, other.top_);
203     Fraction_value_type bottom = fraction_mul(&overflow, self.bottom_, other.bottom_);
204 
205     if (!overflow) {
206         return fraction_construct(top, bottom);
207     }
208     else {
209         /* Fallback option */
210         /*return Fraction(double(*this) * double(other));*/
211         double d1        = fraction_operator_double(self);
212         double d2        = fraction_operator_double(other);
213         Fraction_type f1 = fraction_construct_from_double(d1 * d2);
214         return f1;
215     }
216 }
217 
218 /* bool Fraction::operator<(const Fraction& other) */
fraction_operator_less_than(Fraction_type self,Fraction_type other)219 static int fraction_operator_less_than(Fraction_type self, Fraction_type other)
220 {
221     int overflow = 0;
222     int result   = fraction_mul(&overflow, self.top_, other.bottom_) < fraction_mul(&overflow, other.top_, self.bottom_);
223     if (overflow) {
224         double d1 = fraction_operator_double(self);
225         double d2 = fraction_operator_double(other);
226         return (d1 < d2);
227         /* return double(*this) < double(other); */
228     }
229     return result;
230 }
231 
232 /* bool Fraction::operator>(const Fraction& other) */
fraction_operator_greater_than(Fraction_type self,Fraction_type other)233 static int fraction_operator_greater_than(Fraction_type self, Fraction_type other)
234 {
235     int overflow = 0;
236     int result   = fraction_mul(&overflow, self.top_, other.bottom_) > fraction_mul(&overflow, other.top_, self.bottom_);
237     if (overflow) {
238         double d1 = fraction_operator_double(self);
239         double d2 = fraction_operator_double(other);
240         return (d1 > d2);
241         /* return double(*this) > double(other);*/
242     }
243     return result;
244 }
245 
246 /*explicit Fraction(long long n): top_(n), bottom_(1) {}*/
fraction_construct_from_long_long(long long n)247 static Fraction_type fraction_construct_from_long_long(long long n)
248 {
249     Fraction_type result;
250     result.top_    = n;
251     result.bottom_ = 1;
252     return result;
253 }
254 
255 /*template<class T>Fraction operator*(T n, const Fraction& f){    return Fraction(n) * f;  }*/
fraction_operator_multiply_n_Frac(Fraction_value_type n,Fraction_type f)256 static Fraction_type fraction_operator_multiply_n_Frac(Fraction_value_type n, Fraction_type f)
257 {
258     Fraction_type ft     = fraction_construct_from_long_long(n);
259     Fraction_type result = fraction_operator_multiply(ft, f);
260     return result;
261     /*return Fraction(n) * f;*/
262 }
263 
get_min(Fraction_value_type a,Fraction_value_type b)264 static Fraction_value_type get_min(Fraction_value_type a, Fraction_value_type b)
265 {
266     return ((a < b) ? a : b);
267 }
268 
gaussian_reduced_row(long long Ni_globe,const Fraction_type w,const Fraction_type e,long long * pNi,double * pLon1,double * pLon2)269 static void gaussian_reduced_row(
270     long long Ni_globe,    /*plj*/
271     const Fraction_type w, /*lon_first*/
272     const Fraction_type e, /*lon_last*/
273     long long* pNi,        /*npoints*/
274     double* pLon1,
275     double* pLon2)
276 {
277     Fraction_value_type Nw, Ne;
278     Fraction_type inc, Nw_inc, Ne_inc;
279     inc = fraction_construct(360ll, Ni_globe);
280 
281     /* auto Nw = (w / inc).integralPart(); */
282     Nw     = fraction_integralPart(fraction_operator_divide(w, inc));
283     Nw_inc = fraction_operator_multiply_n_Frac(Nw, inc);
284 
285     Assert(Ni_globe > 1);
286     /*if (Nw * inc < w) {*/
287     if (fraction_operator_less_than(Nw_inc, w)) {
288         Nw += 1;
289     }
290 
291     /*auto Ne = (e / inc).integralPart();*/
292     Ne     = fraction_integralPart(fraction_operator_divide(e, inc));
293     Ne_inc = fraction_operator_multiply_n_Frac(Ne, inc);
294     /* if (Ne * inc > e) */
295     if (fraction_operator_greater_than(Ne_inc, e)) {
296         Ne -= 1;
297     }
298     if (Nw > Ne) {
299         *pNi   = 0;          /* no points on this latitude */
300         *pLon1 = *pLon2 = 0; /* dummy - unused */
301     }
302     else {
303         *pNi = get_min(Ni_globe, Ne - Nw + 1);
304 
305         Nw_inc = fraction_operator_multiply_n_Frac(Nw, inc);
306         *pLon1 = fraction_operator_double(Nw_inc);
307         Ne_inc = fraction_operator_multiply_n_Frac(Ne, inc);
308         *pLon2 = fraction_operator_double(Ne_inc);
309     }
310 }
311 
312 /* --------------------------------------------------------------------------------------------------- */
grib_get_reduced_row_wrapper(grib_handle * h,long pl,double lon_first,double lon_last,long * npoints,long * ilon_first,long * ilon_last)313 void grib_get_reduced_row_wrapper(grib_handle* h, long pl, double lon_first, double lon_last, long* npoints, long* ilon_first, long* ilon_last)
314 {
315     grib_get_reduced_row(pl, lon_first, lon_last, npoints, ilon_first, ilon_last);
316 
317     /* Legacy
318      * grib_get_reduced_row1(pl, lon_first, lon_last, npoints, ilon_first, ilon_last);
319      */
320 }
321 
322 /* This was the legacy way of counting the points within a subarea of a Gaussian grid.
323    In the days of Prodgen/libemos */
grib_get_reduced_row_legacy(long pl,double lon_first,double lon_last,long * npoints,long * ilon_first,long * ilon_last)324 void grib_get_reduced_row_legacy(long pl, double lon_first, double lon_last, long* npoints, long* ilon_first, long* ilon_last)
325 {
326     double range = 0, dlon_first = 0, dlon_last = 0;
327     long irange;
328     range = lon_last - lon_first;
329     if (range < 0) {
330         range += 360;
331         lon_first -= 360;
332     }
333 
334     /* computing integer number of points and coordinates without using floating point resolution*/
335     *npoints    = (range * pl) / 360.0 + 1;
336     *ilon_first = (lon_first * pl) / 360.0;
337     *ilon_last  = (lon_last * pl) / 360.0;
338 
339     irange = *ilon_last - *ilon_first + 1;
340 
341 #if EFDEBUG
342     printf("  pl=%ld npoints=%ld range=%.10e ilon_first=%ld ilon_last=%ld irange=%ld\n",
343            pl, *npoints, range, *ilon_first, *ilon_last, irange);
344 #endif
345 
346     if (irange != *npoints) {
347 #if EFDEBUG
348         printf("       ---> (irange=%ld) != (npoints=%ld) ", irange, *npoints);
349 #endif
350         if (irange > *npoints) {
351             /* checking if the first point is out of range*/
352             dlon_first = ((*ilon_first) * 360.0) / pl;
353             if (dlon_first < lon_first) {
354                 (*ilon_first)++;
355                 irange--;
356 #if EFDEBUG
357                 printf(" dlon_first=%.10e < lon_first=%.10e\n", dlon_first, lon_first);
358 #endif
359             }
360 
361             /* checking if the last point is out of range*/
362             dlon_last = ((*ilon_last) * 360.0) / pl;
363             if (dlon_last > lon_last) {
364                 (*ilon_last)--;
365                 irange--;
366 #if EFDEBUG
367                 printf(" dlon_last=%.10e < lon_last=%.10e\n", dlon_last, lon_last);
368 #endif
369             }
370         }
371         else {
372             int ok = 0;
373             /* checking if the point before the first is in the range*/
374             dlon_first = ((*ilon_first - 1) * 360.0) / pl;
375             if (dlon_first > lon_first) {
376                 (*ilon_first)--;
377                 irange++;
378                 ok = 1;
379 #if EFDEBUG
380                 printf(" dlon_first1=%.10e > lon_first=%.10e\n", dlon_first, lon_first);
381 #endif
382             }
383 
384             /* checking if the point after the last is in the range*/
385             dlon_last = ((*ilon_last + 1) * 360.0) / pl;
386             if (dlon_last < lon_last) {
387                 (*ilon_last)++;
388                 irange++;
389                 ok = 1;
390 #if EFDEBUG
391                 printf(" dlon_last1=%.10e > lon_last=%.10e\n", dlon_last, lon_first);
392 #endif
393             }
394 
395             /* if neither of the two are triggered then npoints is too large */
396             if (!ok) {
397                 (*npoints)--;
398 #if EFDEBUG
399                 printf(" (*npoints)--=%ld\n", *npoints);
400 #endif
401             }
402         }
403 
404         /*Assert(*npoints==irange);*/
405 #if EFDEBUG
406         printf("--  pl=%ld npoints=%ld range=%.10e ilon_first=%ld ilon_last=%ld irange=%ld\n",
407                pl, *npoints, range, *ilon_first, *ilon_last, irange);
408 #endif
409     }
410     else {
411         /* checking if the first point is out of range*/
412         dlon_first = ((*ilon_first) * 360.0) / pl;
413         if (dlon_first < lon_first) {
414             (*ilon_first)++;
415             (*ilon_last)++;
416 #if EFDEBUG
417             printf("       ---> dlon_first=%.10e < lon_first=%.10e\n", dlon_first, lon_first);
418             printf("--  pl=%ld npoints=%ld range=%.10e ilon_first=%ld ilon_last=%ld irange=%ld\n",
419                    pl, *npoints, range, *ilon_first, *ilon_last, irange);
420 #endif
421         }
422     }
423 
424     if (*ilon_first < 0)
425         *ilon_first += pl;
426 
427     return;
428 }
429 
430 /* New method based on eckit Fractions and matching MIR count */
grib_get_reduced_row(long pl,double lon_first,double lon_last,long * npoints,long * ilon_first,long * ilon_last)431 void grib_get_reduced_row(long pl, double lon_first, double lon_last, long* npoints, long* ilon_first, long* ilon_last)
432 {
433     long long Ni_globe = pl;
434     Fraction_type west;
435     Fraction_type east;
436     long long the_count;
437     double the_lon1, the_lon2;
438 
439     while (lon_last < lon_first)
440         lon_last += 360;
441     west = fraction_construct_from_double(lon_first);
442     east = fraction_construct_from_double(lon_last);
443 
444     gaussian_reduced_row(
445         Ni_globe, /*plj*/
446         west,     /*lon_first*/
447         east,     /*lon_last*/
448         &the_count,
449         &the_lon1,
450         &the_lon2);
451     *npoints    = (long)the_count;
452     *ilon_first = (the_lon1 * pl) / 360.0;
453     *ilon_last  = (the_lon2 * pl) / 360.0;
454 }
455 
456 /* This version returns the actual first and last longitudes rather than indexes */
grib_get_reduced_row_p(long pl,double lon_first,double lon_last,long * npoints,double * olon_first,double * olon_last)457 void grib_get_reduced_row_p(long pl, double lon_first, double lon_last, long* npoints, double* olon_first, double* olon_last)
458 {
459     long long Ni_globe = pl;
460     Fraction_type west;
461     Fraction_type east;
462     long long the_count;
463     double the_lon1, the_lon2;
464 
465     while (lon_last < lon_first)
466         lon_last += 360;
467     west = fraction_construct_from_double(lon_first);
468     east = fraction_construct_from_double(lon_last);
469 
470     gaussian_reduced_row(
471         Ni_globe, /*plj*/
472         west,     /*lon_first*/
473         east,     /*lon_last*/
474         &the_count,
475         &the_lon1,
476         &the_lon2);
477     *npoints    = (long)the_count;
478     *olon_first = the_lon1;
479     *olon_last  = the_lon2;
480 }
481