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