1 /*
2  *  This file is part of Healpix_cxx.
3  *
4  *  Healpix_cxx is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Healpix_cxx is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Healpix_cxx; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  *  For more information about HEALPix, see http://healpix.sourceforge.net
19  */
20 
21 /*
22  *  Healpix_cxx is being developed at the Max-Planck-Institut fuer Astrophysik
23  *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt
24  *  (DLR).
25  */
26 
27 /*
28  *  Copyright (C) 2003-2020 Max-Planck-Society
29  *  Author: Martin Reinecke
30  */
31 
32 #include "ducc0/healpix/healpix_base.h"
33 #include "ducc0/math/geom_utils.h"
34 #include "ducc0/math/constants.h"
35 #include "ducc0/infra/mav.h"
36 #include "ducc0/math/space_filling.h"
37 
38 namespace ducc0 {
39 
40 namespace detail_healpix {
41 
42 using namespace std;
43 
44 namespace {
45 
46 template<typename T> inline T spread(int v);
spread(int v)47 template<> inline int spread(int v)
48   { return spread_bits_2D_32(v); }
spread(int v)49 template<> inline int64_t spread(int v)
50   { return spread_bits_2D_64(v); }
51 
52 template<typename T> inline T interleave(int x, int y);
interleave(int x,int y)53 template<> inline int interleave(int x, int y)
54   { return coord2morton2D_32({uint32_t(x), uint32_t(y)}); }
interleave(int x,int y)55 template<> inline int64_t interleave(int x, int y)
56   { return coord2morton2D_64({uint32_t(x), uint32_t(y)}); }
57 
58 template<typename T> inline void deinterleave(T v, int &x, int &y);
deinterleave(int v,int & x,int & y)59 template<> inline void deinterleave(int v, int &x, int &y)
60   {
61   auto res = morton2coord2D_32(v);
62   x = res[0];
63   y = res[1];
64   }
deinterleave(int64_t v,int & x,int & y)65 template<> inline void deinterleave(int64_t v, int &x, int &y)
66   {
67   auto res = morton2coord2D_64(v);
68   x = res[0];
69   y = res[1];
70   }
71 
72 }
73 
nside2order(I nside)74 template<typename I> int T_Healpix_Base<I>::nside2order (I nside)
75   {
76   MR_assert (nside>I(0), "invalid value for Nside");
77   return ((nside)&(nside-1)) ? -1 : ilog2(nside);
78   }
npix2nside(I npix)79 template<typename I> I T_Healpix_Base<I>::npix2nside (I npix)
80   {
81   I res=isqrt(npix/I(12));
82   MR_assert (npix==res*res*I(12), "invalid value for npix");
83   return res;
84   }
85 
ring_above(double z) const86 template<typename I> I T_Healpix_Base<I>::ring_above (double z) const
87   {
88   double az=abs(z);
89   if (az<=twothird) // equatorial region
90     return I(nside_*(2-1.5*z));
91   I iring = I(nside_*sqrt(3*(1-az)));
92   return (z>0) ? iring : 4*nside_-iring-1;
93   }
94 
95 namespace {
96 
97 /* Short note on the "zone":
98    zone = 0: pixel lies completely outside the queried shape
99           1: pixel may overlap with the shape, pixel center is outside
100           2: pixel center is inside the shape, but maybe not the complete pixel
101           3: pixel lies completely inside the shape */
102 
check_pixel(size_t o,size_t order_,size_t omax,size_t zone,rangeset<I2> & pixset,I pix,vector<pair<I,size_t>> & stk,bool inclusive,size_t & stacktop)103 template<typename I, typename I2> inline void check_pixel (size_t o, size_t order_,
104   size_t omax, size_t zone, rangeset<I2> &pixset, I pix, vector<pair<I,size_t> > &stk,
105   bool inclusive, size_t &stacktop)
106   {
107   if (zone==0) return;
108 
109   if (o<order_)
110     {
111     if (zone>=3)
112       {
113       int sdist=2*(order_-o); // the "bit-shift distance" between map orders
114       pixset.append(pix<<sdist,(pix+1)<<sdist); // output all subpixels
115       }
116     else // (1<=zone<=2)
117       for (size_t i=0; i<4; ++i)
118         stk.push_back(make_pair(4*pix+3-i,o+1)); // add children
119     }
120   else if (o>order_) // this implies that inclusive==true
121     {
122     if (zone>=2) // pixel center in shape
123       {
124       pixset.append(pix>>(2*(o-order_))); // output the parent pixel at order_
125       stk.resize(stacktop); // unwind the stack
126       }
127     else // (zone==1): pixel center in safety range
128       {
129       if (o<omax) // check sublevels
130         for (int i=0; i<4; ++i) // add children in reverse order
131           stk.push_back(make_pair(4*pix+3-i,o+1));
132       else // at resolution limit
133         {
134         pixset.append(pix>>(2*(o-order_))); // output the parent pixel at order_
135         stk.resize(stacktop); // unwind the stack
136         }
137       }
138     }
139   else // o==order_
140     {
141     if (zone>=2)
142       pixset.append(pix);
143     else if (inclusive) // and (zone>=1)
144       {
145       if (order_<omax) // check sublevels
146         {
147         stacktop=stk.size(); // remember current stack position
148         for (size_t i=0; i<4; ++i) // add children in reverse order
149           stk.push_back(make_pair(4*pix+3-i,o+1));
150         }
151       else // at resolution limit
152         pixset.append(pix); // output the pixel
153       }
154     }
155   }
156 
check_pixel_ring(const T_Healpix_Base<I> & b1,const T_Healpix_Base<I> & b2,I pix,I nr,I ipix1,int fct,double cz,double cphi,double cosrp2,I cpix)157 template<typename I> bool check_pixel_ring (const T_Healpix_Base<I> &b1,
158   const T_Healpix_Base<I> &b2, I pix, I nr, I ipix1, int fct,
159   double cz, double cphi, double cosrp2, I cpix)
160   {
161   if (pix>=nr) pix-=nr;
162   if (pix<0) pix+=nr;
163   pix+=ipix1;
164   if (pix==cpix) return false; // disk center in pixel => overlap
165   int px,py,pf;
166   b1.pix2xyf(pix,px,py,pf);
167   for (int i=0; i<fct-1; ++i) // go along the 4 edges
168     {
169     I ox=fct*px, oy=fct*py;
170     double pz,pphi;
171     b2.pix2zphi(b2.xyf2pix(ox+i,oy,pf),pz,pphi);
172     if (cosdist_zphi(pz,pphi,cz,cphi)>cosrp2) // overlap
173       return false;
174     b2.pix2zphi(b2.xyf2pix(ox+fct-1,oy+i,pf),pz,pphi);
175     if (cosdist_zphi(pz,pphi,cz,cphi)>cosrp2) // overlap
176       return false;
177     b2.pix2zphi(b2.xyf2pix(ox+fct-1-i,oy+fct-1,pf),pz,pphi);
178     if (cosdist_zphi(pz,pphi,cz,cphi)>cosrp2) // overlap
179       return false;
180     b2.pix2zphi(b2.xyf2pix(ox,oy+fct-1-i,pf),pz,pphi);
181     if (cosdist_zphi(pz,pphi,cz,cphi)>cosrp2) // overlap
182       return false;
183     }
184   return true;
185   }
186 
187 } // unnamed namespace
188 
189 template<typename I> template<typename I2>
query_disc_internal(pointing ptg,double radius,int fact,rangeset<I2> & pixset) const190   void T_Healpix_Base<I>::query_disc_internal
191   (pointing ptg, double radius, int fact, rangeset<I2> &pixset) const
192   {
193   bool inclusive = (fact!=0);
194   pixset.clear();
195   ptg.normalize();
196 
197   if (scheme_==RING)
198     {
199     I fct=1;
200     if (inclusive)
201       {
202       MR_assert (((I(1)<<order_max)/nside_)>=fact,
203         "invalid oversampling factor");
204       fct = fact;
205       }
206     T_Healpix_Base b2;
207     double rsmall, rbig;
208     if (fct>1)
209       {
210       b2.SetNside(fct*nside_,RING);
211       rsmall = radius+b2.max_pixrad();
212       rbig = radius+max_pixrad();
213       }
214     else
215       rsmall = rbig = inclusive ? radius+max_pixrad() : radius;
216 
217     if (rsmall>=pi)
218       { pixset.append(0,npix_); return; }
219 
220     rbig = min(pi,rbig);
221 
222     double cosrsmall = cos(rsmall);
223     double cosrbig = cos(rbig);
224 
225     double z0 = cos(ptg.theta);
226     double xa = 1./sqrt((1-z0)*(1+z0));
227 
228     I cpix=zphi2pix(z0,ptg.phi);
229 
230     double rlat1 = ptg.theta - rsmall;
231     double zmax = cos(rlat1);
232     I irmin = ring_above (zmax)+1;
233 
234     if ((rlat1<=0) && (irmin>1)) // north pole in the disk
235       {
236       I sp,rp; bool dummy;
237       get_ring_info_small(irmin-1,sp,rp,dummy);
238       pixset.append(0,sp+rp);
239       }
240 
241     if ((fct>1) && (rlat1>0)) irmin=max(I(1),irmin-1);
242 
243     double rlat2 = ptg.theta + rsmall;
244     double zmin = cos(rlat2);
245     I irmax = ring_above (zmin);
246 
247     if ((fct>1) && (rlat2<pi)) irmax=min(4*nside_-1,irmax+1);
248 
249     for (I iz=irmin; iz<=irmax; ++iz)
250       {
251       double z=ring2z(iz);
252       double x = (cosrbig-z*z0)*xa;
253       double ysq = 1-z*z-x*x;
254       double dphi=-1;
255       if (ysq<=0) // no intersection, ring completely inside or outside
256         dphi = (fct==1) ? 0: pi-1e-15;
257       else
258         dphi = atan2(sqrt(ysq),x);
259       if (dphi>0)
260         {
261         I nr, ipix1;
262         bool shifted;
263         get_ring_info_small(iz,ipix1,nr,shifted);
264         double shift = shifted ? 0.5 : 0.;
265 
266         I ipix2 = ipix1 + nr - 1; // highest pixel number in the ring
267 
268         I ip_lo = ifloor<I>(nr*inv_twopi*(ptg.phi-dphi) - shift)+1;
269         I ip_hi = ifloor<I>(nr*inv_twopi*(ptg.phi+dphi) - shift);
270 
271         if (fct>1)
272           {
273           while ((ip_lo<=ip_hi) && check_pixel_ring
274                 (*this,b2,ip_lo,nr,ipix1,fct,z0,ptg.phi,cosrsmall,cpix))
275             ++ip_lo;
276           while ((ip_hi>ip_lo) && check_pixel_ring
277                 (*this,b2,ip_hi,nr,ipix1,fct,z0,ptg.phi,cosrsmall,cpix))
278             --ip_hi;
279           }
280 
281         if (ip_lo<=ip_hi)
282           {
283           if (ip_hi>=nr)
284             { ip_lo-=nr; ip_hi-=nr; }
285           if (ip_lo<0)
286             {
287             pixset.append(ipix1,ipix1+ip_hi+1);
288             pixset.append(ipix1+ip_lo+nr,ipix2+1);
289             }
290           else
291             pixset.append(ipix1+ip_lo,ipix1+ip_hi+1);
292           }
293         }
294       }
295     if ((rlat2>=pi) && (irmax+1<4*nside_)) // south pole in the disk
296       {
297       I sp,rp; bool dummy;
298       get_ring_info_small(irmax+1,sp,rp,dummy);
299       pixset.append(sp,npix_);
300       }
301     }
302   else // scheme_==NEST
303     {
304     if (radius>=pi) // disk covers the whole sphere
305       { pixset.append(0,npix_); return; }
306 
307     int oplus = 0;
308     if (inclusive)
309       {
310       MR_assert ((I(1)<<(order_max-order_))>=fact,
311         "invalid oversampling factor");
312       MR_assert ((fact&(fact-1))==0,
313         "oversampling factor must be a power of 2");
314       oplus=ilog2(fact);
315       }
316     int omax=order_+oplus; // the order up to which we test
317 
318     vec3 vptg(ptg);
319     vector<T_Healpix_Base<I> > base(omax+1);
320     vector<double> crpdr(omax+1), crmdr(omax+1);
321     double cosrad=cos(radius);
322     for (int o=0; o<=omax; ++o) // prepare data at the required orders
323       {
324       base[o].Set(o,NEST);
325       double dr=base[o].max_pixrad(); // safety distance
326       crpdr[o] = (radius+dr>pi) ? -1. : cos(radius+dr);
327       crmdr[o] = (radius-dr<0.) ?  1. : cos(radius-dr);
328       }
329     vector<pair<I,size_t> > stk; // stack for pixel numbers and their orders
330     stk.reserve(12+3*omax); // reserve maximum size to avoid reallocation
331     for (int i=0; i<12; ++i) // insert the 12 base pixels in reverse order
332       stk.push_back(make_pair(I(11-i),0));
333 
334     size_t stacktop=0; // a place to save a stack position
335 
336     while (!stk.empty()) // as long as there are pixels on the stack
337       {
338       // pop current pixel number and order from the stack
339       I pix=stk.back().first;
340       int o=stk.back().second;
341       stk.pop_back();
342 
343       double z,phi;
344       base[o].pix2zphi(pix,z,phi);
345       // cosine of angular distance between pixel center and disk center
346       double cangdist=cosdist_zphi(vptg.z,ptg.phi,z,phi);
347 
348       if (cangdist>crpdr[o])
349         {
350         size_t zone = (cangdist<cosrad) ? 1 : ((cangdist<=crmdr[o]) ? 2 : 3);
351 
352         check_pixel (o, order_, omax, zone, pixset, pix, stk, inclusive,
353           stacktop);
354         }
355       }
356     }
357   }
358 
query_disc(pointing ptg,double radius,rangeset<I> & pixset) const359 template<typename I> void T_Healpix_Base<I>::query_disc
360   (pointing ptg, double radius, rangeset<I> &pixset) const
361   {
362   query_disc_internal (ptg, radius, 0, pixset);
363   }
364 
query_disc_inclusive(pointing ptg,double radius,rangeset<I> & pixset,int fact) const365 template<typename I> void T_Healpix_Base<I>::query_disc_inclusive
366   (pointing ptg, double radius, rangeset<I> &pixset, int fact) const
367   {
368   MR_assert(fact>0,"fact must be a positive integer");
369   if ((sizeof(I)<8) && (((I(1)<<order_max)/nside_)<fact))
370     {
371     T_Healpix_Base<int64_t> base2(nside_,scheme_,SET_NSIDE);
372     base2.query_disc_internal(ptg,radius,fact,pixset);
373     return;
374     }
375   query_disc_internal (ptg, radius, fact, pixset);
376   }
377 
378 template<typename I> template<typename I2>
query_multidisc(const vector<vec3> & norm,const vector<double> & rad,int fact,rangeset<I2> & pixset) const379   void T_Healpix_Base<I>::query_multidisc (const vector<vec3> &norm,
380   const vector<double> &rad, int fact, rangeset<I2> &pixset) const
381   {
382   bool inclusive = (fact!=0);
383   size_t nv=norm.size();
384   MR_assert(nv==rad.size(),"inconsistent input arrays");
385   pixset.clear();
386 
387   if (scheme_==RING)
388     {
389     I fct=1;
390     if (inclusive)
391       {
392       MR_assert (((I(1)<<order_max)/nside_)>=fact,
393         "invalid oversampling factor");
394       fct = fact;
395       }
396     T_Healpix_Base b2;
397     double rpsmall, rpbig;
398     if (fct>1)
399       {
400       b2.SetNside(fct*nside_,RING);
401       rpsmall = b2.max_pixrad();
402       rpbig = max_pixrad();
403       }
404     else
405       rpsmall = rpbig = inclusive ? max_pixrad() : 0;
406 
407     I irmin=1, irmax=4*nside_-1;
408     vector<double> z0,xa,cosrsmall,cosrbig;
409     vector<pointing> ptg;
410     vector<I> cpix;
411     for (size_t i=0; i<nv; ++i)
412       {
413       double rsmall=rad[i]+rpsmall;
414       if (rsmall<pi)
415         {
416         double rbig=min(pi,rad[i]+rpbig);
417         pointing pnt=pointing(norm[i]);
418         cosrsmall.push_back(cos(rsmall));
419         cosrbig.push_back(cos(rbig));
420         double cth=cos(pnt.theta);
421         z0.push_back(cth);
422         if (fct>1) cpix.push_back(zphi2pix(cth,pnt.phi));
423         xa.push_back(1./sqrt((1-cth)*(1+cth)));
424         ptg.push_back(pnt);
425 
426         double rlat1 = pnt.theta - rsmall;
427         double zmax = cos(rlat1);
428         I irmin_t = (rlat1<=0) ? 1 : ring_above (zmax)+1;
429 
430         if ((fct>1) && (rlat1>0)) irmin_t=max(I(1),irmin_t-1);
431 
432         double rlat2 = pnt.theta + rsmall;
433         double zmin = cos(rlat2);
434         I irmax_t = (rlat2>=pi) ? 4*nside_-1 : ring_above (zmin);
435 
436         if ((fct>1) && (rlat2<pi)) irmax_t=min(4*nside_-1,irmax_t+1);
437 
438         if (irmax_t < irmax) irmax=irmax_t;
439         if (irmin_t > irmin) irmin=irmin_t;
440         }
441       }
442 
443     for (I iz=irmin; iz<=irmax; ++iz)
444       {
445       double z=ring2z(iz);
446       I ipix1,nr;
447       bool shifted;
448       get_ring_info_small(iz,ipix1,nr,shifted);
449       double shift = shifted ? 0.5 : 0.;
450       rangeset<I2> tr;
451       tr.append(ipix1,ipix1+nr);
452       for (size_t j=0; j<z0.size(); ++j)
453         {
454         double x = (cosrbig[j]-z*z0[j])*xa[j];
455         double ysq = 1.-z*z-x*x;
456         double dphi = (ysq<=0) ? pi-1e-15 : atan2(sqrt(ysq),x);
457         I ip_lo = ifloor<I>(nr*inv_twopi*(ptg[j].phi-dphi) - shift)+1;
458         I ip_hi = ifloor<I>(nr*inv_twopi*(ptg[j].phi+dphi) - shift);
459         if (fct>1)
460           {
461           while ((ip_lo<=ip_hi) && check_pixel_ring
462             (*this,b2,ip_lo,nr,ipix1,fct,z0[j],ptg[j].phi,cosrsmall[j],cpix[j]))
463             ++ip_lo;
464           while ((ip_hi>ip_lo) && check_pixel_ring
465             (*this,b2,ip_hi,nr,ipix1,fct,z0[j],ptg[j].phi,cosrsmall[j],cpix[j]))
466             --ip_hi;
467           }
468         if (ip_hi>=nr)
469           { ip_lo-=nr; ip_hi-=nr;}
470         if (ip_lo<0)
471           tr.remove(ipix1+ip_hi+1,ipix1+ip_lo+nr);
472         else
473           tr.intersect(ipix1+ip_lo,ipix1+ip_hi+1);
474         }
475       pixset.append(tr);
476       }
477     }
478   else // scheme_ == NEST
479     {
480     size_t oplus = 0;
481     if (inclusive)
482       {
483       MR_assert ((I(1)<<(order_max-order_))>=fact,
484         "invalid oversampling factor");
485       MR_assert ((fact&(fact-1))==0,
486         "oversampling factor must be a power of 2");
487       oplus=ilog2(fact);
488       }
489     size_t omax=size_t(order_)+oplus; // the order up to which we test
490 
491     // TODO: ignore all disks with radius>=pi
492 
493     vector<T_Healpix_Base<I> > base(omax+1);
494     vmav<double,3> crlimit({size_t(omax)+1,nv,3});
495     for (size_t o=0; o<=omax; ++o) // prepare data at the required orders
496       {
497       base[o].Set(o,NEST);
498       double dr=base[o].max_pixrad(); // safety distance
499       for (size_t i=0; i<nv; ++i)
500         {
501         crlimit(o,i,0) = (rad[i]+dr>pi) ? -1. : cos(rad[i]+dr);
502         crlimit(o,i,1) = (o==0) ? cos(rad[i]) : crlimit(0,i,1);
503         crlimit(o,i,2) = (rad[i]-dr<0.) ?  1. : cos(rad[i]-dr);
504         }
505       }
506 
507     vector<pair<I,size_t> > stk; // stack for pixel numbers and their orders
508     stk.reserve(12+3*omax); // reserve maximum size to avoid reallocation
509     for (int i=0; i<12; ++i) // insert the 12 base pixels in reverse order
510       stk.push_back(make_pair(I(11-i),0));
511 
512     size_t stacktop=0; // a place to save a stack position
513 
514     while (!stk.empty()) // as long as there are pixels on the stack
515       {
516       // pop current pixel number and order from the stack
517       I pix=stk.back().first;
518       size_t o=stk.back().second;
519       stk.pop_back();
520 
521       vec3 pv(base[o].pix2vec(pix));
522 
523       size_t zone=3;
524       for (size_t i=0; i<nv; ++i)
525         {
526         double crad=dotprod(pv,norm[i]);
527         for (size_t iz=0; iz<zone; ++iz)
528           if (crad<crlimit(o,i,iz))
529             if ((zone=iz)==0) goto bailout;
530         }
531 
532       check_pixel (o, order_, omax, zone, pixset, pix, stk, inclusive,
533         stacktop);
534       bailout:;
535       }
536     }
537   }
538 
query_multidisc_general(const vector<vec3> & norm,const vector<double> & rad,bool inclusive,const vector<int> & cmds,rangeset<I> & pixset) const539 template<typename I> void T_Healpix_Base<I>::query_multidisc_general
540   (const vector<vec3> &norm, const vector<double> &rad, bool inclusive,
541   const vector<int> &cmds, rangeset<I> &pixset) const
542   {
543   size_t nv=norm.size();
544   MR_assert(nv==rad.size(),"inconsistent input arrays");
545   pixset.clear();
546 
547   if (scheme_==RING)
548     {
549     MR_fail ("not yet implemented");
550     }
551   else // scheme_ == NEST
552     {
553     size_t oplus=inclusive ? 2 : 0;
554     size_t omax=min<size_t>(order_max,size_t(order_)+oplus); // the order up to which we test
555 
556     // TODO: ignore all disks with radius>=pi
557 
558     vector<T_Healpix_Base<I> > base(omax+1);
559     vmav<double,3> crlimit({size_t(omax+1),nv,3});
560     for (size_t o=0; o<=omax; ++o) // prepare data at the required orders
561       {
562       base[o].Set(o,NEST);
563       double dr=base[o].max_pixrad(); // safety distance
564       for (size_t i=0; i<nv; ++i)
565         {
566         crlimit(o,i,0) = (rad[i]+dr>pi) ? -1. : cos(rad[i]+dr);
567         crlimit(o,i,1) = (o==0) ? cos(rad[i]) : crlimit(0,i,1);
568         crlimit(o,i,2) = (rad[i]-dr<0.) ?  1. : cos(rad[i]-dr);
569         }
570       }
571 
572     vector<pair<I,size_t> > stk; // stack for pixel numbers and their orders
573     stk.reserve(12+3*omax); // reserve maximum size to avoid reallocation
574     for (int i=0; i<12; ++i) // insert the 12 base pixels in reverse order
575       stk.push_back(make_pair(I(11-i),0));
576 
577     size_t stacktop=0; // a place to save a stack position
578     vector<size_t> zone(nv);
579 
580     vector<size_t> zstk; zstk.reserve(cmds.size());
581 
582     while (!stk.empty()) // as long as there are pixels on the stack
583       {
584       // pop current pixel number and order from the stack
585       I pix=stk.back().first;
586       size_t o=stk.back().second;
587       stk.pop_back();
588 
589       vec3 pv(base[o].pix2vec(pix));
590 
591       for (size_t i=0; i<nv; ++i)
592         {
593         zone[i]=3;
594         double crad=dotprod(pv,norm[i]);
595         for (size_t iz=0; iz<zone[i]; ++iz)
596           if (crad<crlimit(o,i,iz))
597             zone[i]=iz;
598         }
599 
600       for (size_t i=0; i<cmds.size(); ++i)
601         {
602         size_t tmp;
603         switch (cmds[i])
604           {
605           case -1: // union
606             tmp=zstk.back(); zstk.pop_back();
607             zstk.back() = max(zstk.back(),tmp);
608             break;
609           case -2: // intersection
610             tmp=zstk.back(); zstk.pop_back();
611             zstk.back() = min(zstk.back(),tmp);
612             break;
613           default: // add value
614             zstk.push_back(zone[cmds[i]]);
615           }
616         }
617       MR_assert(zstk.size()==1,"inconsistent commands");
618       size_t zn=zstk[0]; zstk.pop_back();
619 
620       check_pixel (o, order_, omax, zn, pixset, pix, stk, inclusive,
621         stacktop);
622       }
623     }
624   }
625 
nest2xyf(I pix,int & ix,int & iy,int & face_num) const626 template<typename I> inline void T_Healpix_Base<I>::nest2xyf (I pix, int &ix,
627   int &iy, int &face_num) const
628   {
629   face_num = pix>>(2*order_);
630   pix &= (npface_-1);
631   deinterleave<I>(pix, ix, iy);
632   }
633 
xyf2nest(int ix,int iy,int face_num) const634 template<typename I> inline I T_Healpix_Base<I>::xyf2nest (int ix, int iy,
635   int face_num) const
636   { return (I(face_num)<<(2*order_)) + interleave<I>(ix, iy); }
637 
638 namespace {
639 
640 // low-level hack to accelerate divisions with a result of [0;3]
special_div(I a,I b)641 template<typename I> inline I special_div(I a, I b)
642   {
643   I t=(a>=(b<<1));
644   a-=t*(b<<1);
645   return (t<<1)+(a>=b);
646   }
647 
648 } // unnamed namespace
649 
ring2xyf(I pix,int & ix,int & iy,int & face_num) const650 template<typename I> void T_Healpix_Base<I>::ring2xyf (I pix, int &ix, int &iy,
651   int &face_num) const
652   {
653   I iring, iphi, kshift, nr;
654   I nl2 = 2*nside_;
655 
656   if (pix<ncap_) // North Polar cap
657     {
658     iring = (1+isqrt(1+2*pix))>>1; //counted from North pole
659     iphi  = (pix+1) - 2*iring*(iring-1);
660     kshift = 0;
661     nr = iring;
662     face_num=special_div(iphi-1,nr);
663     }
664   else if (pix<(npix_-ncap_)) // Equatorial region
665     {
666     I ip = pix - ncap_;
667     I tmp = (order_>=0) ? ip>>(order_+2) : ip/(4*nside_);
668     iring = tmp+nside_;
669     iphi = ip-tmp*4*nside_ + 1;
670     kshift = (iring+nside_)&1;
671     nr = nside_;
672     I ire = tmp+1,
673       irm = nl2+1-tmp;
674     I ifm = iphi - (ire>>1) + nside_ -1,
675       ifp = iphi - (irm>>1) + nside_ -1;
676     if (order_>=0)
677       { ifm >>= order_; ifp >>= order_; }
678     else
679       { ifm /= nside_; ifp /= nside_; }
680     face_num = (ifp==ifm) ? (ifp|4) : ((ifp<ifm) ? ifp : (ifm+8));
681     }
682   else // South Polar cap
683     {
684     I ip = npix_ - pix;
685     iring = (1+isqrt(2*ip-1))>>1; //counted from South pole
686     iphi  = 4*iring + 1 - (ip - 2*iring*(iring-1));
687     kshift = 0;
688     nr = iring;
689     iring = 2*nl2-iring;
690     face_num=special_div(iphi-1,nr)+8;
691     }
692 
693   I irt = iring - ((2+(face_num>>2))*nside_) + 1;
694   I ipt = 2*iphi- jpll[face_num]*nr - kshift -1;
695 //  I ipt = 2*iphi- (((face_num&3)<<1)+1-((face_num>>2)&1))*nr - kshift -1;
696   if (ipt>=nl2) ipt-=8*nside_;
697 
698   ix =  (ipt-irt) >>1;
699   iy = (-ipt-irt) >>1;
700   }
701 
xyf2ring(int ix,int iy,int face_num) const702 template<typename I> I T_Healpix_Base<I>::xyf2ring (int ix, int iy,
703   int face_num) const
704   {
705   I nl4 = 4*nside_;
706   I jr = (jrll[face_num]*nside_) - ix - iy  - 1;
707 
708   I nr, kshift, n_before;
709 
710   bool shifted;
711   get_ring_info_small(jr,n_before,nr,shifted);
712   nr>>=2;
713   kshift=1-shifted;
714   I jp = (jpll[face_num]*nr + ix - iy + 1 + kshift) / 2;
715   MR_assert(jp<=4*nr,"must not happen");
716   if (jp<1) jp+=nl4; // assumption: if this triggers, then nl4==4*nr
717 
718   return n_before + jp - 1;
719   }
720 
T_Healpix_Base()721 template<typename I> T_Healpix_Base<I>::T_Healpix_Base ()
722   : order_(-1), nside_(0), npface_(0), ncap_(0), npix_(0),
723     fact1_(0), fact2_(0), scheme_(RING) {}
724 
Set(int order,Ordering_Scheme scheme)725 template<typename I> void T_Healpix_Base<I>::Set (int order,
726   Ordering_Scheme scheme)
727   {
728   MR_assert ((order>=0)&&(order<=order_max), "bad order");
729   order_  = order;
730   nside_  = I(1)<<order;
731   npface_ = nside_<<order_;
732   ncap_   = (npface_-nside_)<<1;
733   npix_   = 12*npface_;
734   fact2_  = 4./npix_;
735   fact1_  = (nside_<<1)*fact2_;
736   scheme_ = scheme;
737   }
SetNside(I nside,Ordering_Scheme scheme)738 template<typename I> void T_Healpix_Base<I>::SetNside (I nside,
739   Ordering_Scheme scheme)
740   {
741   order_  = nside2order(nside);
742   MR_assert ((scheme!=NEST) || (order_>=0),
743     "SetNside: nside must be power of 2 for nested maps");
744   nside_  = nside;
745   npface_ = nside_*nside_;
746   ncap_   = (npface_-nside_)<<1;
747   npix_   = 12*npface_;
748   fact2_  = 4./npix_;
749   fact1_  = (nside_<<1)*fact2_;
750   scheme_ = scheme;
751   }
752 
ring2z(I ring) const753 template<typename I> double T_Healpix_Base<I>::ring2z (I ring) const
754   {
755   if (ring<nside_)
756     return 1 - ring*ring*fact2_;
757   if (ring <=3*nside_)
758     return (2*nside_-ring)*fact1_;
759   ring=4*nside_ - ring;
760   return ring*ring*fact2_ - 1;
761   }
762 
pix2ring(I pix) const763 template<typename I> I T_Healpix_Base<I>::pix2ring (I pix) const
764   {
765   if (scheme_==RING)
766     {
767     if (pix<ncap_) // North Polar cap
768       return (1+I(isqrt(1+2*pix)))>>1; // counted from North pole
769     else if (pix<(npix_-ncap_)) // Equatorial region
770       return (pix-ncap_)/(4*nside_) + nside_; // counted from North pole
771     else // South Polar cap
772       return 4*nside_-((1+I(isqrt(2*(npix_-pix)-1)))>>1);
773     }
774   else
775     {
776     int face_num, ix, iy;
777     nest2xyf(pix,ix,iy,face_num);
778     return (I(jrll[face_num])<<order_) - ix - iy - 1;
779     }
780   }
781 
nest2ring(I pix) const782 template<typename I> I T_Healpix_Base<I>::nest2ring (I pix) const
783   {
784   MR_assert(order_>=0, "hierarchical map required");
785   int ix, iy, face_num;
786   nest2xyf (pix, ix, iy, face_num);
787   return xyf2ring (ix, iy, face_num);
788   }
789 
ring2nest(I pix) const790 template<typename I> I T_Healpix_Base<I>::ring2nest (I pix) const
791   {
792   MR_assert(order_>=0, "hierarchical map required");
793   int ix, iy, face_num;
794   ring2xyf (pix, ix, iy, face_num);
795   return xyf2nest (ix, iy, face_num);
796   }
797 
nest_peano_helper(I pix,int dir) const798 template<typename I> inline I T_Healpix_Base<I>::nest_peano_helper
799   (I pix, int dir) const
800   {
801   int face = (pix>>(2*order_));
802   I result = 0;
803   int state = ((peano_face2path[dir][face]<<4))|(dir<<7);
804   int shift=2*order_-4;
805   for (; shift>=0; shift-=4)
806     {
807     state=peano_arr2[(state&0xF0) | ((pix>>shift)&0xF)];
808     result = (result<<4) | (state&0xF);
809     }
810   if (shift==-2)
811     {
812     state=peano_arr[((state>>2)&0xFC) | (pix&0x3)];
813     result = (result<<2) | (state&0x3);
814     }
815 
816   return result + (I(peano_face2face[dir][face])<<(2*order_));
817   }
818 
nest2peano(I pix) const819 template<typename I> I T_Healpix_Base<I>::nest2peano (I pix) const
820   { return nest_peano_helper(pix,0); }
821 
peano2nest(I pix) const822 template<typename I> I T_Healpix_Base<I>::peano2nest (I pix) const
823   { return nest_peano_helper(pix,1); }
824 
loc2pix(double z,double phi,double sth,bool have_sth) const825 template<typename I> I T_Healpix_Base<I>::loc2pix (double z, double phi,
826   double sth, bool have_sth) const
827   {
828   double za = abs(z);
829   double tt = fmodulo(phi*inv_halfpi,4.0); // in [0,4)
830 
831   if (scheme_==RING)
832     {
833     if (za<=twothird) // Equatorial region
834       {
835       I nl4 = 4*nside_;
836       double temp1 = nside_*(0.5+tt);
837       double temp2 = nside_*z*0.75;
838       I jp = I(temp1-temp2); // index of  ascending edge line
839       I jm = I(temp1+temp2); // index of descending edge line
840 
841       // ring number counted from z=2/3
842       I ir = nside_ + 1 + jp - jm; // in {1,2n+1}
843       I kshift = 1-(ir&1); // kshift=1 if ir even, 0 otherwise
844 
845       I t1 = jp+jm-nside_+kshift+1+nl4+nl4;
846       I ip = (order_>0) ?
847         (t1>>1)&(nl4-1) : ((t1>>1)%nl4); // in {0,4n-1}
848 
849       return ncap_ + (ir-1)*nl4 + ip;
850       }
851     else  // North & South polar caps
852       {
853       double tp = tt-I(tt);
854       double tmp = ((za<0.99)||(!have_sth)) ?
855                    nside_*sqrt(3*(1-za)) :
856                    nside_*sth/sqrt((1.+za)/3.);
857 
858       I jp = I(tp*tmp); // increasing edge line index
859       I jm = I((1.0-tp)*tmp); // decreasing edge line index
860 
861       I ir = jp+jm+1; // ring number counted from the closest pole
862       I ip = I(tt*ir); // in {0,4*ir-1}
863       MR_assert((ip>=0)&&(ip<4*ir),"must not happen");
864       //ip %= 4*ir;
865 
866       return (z>0)  ?  2*ir*(ir-1) + ip  :  npix_ - 2*ir*(ir+1) + ip;
867       }
868     }
869   else // scheme_ == NEST
870     {
871     if (za<=twothird) // Equatorial region
872       {
873       double temp1 = nside_*(0.5+tt);
874       double temp2 = nside_*(z*0.75);
875       I jp = I(temp1-temp2); // index of  ascending edge line
876       I jm = I(temp1+temp2); // index of descending edge line
877       I ifp = jp >> order_;  // in {0,4}
878       I ifm = jm >> order_;
879       int face_num = (ifp==ifm) ? (ifp|4) : ((ifp<ifm) ? ifp : (ifm+8));
880 
881       int ix = jm & (nside_-1),
882           iy = nside_ - (jp & (nside_-1)) - 1;
883       return xyf2nest(ix,iy,face_num);
884       }
885     else // polar region, za > 2/3
886       {
887       int ntt = min(3,int(tt));
888       double tp = tt-ntt;
889       double tmp = ((za<0.99)||(!have_sth)) ?
890                    nside_*sqrt(3*(1-za)) :
891                    nside_*sth/sqrt((1.+za)/3.);
892 
893       I jp = I(tp*tmp); // increasing edge line index
894       I jm = I((1.0-tp)*tmp); // decreasing edge line index
895       jp=min(jp,nside_-1); // for points too close to the boundary
896       jm=min(jm,nside_-1);
897       return (z>=0) ?
898         xyf2nest(nside_-jm -1,nside_-jp-1,ntt) : xyf2nest(jp,jm,ntt+8);
899       }
900     }
901   }
902 
pix2loc(I pix,double & z,double & phi,double & sth,bool & have_sth) const903 template<typename I> void T_Healpix_Base<I>::pix2loc (I pix, double &z,
904   double &phi, double &sth, bool &have_sth) const
905   {
906   have_sth=false;
907   if (scheme_==RING)
908     {
909     if (pix<ncap_) // North Polar cap
910       {
911       I iring = (1+I(isqrt(1+2*pix)))>>1; // counted from North pole
912       I iphi  = (pix+1) - 2*iring*(iring-1);
913 
914       double tmp=(iring*iring)*fact2_;
915       z = 1.0 - tmp;
916       if (z>0.99) { sth=sqrt(tmp*(2.-tmp)); have_sth=true; }
917       phi = (iphi-0.5) * halfpi/iring;
918       }
919     else if (pix<(npix_-ncap_)) // Equatorial region
920       {
921       I nl4 = 4*nside_;
922       I ip  = pix - ncap_;
923       I tmp = (order_>=0) ? ip>>(order_+2) : ip/nl4;
924       I iring = tmp + nside_,
925         iphi = ip-nl4*tmp+1;
926       // 1 if iring+nside is odd, 1/2 otherwise
927       double fodd = ((iring+nside_)&1) ? 1 : 0.5;
928 
929       z = (2*nside_-iring)*fact1_;
930       phi = (iphi-fodd) * pi*0.75*fact1_;
931       }
932     else // South Polar cap
933       {
934       I ip = npix_ - pix;
935       I iring = (1+I(isqrt(2*ip-1)))>>1; // counted from South pole
936       I iphi  = 4*iring + 1 - (ip - 2*iring*(iring-1));
937 
938       double tmp=(iring*iring)*fact2_;
939       z = tmp - 1.0;
940       if (z<-0.99) { sth=sqrt(tmp*(2.-tmp)); have_sth=true; }
941       phi = (iphi-0.5) * halfpi/iring;
942       }
943     }
944   else
945     {
946     int face_num, ix, iy;
947     nest2xyf(pix,ix,iy,face_num);
948 
949     I jr = (I(jrll[face_num])<<order_) - ix - iy - 1;
950 
951     I nr;
952     if (jr<nside_)
953       {
954       nr = jr;
955       double tmp=(nr*nr)*fact2_;
956       z = 1 - tmp;
957       if (z>0.99) { sth=sqrt(tmp*(2.-tmp)); have_sth=true; }
958       }
959     else if (jr > 3*nside_)
960       {
961       nr = nside_*4-jr;
962       double tmp=(nr*nr)*fact2_;
963       z = tmp - 1;
964       if (z<-0.99) { sth=sqrt(tmp*(2.-tmp)); have_sth=true; }
965       }
966     else
967       {
968       nr = nside_;
969       z = (2*nside_-jr)*fact1_;
970       }
971 
972     I tmp=I(jpll[face_num])*nr+ix-iy;
973     MR_assert(tmp<8*nr,"must not happen");
974     if (tmp<0) tmp+=8*nr;
975     phi = (nr==nside_) ? 0.75*halfpi*tmp*fact1_ :
976                          (0.5*halfpi*tmp)/nr;
977     }
978   }
979 
980 template<typename I> template<typename I2>
query_polygon_internal(const vector<pointing> & vertex,int fact,rangeset<I2> & pixset) const981   void T_Healpix_Base<I>::query_polygon_internal
982   (const vector<pointing> &vertex, int fact, rangeset<I2> &pixset) const
983   {
984   bool inclusive = (fact!=0);
985   size_t nv=vertex.size();
986   size_t ncirc = inclusive ? nv+1 : nv;
987   MR_assert(nv>=3,"not enough vertices in polygon");
988   vector<vec3> vv(nv);
989   for (size_t i=0; i<nv; ++i)
990     vv[i]=vertex[i].to_vec3();
991   vector<vec3> normal(ncirc);
992   int flip=0;
993   for (size_t i=0; i<nv; ++i)
994     {
995     normal[i]=crossprod(vv[i],vv[(i+1)%nv]).Norm();
996     double hnd=dotprod(normal[i],vv[(i+2)%nv]);
997     MR_assert(abs(hnd)>1e-10,"degenerate corner");
998     if (i==0)
999       flip = (hnd<0.) ? -1 : 1;
1000     else
1001       MR_assert(flip*hnd>0,"polygon is not convex");
1002     normal[i]*=flip;
1003     }
1004   vector<double> rad(ncirc,halfpi);
1005   if (inclusive)
1006     {
1007     double cosrad;
1008     find_enclosing_circle (vv, normal[nv], cosrad);
1009     rad[nv]=acos(cosrad);
1010     }
1011   query_multidisc(normal,rad,fact,pixset);
1012   }
1013 
query_polygon(const vector<pointing> & vertex,rangeset<I> & pixset) const1014 template<typename I> void T_Healpix_Base<I>::query_polygon
1015   (const vector<pointing> &vertex, rangeset<I> &pixset) const
1016   {
1017   query_polygon_internal(vertex, 0, pixset);
1018   }
1019 
query_polygon_inclusive(const vector<pointing> & vertex,rangeset<I> & pixset,int fact) const1020 template<typename I> void T_Healpix_Base<I>::query_polygon_inclusive
1021   (const vector<pointing> &vertex, rangeset<I> &pixset, int fact) const
1022   {
1023   MR_assert(fact>0,"fact must be a positive integer");
1024   if ((sizeof(I)<8) && (((I(1)<<order_max)/nside_)<fact))
1025     {
1026     T_Healpix_Base<int64_t> base2(nside_,scheme_,SET_NSIDE);
1027     base2.query_polygon_internal(vertex,fact,pixset);
1028     return;
1029     }
1030   query_polygon_internal(vertex, fact, pixset);
1031   }
1032 
query_strip_internal(double theta1,double theta2,bool inclusive,rangeset<I> & pixset) const1033 template<typename I> void T_Healpix_Base<I>::query_strip_internal
1034   (double theta1, double theta2, bool inclusive, rangeset<I> &pixset) const
1035   {
1036   if (scheme_==RING)
1037     {
1038     I ring1 = max(I(1),1+ring_above(cos(theta1))),
1039       ring2 = min(4*nside_-1,ring_above(cos(theta2)));
1040     if (inclusive)
1041       {
1042       ring1 = max(I(1),ring1-1);
1043       ring2 = min(4*nside_-1,ring2+1);
1044       }
1045 
1046     I sp1,rp1,sp2,rp2;
1047     bool dummy;
1048     get_ring_info_small(ring1,sp1,rp1,dummy);
1049     get_ring_info_small(ring2,sp2,rp2,dummy);
1050     I pix1 = sp1,
1051       pix2 = sp2+rp2;
1052     if (pix1<=pix2) pixset.append(pix1,pix2);
1053     }
1054   else
1055     MR_fail("query_strip not yet implemented for NESTED");
1056   }
1057 
query_strip(double theta1,double theta2,bool inclusive,rangeset<I> & pixset) const1058 template<typename I> void T_Healpix_Base<I>::query_strip (double theta1,
1059   double theta2, bool inclusive, rangeset<I> &pixset) const
1060   {
1061   pixset.clear();
1062 
1063   if (theta1<theta2)
1064     query_strip_internal(theta1,theta2,inclusive,pixset);
1065   else
1066     {
1067     query_strip_internal(0.,theta2,inclusive,pixset);
1068     rangeset<I> ps2;
1069     query_strip_internal(theta1,pi,inclusive,ps2);
1070     pixset.append(ps2);
1071     }
1072   }
1073 
get_ring_info_small(I ring,I & startpix,I & ringpix,bool & shifted) const1074 template<typename I> inline void T_Healpix_Base<I>::get_ring_info_small
1075   (I ring, I &startpix, I &ringpix, bool &shifted) const
1076   {
1077   if (ring < nside_)
1078     {
1079     shifted = true;
1080     ringpix = 4*ring;
1081     startpix = 2*ring*(ring-1);
1082     }
1083   else if (ring < 3*nside_)
1084     {
1085     shifted = ((ring-nside_) & 1) == 0;
1086     ringpix = 4*nside_;
1087     startpix = ncap_ + (ring-nside_)*ringpix;
1088     }
1089   else
1090     {
1091     shifted = true;
1092     I nr= 4*nside_-ring;
1093     ringpix = 4*nr;
1094     startpix = npix_-2*nr*(nr+1);
1095     }
1096   }
1097 
get_ring_info(I ring,I & startpix,I & ringpix,double & costheta,double & sintheta,bool & shifted) const1098 template<typename I> void T_Healpix_Base<I>::get_ring_info (I ring, I &startpix,
1099   I &ringpix, double &costheta, double &sintheta, bool &shifted) const
1100   {
1101   I northring = (ring>2*nside_) ? 4*nside_-ring : ring;
1102   if (northring < nside_)
1103     {
1104     double tmp = northring*northring*fact2_;
1105     costheta = 1 - tmp;
1106     sintheta = sqrt(tmp*(2-tmp));
1107     ringpix = 4*northring;
1108     shifted = true;
1109     startpix = 2*northring*(northring-1);
1110     }
1111   else
1112     {
1113     costheta = (2*nside_-northring)*fact1_;
1114     sintheta = sqrt((1+costheta)*(1-costheta));
1115     ringpix = 4*nside_;
1116     shifted = ((northring-nside_) & 1) == 0;
1117     startpix = ncap_ + (northring-nside_)*ringpix;
1118     }
1119   if (northring != ring) // southern hemisphere
1120     {
1121     costheta = -costheta;
1122     startpix = npix_ - startpix - ringpix;
1123     }
1124   }
get_ring_info2(I ring,I & startpix,I & ringpix,double & theta,bool & shifted) const1125 template<typename I> void T_Healpix_Base<I>::get_ring_info2 (I ring,
1126   I &startpix, I &ringpix, double &theta, bool &shifted) const
1127   {
1128   I northring = (ring>2*nside_) ? 4*nside_-ring : ring;
1129   if (northring < nside_)
1130     {
1131     double tmp = northring*northring*fact2_;
1132     double costheta = 1 - tmp;
1133     double sintheta = sqrt(tmp*(2-tmp));
1134     theta = atan2(sintheta,costheta);
1135     ringpix = 4*northring;
1136     shifted = true;
1137     startpix = 2*northring*(northring-1);
1138     }
1139   else
1140     {
1141     theta = acos((2*nside_-northring)*fact1_);
1142     ringpix = 4*nside_;
1143     shifted = ((northring-nside_) & 1) == 0;
1144     startpix = ncap_ + (northring-nside_)*ringpix;
1145     }
1146   if (northring != ring) // southern hemisphere
1147     {
1148     theta = pi-theta;
1149     startpix = npix_ - startpix - ringpix;
1150     }
1151   }
1152 
neighbors(I pix,array<I,8> & result) const1153 template<typename I> void T_Healpix_Base<I>::neighbors (I pix,
1154   array<I,8> &result) const
1155   {
1156   int ix, iy, face_num;
1157   (scheme_==RING) ?
1158     ring2xyf(pix,ix,iy,face_num) : nest2xyf(pix,ix,iy,face_num);
1159 
1160   const I nsm1 = nside_-1;
1161   if ((ix>0)&&(ix<nsm1)&&(iy>0)&&(iy<nsm1))
1162     {
1163     if (scheme_==RING)
1164       for (size_t m=0; m<8; ++m)
1165         result[m] = xyf2ring(ix+nb_xoffset[m],iy+nb_yoffset[m],face_num);
1166     else
1167       {
1168       I fpix = I(face_num)<<(2*order_),
1169         px0=spread<I>(ix  ), py0=spread<I>(iy  )<<1,
1170         pxp=spread<I>(ix+1), pyp=spread<I>(iy+1)<<1,
1171         pxm=spread<I>(ix-1), pym=spread<I>(iy-1)<<1;
1172 
1173       result[0] = fpix+pxm+py0; result[1] = fpix+pxm+pyp;
1174       result[2] = fpix+px0+pyp; result[3] = fpix+pxp+pyp;
1175       result[4] = fpix+pxp+py0; result[5] = fpix+pxp+pym;
1176       result[6] = fpix+px0+pym; result[7] = fpix+pxm+pym;
1177       }
1178     }
1179   else
1180     {
1181     for (size_t i=0; i<8; ++i)
1182       {
1183       int x=ix+nb_xoffset[i], y=iy+nb_yoffset[i];
1184       int nbnum=4;
1185       if (x<0)
1186         { x+=nside_; nbnum-=1; }
1187       else if (x>=nside_)
1188         { x-=nside_; nbnum+=1; }
1189       if (y<0)
1190         { y+=nside_; nbnum-=3; }
1191       else if (y>=nside_)
1192         { y-=nside_; nbnum+=3; }
1193 
1194       int f = nb_facearray[nbnum][face_num];
1195       if (f>=0)
1196         {
1197         int bits = nb_swaparray[nbnum][face_num>>2];
1198         if (bits&1) x=nside_-x-1;
1199         if (bits&2) y=nside_-y-1;
1200         if (bits&4) std::swap(x,y);
1201         result[i] = (scheme_==RING) ? xyf2ring(x,y,f) : xyf2nest(x,y,f);
1202         }
1203       else
1204         result[i] = -1;
1205       }
1206     }
1207   }
1208 
get_interpol(const pointing & ptg,array<I,4> & pix,array<double,4> & wgt) const1209 template<typename I> void T_Healpix_Base<I>::get_interpol (const pointing &ptg,
1210   array<I,4> &pix, array<double,4> &wgt) const
1211   {
1212   MR_assert((ptg.theta>=0)&&(ptg.theta<=pi),"invalid theta value");
1213   double z = cos (ptg.theta);
1214   I ir1 = ring_above(z);
1215   I ir2 = ir1+1;
1216   double theta1, theta2, w1, tmp, dphi;
1217   I sp,nr;
1218   bool shift;
1219   I i1,i2;
1220   if (ir1>0)
1221     {
1222     get_ring_info2 (ir1, sp, nr, theta1, shift);
1223     dphi = twopi/nr;
1224     tmp = (ptg.phi/dphi - .5*shift);
1225     i1 = (tmp<0) ? I(tmp)-1 : I(tmp);
1226     w1 = (ptg.phi-(i1+.5*shift)*dphi)/dphi;
1227     i2 = i1+1;
1228     if (i1<0) i1 +=nr;
1229     if (i2>=nr) i2 -=nr;
1230     pix[0] = sp+i1; pix[1] = sp+i2;
1231     wgt[0] = 1-w1; wgt[1] = w1;
1232     }
1233   if (ir2<(4*nside_))
1234     {
1235     get_ring_info2 (ir2, sp, nr, theta2, shift);
1236     dphi = twopi/nr;
1237     tmp = (ptg.phi/dphi - .5*shift);
1238     i1 = (tmp<0) ? I(tmp)-1 : I(tmp);
1239     w1 = (ptg.phi-(i1+.5*shift)*dphi)/dphi;
1240     i2 = i1+1;
1241     if (i1<0) i1 +=nr;
1242     if (i2>=nr) i2 -=nr;
1243     pix[2] = sp+i1; pix[3] = sp+i2;
1244     wgt[2] = 1-w1; wgt[3] = w1;
1245     }
1246 
1247   if (ir1==0)
1248     {
1249     double wtheta = ptg.theta/theta2;
1250     wgt[2] *= wtheta; wgt[3] *= wtheta;
1251     double fac = (1-wtheta)*0.25;
1252     wgt[0] = fac; wgt[1] = fac; wgt[2] += fac; wgt[3] +=fac;
1253     pix[0] = (pix[2]+2)&3;
1254     pix[1] = (pix[3]+2)&3;
1255     }
1256   else if (ir2==4*nside_)
1257     {
1258     double wtheta = (ptg.theta-theta1)/(pi-theta1);
1259     wgt[0] *= (1-wtheta); wgt[1] *= (1-wtheta);
1260     double fac = wtheta*0.25;
1261     wgt[0] += fac; wgt[1] += fac; wgt[2] = fac; wgt[3] =fac;
1262     pix[2] = ((pix[0]+2)&3)+npix_-4;
1263     pix[3] = ((pix[1]+2)&3)+npix_-4;
1264     }
1265   else
1266     {
1267     double wtheta = (ptg.theta-theta1)/(theta2-theta1);
1268     wgt[0] *= (1-wtheta); wgt[1] *= (1-wtheta);
1269     wgt[2] *= wtheta; wgt[3] *= wtheta;
1270     }
1271 
1272   if (scheme_==NEST)
1273     for (size_t m=0; m<pix.size(); ++m)
1274       pix[m] = ring2nest(pix[m]);
1275   }
1276 
swap(T_Healpix_Base & other)1277 template<typename I> void T_Healpix_Base<I>::swap (T_Healpix_Base &other)
1278   {
1279   std::swap(order_,other.order_);
1280   std::swap(nside_,other.nside_);
1281   std::swap(npface_,other.npface_);
1282   std::swap(ncap_,other.ncap_);
1283   std::swap(npix_,other.npix_);
1284   std::swap(fact1_,other.fact1_);
1285   std::swap(fact2_,other.fact2_);
1286   std::swap(scheme_,other.scheme_);
1287   }
1288 
max_pixrad() const1289 template<typename I> double T_Healpix_Base<I>::max_pixrad() const
1290   {
1291   vec3 va,vb;
1292   va.set_z_phi (2./3., pi/(4*nside_));
1293   double t1 = 1.-1./nside_;
1294   t1*=t1;
1295   vb.set_z_phi (1-t1/3, 0);
1296   return v_angle(va,vb);
1297   }
1298 
max_pixrad(I ring) const1299 template<typename I> double T_Healpix_Base<I>::max_pixrad(I ring) const
1300   {
1301   if (ring>=2*nside_) ring=4*nside_-ring;
1302   double z=ring2z(ring), z_up=ring2z(ring-1);
1303   vec3 mypos, uppos;
1304   uppos.set_z_phi(z_up,0);
1305   if (ring<=nside_)
1306     {
1307     mypos.set_z_phi(z,pi/(4*ring));
1308     double v1=v_angle(mypos,uppos);
1309     if (ring!=1) return v1;
1310     uppos.set_z_phi(ring2z(ring+1),pi/(4*(min(nside_,ring+1))));
1311     return max(v1,v_angle(mypos,uppos));
1312     }
1313   mypos.set_z_phi(z,0);
1314   double vdist=v_angle(mypos,uppos);
1315   double hdist=sqrt(1.-z*z)*pi/(4*nside_);
1316   return max(hdist,vdist);
1317   }
1318 
xyf2loc(double x,double y,int face,double & z,double & phi,double & sth,bool & have_sth) const1319 template<typename I> void T_Healpix_Base<I>::xyf2loc (double x, double y,
1320   int face, double &z, double &phi, double &sth, bool &have_sth) const
1321   {
1322   have_sth = false;
1323   double jr = jrll[face] - x - y;
1324   double nr;
1325   if (jr<1)
1326     {
1327     nr = jr;
1328     double tmp = nr*nr/3.;
1329     z = 1 - tmp;
1330     if (z > 0.99)
1331       {
1332       sth = std::sqrt(tmp*(2.0-tmp));
1333       have_sth = true;
1334       }
1335     }
1336   else if (jr>3)
1337     {
1338     nr = 4-jr;
1339     double tmp = nr*nr/3.;
1340     z = tmp - 1;
1341     if (z<-0.99)
1342       {
1343       sth = std::sqrt(tmp*(2.-tmp));
1344       have_sth = true;
1345       }
1346     }
1347   else
1348     {
1349     nr = 1;
1350     z = (2-jr)*2./3.;
1351     }
1352 
1353   double tmp=jpll[face]*nr+x-y;
1354   if (tmp<0) tmp+=8;
1355   if (tmp>=8) tmp-=8;
1356   phi = (nr<1e-15) ? 0 : (0.5*halfpi*tmp)/nr;
1357   }
1358 
1359 namespace {
1360 
locToVec3(double z,double phi,double sth,bool have_sth)1361 vec3 locToVec3 (double z, double phi, double sth, bool have_sth)
1362   {
1363   if (have_sth)
1364     return vec3(sth*cos(phi),sth*sin(phi),z);
1365   else
1366     {
1367     vec3 res;
1368     res.set_z_phi (z, phi);
1369     return res;
1370     }
1371   }
1372 
1373 } // unnamed namespace
1374 
boundaries(I pix,size_t step,vector<vec3> & out) const1375 template<typename I> void T_Healpix_Base<I>::boundaries(I pix, size_t step,
1376   vector<vec3> &out) const
1377   {
1378   out.resize(4*step);
1379   int ix, iy, face;
1380   pix2xyf(pix, ix, iy, face);
1381   double dc = 0.5 / nside_;
1382   double xc = (ix + 0.5)/nside_, yc = (iy + 0.5)/nside_;
1383   double d = 1.0/(step*nside_);
1384   for (size_t i=0; i<step; ++i)
1385     {
1386     double z, phi, sth;
1387     bool have_sth;
1388     xyf2loc(xc+dc-i*d, yc+dc, face, z, phi, sth, have_sth);
1389     out[i] = locToVec3(z, phi, sth, have_sth);
1390     xyf2loc(xc-dc, yc+dc-i*d, face, z, phi, sth, have_sth);
1391     out[i+step] = locToVec3(z, phi, sth, have_sth);
1392     xyf2loc(xc-dc+i*d, yc-dc, face, z, phi, sth, have_sth);
1393     out[i+2*step] = locToVec3(z, phi, sth, have_sth);
1394     xyf2loc(xc+dc, yc-dc+i*d, face, z, phi, sth, have_sth);
1395     out[i+3*step] = locToVec3(z, phi, sth, have_sth);
1396     }
1397   }
1398 
swap_cycles() const1399 template<typename I> vector<int> T_Healpix_Base<I>::swap_cycles() const
1400   {
1401   MR_assert(order_>=0, "need hierarchical map");
1402   MR_assert(order_<=13, "map too large");
1403   vector<int> result(swap_clen[order_]);
1404   size_t ofs=0;
1405   for (int m=0; m<order_;++m) ofs+=swap_clen[m];
1406   for (size_t m=0; m<result.size();++m) result[m]=swap_cycle[m+ofs];
1407   return result;
1408   }
1409 
1410 template class T_Healpix_Base<int>;
1411 template class T_Healpix_Base<int64_t>;
1412 
1413 }}
1414