source: Sophya/trunk/SophyaLib/SkyMap/spherehealpix.cc@ 2960

Last change on this file since 2960 was 2960, checked in by ansari, 19 years ago

petites corrections + doc(xygen) / Protection Resize() ds SphereHEALPix et SphereThetaPhi - Reza 1/06/2006

File size: 17.7 KB
Line 
1#include "sopnamsp.h"
2#include "machdefs.h"
3#include <math.h>
4#include <complex>
5
6#include "pexceptions.h"
7#include "fiondblock.h"
8#include "spherehealpix.h"
9#include "strutil.h"
10
11extern "C"
12{
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16}
17
18using namespace SOPHYA;
19
20/*!
21 \class SOPHYA::SphereHEALPix
22 \ingroup SkyMap
23 \brief Spherical maps in HEALPix pixelisation scheme.
24
25 Class implementing spherical maps, in the HEALPix pixelisation scheme,
26 with template data types (double, float, complex, ...)
27
28
29\verbatim
30 Adapted from :
31 -----------------------------------------------------------------------
32 version 0.8.2 Aug97 TAC Eric Hivon, Kris Gorski
33 -----------------------------------------------------------------------
34
35 the sphere is split in 12 diamond-faces containing nside**2 pixels each
36 the numbering of the pixels (in the nested scheme) is similar to
37 quad-cube
38 In each face the first pixel is in the lowest corner of the diamond
39 the faces are (x,y) coordinate on each face
40
41 . . . . <--- North Pole
42 / \ / \ / \ / \ ^ ^
43 . 0 . 1 . 2 . 3 . <--- z = 2/3 \ /
44 \ / \ / \ / \ / y \ / x
45 4 . 5 . 6 . 7 . 4 <--- equator \ /
46 / \ / \ / \ / \ \/
47 . 8 . 9 .10 .11 . <--- z = -2/3 (0,0) : lowest corner
48 \ / \ / \ / \ /
49 . . . . <--- South Pole
50\endverbatim
51 phi:0 2Pi
52
53 in the ring scheme pixels are numbered along the parallels
54 the first parallel is the one closest to the north pole and so on
55 on each parallel, pixels are numbered starting from the one closest
56 to phi = 0
57
58 nside MUST be a power of 2 (<= 8192)
59
60*/
61
62/* --Methode-- */
63
64template<class T>
65SphereHEALPix<T>::SphereHEALPix() : pixels_(), sliceBeginIndex_(),
66 sliceLenght_()
67
68
69{
70 InitNul();
71}
72
73/*! \fn SOPHYA::SphereHEALPix::SphereHEALPix(int_4 m)
74
75 \param <m> : "nside" of the Healpix algorithm
76
77 The total number of pixels will be Npix = 12*nside**2
78
79 nside MUST be a power of 2 (<= 8192)
80*/
81
82template<class T>
83SphereHEALPix<T>::SphereHEALPix(int_4 m)
84
85{
86 InitNul();
87 Pixelize(m);
88 SetThetaSlices();
89}
90//! Copy constructor
91template<class T>
92SphereHEALPix<T>::SphereHEALPix(const SphereHEALPix<T>& s, bool share)
93 : pixels_(s.pixels_, share), sliceBeginIndex_(s.sliceBeginIndex_, share),
94 sliceLenght_(s.sliceLenght_, share)
95//--
96{
97 nSide_= s.nSide_;
98 nPix_ = s.nPix_;
99 omeg_ = s.omeg_;
100 if(s.mInfo_) this->mInfo_= new DVList(*s.mInfo_);
101}
102//++
103template<class T>
104SphereHEALPix<T>::SphereHEALPix(const SphereHEALPix<T>& s)
105 : pixels_(s.pixels_), sliceBeginIndex_(s.sliceBeginIndex_),
106 sliceLenght_(s.sliceLenght_)
107// copy constructor
108//--
109{
110 nSide_= s.nSide_;
111 nPix_ = s.nPix_;
112 omeg_ = s.omeg_;
113 if(s.mInfo_) this->mInfo_= new DVList(*s.mInfo_);
114 // CloneOrShare(s);
115}
116
117//! Clone if \b a is not temporary, share if temporary
118/*! \sa NDataBlock::CloneOrShare(const NDataBlock<T>&) */
119template<class T>
120void SphereHEALPix<T>::CloneOrShare(const SphereHEALPix<T>& a)
121{
122 nSide_= a.nSide_;
123 nPix_ = a.nPix_;
124 omeg_ = a.omeg_;
125 pixels_.CloneOrShare(a.pixels_);
126 sliceBeginIndex_.CloneOrShare(a.sliceBeginIndex_);
127 sliceLenght_.CloneOrShare(a.sliceLenght_);
128 if (this->mInfo_) {delete this->mInfo_; this->mInfo_ = NULL;}
129 if (a.mInfo_) this->mInfo_ = new DVList(*(a.mInfo_));
130}
131
132//! Share data with a
133template<class T>
134void SphereHEALPix<T>::Share(const SphereHEALPix<T>& a)
135{
136 nSide_= a.nSide_;
137 nPix_ = a.nPix_;
138 omeg_ = a.omeg_;
139 pixels_.Share(a.pixels_);
140 sliceBeginIndex_.Share(a.sliceBeginIndex_);
141 sliceLenght_.Share(a.sliceLenght_);
142 if (this->mInfo_) {delete this->mInfo_; this->mInfo_ = NULL;}
143 if (a.mInfo_) this->mInfo_ = new DVList(*(a.mInfo_));
144}
145
146////////////////////////// methodes de copie/share
147template<class T>
148SphereHEALPix<T>& SphereHEALPix<T>::Set(const SphereHEALPix<T>& a)
149{
150 if (this != &a)
151 {
152
153 if (a.NbPixels() < 1)
154 throw RangeCheckError("SphereHEALPix<T>::Set(a ) - SphereHEALPix a not allocated ! ");
155 if (NbPixels() < 1) CloneOrShare(a);
156 else CopyElt(a);
157
158
159 if (this->mInfo_) delete this->mInfo_;
160 this->mInfo_ = NULL;
161 if (a.mInfo_) this->mInfo_ = new DVList(*(a.mInfo_));
162 }
163 return(*this);
164}
165
166template<class T>
167SphereHEALPix<T>& SphereHEALPix<T>::CopyElt(const SphereHEALPix<T>& a)
168{
169 if (NbPixels() < 1)
170 throw RangeCheckError("SphereHEALPix<T>::CopyElt(const SphereHEALPix<T>& ) - Not Allocated SphereHEALPix ! ");
171 if (NbPixels() != a.NbPixels())
172 throw(SzMismatchError("SphereHEALPix<T>::CopyElt(const SphereHEALPix<T>&) SizeMismatch")) ;
173 nSide_= a.nSide_;
174 nPix_ = a.nPix_;
175 omeg_ = a.omeg_;
176 int k;
177 for (k=0; k< nPix_; k++) pixels_(k) = a.pixels_(k);
178 for (k=0; k< a.sliceBeginIndex_.Size(); k++) sliceBeginIndex_(k) = a.sliceBeginIndex_(k);
179 for (k=0; k< a.sliceLenght_.Size(); k++) sliceLenght_(k) = a.sliceLenght_(k);
180 return(*this);
181}
182
183
184
185//++
186// Titre Destructor
187//--
188//++
189template<class T>
190SphereHEALPix<T>::~SphereHEALPix()
191
192//--
193{
194}
195
196//++
197// Titre Public Methods
198//--
199
200/*! \fn void SOPHYA::SphereHEALPix::Resize(int_4 m)
201 \param <m> "nside" of the HEALPix algorithm
202
203 The total number of pixels will be Npix = 12*nside**2
204
205 nside MUST be a power of 2 (<= 8192)
206*/
207template<class T>
208void SphereHEALPix<T>::Resize(int_4 m)
209
210
211{
212 if ((m <= 0 && nSide_ > 0)) {
213 cout << "SphereHEALPix<T>::Resize(m) with m<=0, NOT resized" << endl;
214 return;
215 }
216 InitNul();
217 Pixelize(m);
218 SetThetaSlices();
219}
220
221template<class T>
222void SphereHEALPix<T>::Pixelize( int_4 m)
223
224// prépare la pixelisation Gorski (m a la même signification
225// que pour le constructeur)
226//
227//
228{
229 if (m<=0 || m> 8192) {
230 cout << "SphereHEALPix<T>::Pixelize() m=" << m <<" out of range [0,8192]" << endl;
231 throw ParmError("SphereHEALPix<T>::Pixelize() m out of range");
232 }
233 // verifier que m est une puissance de deux
234 int x= m;
235 while (x%2==0) x/=2;
236 if(x != 1) {
237 cout << "SphereHEALPix<T>::Pixelize() m=" << m << " != 2^n " << endl;
238 throw ParmError("SphereHEALPix<T>::Pixelize() m!=2^n");
239 }
240
241 // On memorise les arguments d'appel
242 nSide_= m;
243
244 // Nombre total de pixels sur la sphere entiere
245 nPix_= 12*nSide_*nSide_;
246
247 // pour le moment les tableaux qui suivent seront ranges dans l'ordre
248 // de l'indexation GORSKY "RING"
249 // on pourra ulterieurement changer de strategie et tirer profit
250 // de la dualite d'indexation GORSKY (RING et NEST) : tout dependra
251 // de pourquoi c'est faire
252
253 // Creation et initialisation du vecteur des contenus des pixels
254 pixels_.ReSize(nPix_);
255 pixels_.Reset();
256
257 // solid angle per pixel
258 omeg_= 4.0*Pi/nPix_;
259}
260
261template<class T>
262void SphereHEALPix<T>::InitNul()
263//
264// initialise à zéro les variables de classe
265{
266 nSide_= 0;
267 nPix_ = 0;
268 omeg_ = 0.;
269// pixels_.Reset(); - Il ne faut pas mettre les pixels a zero si share !
270}
271
272/* --Methode-- */
273/* Nombre de pixels du decoupage */
274/*! \fn int_4 SOPHYA::SphereHEALPix::NbPixels() const
275
276 Return number of pixels of the splitting
277*/
278template<class T>
279int_4 SphereHEALPix<T>::NbPixels() const
280{
281 return(nPix_);
282}
283
284
285/*! \fn uint_4 SOPHYA::SphereHEALPix::NbThetaSlices() const
286
287 \return number of slices in theta direction on the sphere
288*/
289template<class T>
290uint_4 SphereHEALPix<T>::NbThetaSlices() const
291{
292 uint_4 nbslices = uint_4(4*nSide_-1);
293 if (nSide_<=0)
294 {
295 nbslices = 0;
296 throw PException(" sphere not pixelized, NbSlice=0 ");
297 }
298 return nbslices;
299}
300
301/*! \fn void SOPHYA::SphereHEALPix::GetThetaSlice(int_4 index,r_8& theta,TVector<r_8>& phi,TVector<T>& value) const
302
303 For a theta-slice with index 'index', return :
304
305 the corresponding "theta"
306
307 a vector containing the phi's of the pixels of the slice
308
309 a vector containing the corresponding values of pixels
310*/
311template<class T>
312void SphereHEALPix<T>::GetThetaSlice(int_4 index,r_8& theta,TVector<r_8>& phi,TVector<T>& value) const
313
314{
315
316 if (index<0 || index >= NbThetaSlices())
317 {
318 cout << " SphereHEALPix::GetThetaSlice : Pixel index out of range" <<endl;
319 throw RangeCheckError(" SphereHEALPix::GetThetaSlice : Pixel index out of range");
320 }
321
322
323 int_4 iring= sliceBeginIndex_(index);
324 int_4 lring = sliceLenght_(index);
325
326 phi.ReSize(lring);
327 value.ReSize(lring);
328
329 double TH= 0.;
330 double FI= 0.;
331 for(int_4 kk = 0; kk < lring;kk++)
332 {
333 PixThetaPhi(kk+iring,TH,FI);
334 phi(kk)= FI;
335 value(kk)= PixVal(kk+iring);
336 }
337 theta= TH;
338}
339/*! \fn void SOPHYA::SphereHEALPix::GetThetaSlice(int_4 sliceIndex,r_8& theta, r_8& phi0, TVector<int_4>& pixelIndices,TVector<T>& value) const
340
341 For a theta-slice with index 'index', return :
342
343 the corresponding "theta"
344
345 the corresponding "phi" for first pixel of the slice
346
347 a vector containing indices of the pixels of the slice
348
349 (equally distributed in phi)
350
351 a vector containing the corresponding values of pixels
352*/
353
354template<class T>
355void SphereHEALPix<T>::GetThetaSlice(int_4 sliceIndex,r_8& theta, r_8& phi0, TVector<int_4>& pixelIndices,TVector<T>& value) const
356
357{
358
359 if (sliceIndex<0 || sliceIndex >= NbThetaSlices())
360 {
361 cout << " SphereHEALPix::GetThetaSlice : Pixel index out of range" <<endl;
362 throw RangeCheckError(" SphereHEALPix::GetThetaSlice : Pixel index out of range");
363 }
364 int_4 iring= sliceBeginIndex_(sliceIndex);
365 int_4 lring = sliceLenght_(sliceIndex);
366 pixelIndices.ReSize(lring);
367 value.ReSize(lring);
368
369 for(int_4 kk = 0; kk < lring;kk++)
370 {
371 pixelIndices(kk)= kk+iring;
372 value(kk)= PixVal(kk+iring);
373 }
374 PixThetaPhi(iring, theta, phi0);
375}
376
377template<class T>
378void SphereHEALPix<T>::SetThetaSlices()
379{
380 sliceBeginIndex_.ReSize(4*nSide_-1);
381 sliceLenght_.ReSize(4*nSide_-1);
382 int sliceIndex;
383 for (sliceIndex=0; sliceIndex< nSide_-1; sliceIndex++)
384 {
385 sliceBeginIndex_(sliceIndex) = 2*sliceIndex*(sliceIndex+1);
386 sliceLenght_(sliceIndex) = 4*(sliceIndex+1);
387 }
388 for (sliceIndex= nSide_-1; sliceIndex< 3*nSide_; sliceIndex++)
389 {
390 sliceBeginIndex_(sliceIndex) = 2*nSide_*(2*sliceIndex-nSide_+1);
391 sliceLenght_(sliceIndex) = 4*nSide_;
392 }
393 for (sliceIndex= 3*nSide_; sliceIndex< 4*nSide_-1; sliceIndex++)
394 {
395 int_4 nc= 4*nSide_-1-sliceIndex;
396 sliceBeginIndex_(sliceIndex) = nPix_-2*nc*(nc+1);
397 sliceLenght_(sliceIndex) = 4*nc;
398 }
399}
400
401
402/*! \fn T& SOPHYA::SphereHEALPix::PixVal(int_4 k)
403
404 \return value of pixel with "RING" index k
405*/
406template<class T>
407T& SphereHEALPix<T>::PixVal(int_4 k)
408
409{
410 if((k < 0) || (k >= nPix_))
411 {
412 throw RangeCheckError("SphereHEALPix::PIxVal Pixel index out of range");
413 }
414 return pixels_(k);
415}
416
417/*! \fn T const& SOPHYA::SphereHEALPix::PixVal(int_4 k) const
418
419 \return value of pixel with "RING" index k
420*/
421template<class T>
422T const& SphereHEALPix<T>::PixVal(int_4 k) const
423
424{
425 if((k < 0) || (k >= nPix_))
426 {
427 throw RangeCheckError("SphereHEALPix::PIxVal Pixel index out of range");
428 }
429 return *(pixels_.Data()+k);
430}
431
432/*! \fn T& SOPHYA::SphereHEALPix::PixValNest(int_4 k)
433
434 \return value of pixel with "NESTED" index k
435*/
436template<class T>
437T& SphereHEALPix<T>::PixValNest(int_4 k)
438
439//--
440{
441 if((k < 0) || (k >= nPix_))
442 {
443 throw RangeCheckError("SphereHEALPix::PIxValNest Pixel index out of range");
444 }
445 return pixels_(nest2ring(nSide_,k));
446}
447
448/*! \fn T const& SOPHYA::SphereHEALPix::PixValNest(int_4 k) const
449
450 \return value of pixel with "NESTED" index k
451*/
452
453template<class T>
454T const& SphereHEALPix<T>::PixValNest(int_4 k) const
455
456{
457 if((k < 0) || (k >= nPix_))
458 {
459 throw RangeCheckError("SphereHEALPix::PIxValNest Pixel index out of range");
460 }
461 int_4 pix= nest2ring(nSide_,k);
462 return *(pixels_.Data()+pix);
463}
464
465/*! \fn bool SOPHYA::SphereHEALPix::ContainsSph(double theta, double phi) const
466
467\return true if teta,phi in map
468*/
469template<class T>
470bool SphereHEALPix<T>::ContainsSph(double theta, double phi) const
471{
472return(true);
473}
474
475/*! \fn int_4 SOPHYA::SphereHEALPix::PixIndexSph(double theta,double phi) const
476
477 \return "RING" index of the pixel corresponding to direction (theta, phi).
478 */
479template<class T>
480int_4 SphereHEALPix<T>::PixIndexSph(double theta,double phi) const
481
482{
483 return ang2pix_ring(nSide_,theta,phi);
484}
485
486/*! \fn int_4 SOPHYA::SphereHEALPix::PixIndexSphNest(double theta,double phi) const
487
488 \return "NESTED" index of the pixel corresponding to direction (theta, phi).
489 */
490template<class T>
491int_4 SphereHEALPix<T>::PixIndexSphNest(double theta,double phi) const
492
493{
494 return ang2pix_nest(nSide_,theta,phi);
495}
496
497
498/* --Methode-- */
499/*! \fn void SOPHYA::SphereHEALPix::PixThetaPhi(int_4 k,double& theta,double& phi) const
500 \return (theta,phi) coordinates of middle of pixel with "RING" index k
501 */
502template<class T>
503void SphereHEALPix<T>::PixThetaPhi(int_4 k,double& theta,double& phi) const
504{
505 pix2ang_ring(nSide_,k,theta,phi);
506}
507
508/*! \fn T SOPHYA::SphereHEALPix::SetPixels(T v)
509
510Set all pixels to value v
511*/
512template <class T>
513T SphereHEALPix<T>::SetPixels(T v)
514{
515pixels_.Reset(v);
516return(v);
517}
518
519
520
521/*! \fn void SOPHYA::SphereHEALPix::PixThetaPhiNest(int_4 k,double& theta,double& phi) const
522
523 \return (theta,phi) coordinates of middle of pixel with "NESTED" index k
524 */
525template<class T>
526void SphereHEALPix<T>::PixThetaPhiNest(int_4 k,double& theta,double& phi) const
527
528{
529 pix2ang_nest(nSide_,k,theta,phi);
530}
531
532
533/*! \fn int_4 SOPHYA::SphereHEALPix::NestToRing(int_4 k) const
534
535 translation from NESTED index into RING index
536*/
537template<class T>
538int_4 SphereHEALPix<T>::NestToRing(int_4 k) const
539
540{
541 return nest2ring(nSide_,k);
542}
543
544
545/*! \fn int_4 SOPHYA::SphereHEALPix::RingToNest(int_4 k) const
546
547 translation from RING index into NESTED index
548*/
549template<class T>
550int_4 SphereHEALPix<T>::RingToNest(int_4 k) const
551{
552 return ring2nest(nSide_,k);
553}
554
555// ...... Operations de calcul ......
556
557//! Fill a SphereHEALPix with a constant value \b a
558template <class T>
559SphereHEALPix<T>& SphereHEALPix<T>::SetT(T a)
560{
561 if (NbPixels() < 1)
562 throw RangeCheckError("SphereHEALPix<T>::SetT(T ) - SphereHEALPix not dimensionned ! ");
563 pixels_ = a;
564 return (*this);
565}
566
567/*! Add a constant value \b x to a SphereHEALPix */
568template <class T>
569SphereHEALPix<T>& SphereHEALPix<T>::Add(T a)
570 {
571 cout << " c'est mon Add " << endl;
572 if (NbPixels() < 1)
573 throw RangeCheckError("SphereHEALPix<T>::Add(T ) - SphereHEALPix not dimensionned ! ");
574 // pixels_ += a;
575 pixels_.Add(a);
576 return (*this);
577}
578
579/*! Substract a constant value \b a to a SphereHEALPix */
580template <class T>
581SphereHEALPix<T>& SphereHEALPix<T>::Sub(T a,bool fginv)
582{
583 if (NbPixels() < 1)
584 throw RangeCheckError("SphereHEALPix<T>::Sub(T ) - SphereHEALPix not dimensionned ! ");
585 pixels_.Sub(a,fginv);
586 return (*this);
587}
588
589/*! multiply a SphereHEALPix by a constant value \b a */
590template <class T>
591SphereHEALPix<T>& SphereHEALPix<T>::Mul(T a)
592{
593 if (NbPixels() < 1)
594 throw RangeCheckError("SphereHEALPix<T>::Mul(T ) - SphereHEALPix not dimensionned ! ");
595 pixels_ *= a;
596 return (*this);
597}
598
599/*! divide a SphereHEALPix by a constant value \b a */
600template <class T>
601SphereHEALPix<T>& SphereHEALPix<T>::Div(T a)
602{
603 if (NbPixels() < 1)
604 throw RangeCheckError("SphereHEALPix<T>::Div(T ) - SphereHEALPix not dimensionned ! ");
605 pixels_ /= a;
606 return (*this);
607}
608
609// >>>> Operations avec 2nd membre de type SphereHEALPix
610//! Add two SphereHEALPix
611
612template <class T>
613SphereHEALPix<T>& SphereHEALPix<T>::AddElt(const SphereHEALPix<T>& a)
614{
615 if (NbPixels() != a.NbPixels() )
616 {
617 throw(SzMismatchError("SphereHEALPix<T>::AddElt(const SphereHEALPix<T>&) SizeMismatch")) ;
618 }
619 pixels_ += a.pixels_;
620 return (*this);
621}
622
623//! Substract two SphereHEALPix
624template <class T>
625SphereHEALPix<T>& SphereHEALPix<T>::SubElt(const SphereHEALPix<T>& a)
626{
627 if (NbPixels() != a.NbPixels() )
628 {
629 throw(SzMismatchError("SphereHEALPix<T>::SubElt(const SphereHEALPix<T>&) SizeMismatch")) ;
630 }
631 pixels_ -= a.pixels_;
632 return (*this);
633}
634
635//! Multiply two SphereHEALPix (elements by elements)
636template <class T>
637SphereHEALPix<T>& SphereHEALPix<T>::MulElt(const SphereHEALPix<T>& a)
638{
639 if (NbPixels() != a.NbPixels() )
640 {
641 throw(SzMismatchError("SphereHEALPix<T>::MulElt(const SphereHEALPix<T>&) SizeMismatch")) ;
642 }
643 pixels_ *= a.pixels_;
644 return (*this);
645}
646
647//! Divide two SphereHEALPix (elements by elements) - No protection for divide by 0
648template <class T>
649SphereHEALPix<T>& SphereHEALPix<T>::DivElt(const SphereHEALPix<T>& a)
650{
651 if (NbPixels() != a.NbPixels() )
652 {
653 throw(SzMismatchError("SphereHEALPix<T>::DivElt(const SphereHEALPix<T>&) SizeMismatch")) ;
654 }
655 pixels_ /= a.pixels_;
656 return (*this);
657}
658
659
660
661
662template <class T>
663void SphereHEALPix<T>::print(ostream& os) const
664{
665 if(this->mInfo_) os << " DVList Info= " << *(this->mInfo_) << endl;
666 //
667 os << " nSide_ = " << nSide_ << endl;
668 os << " nPix_ = " << nPix_ << endl;
669 os << " omeg_ = " << omeg_ << endl;
670
671 os << " content of pixels : ";
672 for(int i=0; i < nPix_; i++)
673 {
674 if(i%5 == 0) os << endl;
675 os << pixels_(i) <<", ";
676 }
677 os << endl;
678
679
680}
681
682
683
684//*******************************************************************
685
686#ifdef __CXX_PRAGMA_TEMPLATES__
687#pragma define_template SphereHEALPix<uint_2>
688#pragma define_template SphereHEALPix<int_4>
689#pragma define_template SphereHEALPix<r_8>
690#pragma define_template SphereHEALPix<r_4>
691#pragma define_template SphereHEALPix< complex<r_4> >
692#pragma define_template SphereHEALPix< complex<r_8> >
693#endif
694#if defined(ANSI_TEMPLATES) || defined(GNU_TEMPLATES)
695namespace SOPHYA {
696template class SphereHEALPix<uint_2>;
697template class SphereHEALPix<int_4>;
698template class SphereHEALPix<r_8>;
699template class SphereHEALPix<r_4>;
700template class SphereHEALPix< complex<r_4> >;
701template class SphereHEALPix< complex<r_8> >;
702}
703#endif
704
Note: See TracBrowser for help on using the repository browser.