ROOT logo
/*************************************************************************
 * Copyright(c) 1998, University of Connecticut, All rights reserved.    *
 * Author: Richard T. Jones, Assoc. Prof. of Physics                     *
 *                                                                       *
 * Permission to use, copy, modify and distribute this software and its  *
 * documentation for non-commercial purposes is hereby granted without   *
 * fee, provided that the above copyright notice appears in all copies   *
 * and that both the copyright notice and this permission notice appear  *
 * in the supporting documentation. The author makes no claims about the *
 * suitability of this software for any purpose.                         *
 * It is provided "as is" without express or implied warranty.           *
 *************************************************************************/
//////////////////////////////////////////////////////////////////////////
//									//
// Map2D Class								//
//									//
// The Map2D class provides a few useful methods for manipulating and	//
// comparing topographical maps within the root framework.  Map2D is	//
// derived from the basic two dimensional histogram TH2D class in root	//
// and inherits all of the storage and visualization capabilities of	//
// the root framework.  The main thing that is missing in TH2D is any	//
// concept of empty or invalid regions within the plot.  Many standard	//
// image formats incorporate "matte" information (or alpha channel) to  //
// indicate regions within the image frame that are not actually part	//
// of the image.  This is a basic feature of maps, that the coverage	//
// region is complex, and cannot be reduced to a rectangle.  The Map2D	//
// class introduces an alpha channel by defining the value (double)0.0	//
// as the matte value, or undefined.  Operations on that value should	//
// leave it undefined, for example: 0 + 3.8 = 0.  Interpolation on a	//
// map in a region next to a matte pixel must be designed so as not to	//
// treat it as z=0, and preserve the sharpness of the boundary between	//
// matte and ordinary pixels.						//
//									//
// The class currently supports the following operations on maps:	//
//	* addition/subtraction of 2 maps,				//
//	* shifting a map in all 3 dimensions,				//
//	* rescaling a map in all 3 dimensions,				//
//	* rotating a map in all 3 dimensions,				//
//      * computing correlations between 2 maps,			//
//      * determining the optimum alignment between 2 similar maps,	//
// all of which respect the meaning of matte pixels.			//
//									//
// This package was developed at the University of Connecticut by	//
// Richard T. Jones                            				//
//									//
//////////////////////////////////////////////////////////////////////////

#define REPORT_PROCESS_TIME 0

#include <iostream>
#include <TROOT.h>
#include <TMath.h>
#include <TCanvas.h>
#include "Map2D.h"
#include <list>

#include <TMatrixD.h>
#include <TDecompSVD.h>

#include <sys/time.h>
#include <sys/resource.h>

ClassImp(Map2D);

#ifndef SQR_FUNC
#define SQR_FUNC 1
Double_t sqr(Double_t x) { return x*x; }
#endif

const Double_t Map2D::zunit2xy = 0.001;

#define EMBED_BREAKPOINT  asm volatile ("int3;")

Map2D::Map2D() : TH2D()
{
   SetStats(0);
}

Map2D::Map2D(const Map2D &m) : TH2D((TH2D&)m)
{
   SetStats(0);
}

Map2D::Map2D(const TH2D &h) : TH2D(h)
{
   SetStats(0);
}

Map2D::~Map2D()
{ }

Map2D &Map2D::Add(const Map2D *map, Double_t c)
{
   // Overloads TH2D::Add, has the same basic behaviour,
   // except that zero plus anything is zero.

   Int_t nxbins = map->GetXaxis()->GetNbins();
   Int_t nybins = map->GetYaxis()->GetNbins();
   if (nxbins != GetXaxis()->GetNbins() ||
       nybins != GetYaxis()->GetNbins()) {
      std::cerr << "Error in Map2D::Add - source and destination histograms"
                << " have different dimensions, cannot add them"
                << std::endl;
      return *this;
   }
   for (Int_t i=1; i <= nxbins; ++i) {
      for (Int_t j=1; j <= nybins; ++j) {
         Double_t z1 = map->GetBinContent(i,j);
         Double_t z0 = GetBinContent(i,j);
         SetBinContent(i,j,z0+c*z1);
      }
   }
   return *this;
}

Map2D &Map2D::Add(const Map2D *map1, const Map2D *map2,
                          Double_t c1, Double_t c2)
{
   // Overloads TH2D::Add, has the same basic behaviour,
   // except that zero plus anything is zero.

   Int_t nxbins = map1->GetXaxis()->GetNbins();
   Int_t nybins = map1->GetYaxis()->GetNbins();
   if (nxbins != map2->GetXaxis()->GetNbins() ||
       nybins != map2->GetYaxis()->GetNbins()) {
      std::cerr << "Error in Map2D::Add - two source histograms"
                << " have different dimensions, cannot add them"
                << std::endl;
      return *this;
   }
   else if (nxbins != GetXaxis()->GetNbins() ||
            nybins != GetYaxis()->GetNbins()) {
      Double_t xmin = map1->GetXaxis()->GetXmin();
      Double_t xmax = map1->GetXaxis()->GetXmax();
      Double_t ymin = map1->GetYaxis()->GetXmin();
      Double_t ymax = map1->GetYaxis()->GetXmax();
      SetBins(nxbins,xmin,xmax,nybins,ymin,ymax);
   }
   for (Int_t i=1; i <= nxbins; ++i) {
      for (Int_t j=1; j <= nybins; ++j) {
         Double_t z1 = map1->GetBinContent(i,j);
         Double_t z2 = map2->GetBinContent(i,j);
         SetBinContent(i,j,c1*z1+c2*z2);
      }
   }
   return *this;
}

Map2D &Map2D::Shift(Double_t dx, Double_t dy, Double_t dz)
{
   // Translates the map image by a distance (dx,dy) in map coordinates,
   // and shifts all pixels up or down by vertical distance dz in
   // height units.  Image regions shifted outside the range of the
   // map are lost.  Image regions shift into the range of the map
   // from outside are zero.  Original contents are overwritten.

   Map2D *tmp = shift(dx,dy,dz);
   *this = *tmp;
   delete tmp;
   return *this;
}

Map2D *Map2D::shift(Double_t dx, Double_t dy, Double_t dz) const
{
   // Same as Shift(), except a new object is created to hold
   // the results and returned, and the original object is not
   // touched.  The caller must delete the returned object.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dbinx = dx/(xmax-xmin)*nxbins;
   Double_t dbiny = dy/(ymax-ymin)*nybins;
   Int_t di = int(dbinx);
   Int_t dj = int(dbiny);
   Int_t i0 = (di > 0)? di+1 : 1;
   Int_t j0 = (dj > 0)? dj+1 : 1;
   Int_t i1 = (di < 0)? di+nxbins : nxbins;
   Int_t j1 = (dj < 0)? dj+nybins : nybins;
   Double_t fx1 = dbinx-di;
   Double_t fy1 = dbiny-dj;
   Double_t fx0 = 1-fx1;
   Double_t fy0 = 1-fy1;

   Map2D *dst = new Map2D(*this);
   dst->Reset();
   for (Int_t i=i0; i < i1; ++i) {
      Int_t isrc = i-di;
      Double_t z00 = GetBinContent(isrc,j0-dj);
      Double_t z10 = GetBinContent(isrc+1,j0-dj);
      Double_t zint0 = (z00 == 0)? ((fx0 > 0.5)? 0 : z10) :
                       (z10 == 0)? ((fx1 > 0.5)? 0 : z00) :
                       z00*fx0 + z10*fx1;
      for (Int_t j=j0; j < j1; ++j) {
         Int_t jsrc = j-dj;
         Double_t z01 = GetBinContent(isrc,jsrc+1);
         Double_t z11 = GetBinContent(isrc+1,jsrc+1);
         Double_t zint1 = (z01 == 0)? ((fx0 > 0.5)? 0 : z11) :
                          (z11 == 0)? ((fx1 > 0.5)? 0 : z01) :
                          z01*fx0 + z11*fx1;
         Double_t zint = (zint0 == 0)? ((fy0 > 0.5)? 0 : zint1) :
                         (zint1 == 0)? ((fy1 > 0.5)? 0 : zint0) :
                         zint0*fy0 + zint1*fy1;
         dst->SetBinContent(i,j,(zint == 0)? 0 : zint+dz);
         zint0 = zint1;
      }
   }
   return dst;
}

Map2D &Map2D::Rotate(Double_t phi, Double_t theta, Double_t psi, Int_t degrees)
{
   // Rotates the map image by Euler angles phi,theta,psi about the center
   // of the map surface.  Angles may be in degrees (degrees=1) or
   // radians (degrees=0).  The angle phi determines the direction of
   // the theta rotation axis in the xy plane, and does not generate
   // an independent rotation.  Map rotations only make sense for small
   // theta.  The theta rotation is implemented as a rotation about the
   // x axis by angle theta*cos(phi) followed by a rotation about the y
   // axis by angle theta*sin(phi), with errors entering at the level of
   // theta^3.  Rotations in the xy plane (angle psi) may be of any size,
   // and may result in clipping of the image.  Regions of the map that
   // are rotated outside the map range are lost.  Original contents of
   // the map are overwritten.

   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t x0 = (xmin+xmax)/2;
   Double_t y0 = (ymin+ymax)/2;
   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Int_t pcount=0;
   for (Int_t ix=1; ix <= nxbins; ++ix) {
      for (Int_t iy=1; iy <= nybins; ++iy) {
         if (GetBinContent(ix,iy)) {
            ++pcount;
         }
      }
   }
   Int_t i0 = nxbins/2;
   Int_t j0 = nybins/2;
   Int_t peri=0;
   Double_t sum0=1;
   Double_t sum1=GetBinContent(i0,j0);
   while (sum0 < pcount/10) {
      ++peri;
      for (Int_t i=-peri; i < peri; ++i) {
          Double_t z;
          sum1 += ((z=GetBinContent(i0+i,j0-peri)) != 0)? ++sum0,z : 0;
          sum1 += ((z=GetBinContent(i0+peri,j0+i)) != 0)? ++sum0,z : 0;
          sum1 += ((z=GetBinContent(i0-i,j0+peri)) != 0)? ++sum0,z : 0;
          sum1 += ((z=GetBinContent(i0-peri,j0-i)) != 0)? ++sum0,z : 0;
      }
   }
   Double_t z0 = sum1/sum0;
   Double_t toradians = (degrees)? M_PI/180 : 1;
   Double_t thetax = theta*cos(phi*toradians);
   Double_t thetay = theta*sin(phi*toradians);
   Map2D *tmp1 = this;
   if (thetax != 0) {
      tmp1 = rotateX(thetax*toradians,y0,z0);
   }
   Map2D *tmp2 = tmp1;
   if (thetay != 0) {
      tmp2 = tmp1->rotateY(thetay*toradians,x0,z0);
   }
   Map2D *tmp3 = tmp2;
   if (psi != 0) {
      tmp3 = tmp2->rotateZ(psi*toradians,x0,y0);
   }
   if (tmp3 != this) {
      *this = *tmp3;
   }
   if (tmp1 != this) {
      delete tmp1;
   }
   if (tmp2 != tmp1) {
      delete tmp2;
   }
   if (tmp3 != tmp2) {
      delete tmp3;
   }
   return *this;
}

Map2D *Map2D::rotateX(Double_t thetarad, Double_t y0, Double_t z0) const
{
   // Rotates the map image about the x axis by angle thetarad in radians.
   // The rotation axis is located at coordinate y0 and height z0.
   // The map must remain single-valued after the rotation, which places
   // an upper limit on the magnitude of theta that depends on the
   // height profile of the map relative to z0.  An error message is
   // printed if this constraint is violated.  A new Map2D object is
   // created to hold the result, and the original object is untouched.
   // The caller must delete the returned object.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dy = (ymax-ymin)/nybins;
   Double_t costheta = cos(thetarad);
   Double_t sintheta = sin(thetarad);
   Map2D *dst = new Map2D(*this);
   dst->Reset();
   for (Int_t j=1; j <= nybins; ++j) {
      Double_t y = ymin+(j-0.5)*(ymax-ymin)/nybins - y0;
      for (Int_t i=1; i <= nxbins; ++i) {
         Double_t z = GetBinContent(i,j);
         if (z == 0) {
            dst->SetBinContent(i,j,0);
         }
         else {
            dst->SetBinContent(i,j,y*sintheta/zunit2xy+(z-z0)*costheta+z0);
         }
         if (fabs((z-z0)*sintheta)*zunit2xy > dy) {
            std::cerr << "Warning in Map2D::rotateX - loss of accuracy"
                      << " trying to rotate too far away from normal."
                      << std::endl;
         }
      }
   }
   Map2D *redst = dst->rescale(1,costheta,1,0,y0,0);
   delete dst;
   return redst;
}

Map2D *Map2D::rotateY(Double_t thetarad, Double_t x0, Double_t z0) const
{
   // Rotates the map image about the y axis by angle thetarad in radians.
   // The rotation axis is located at coordinate x0 and height z0.
   // The map must remain single-valued after the rotation, which places
   // an upper limit on the magnitude of theta that depends on the
   // height profile of the map relative to z0.  An error message is
   // printed if this constraint is violated.  A new Map2D object is
   // created to hold the result, and the original object is untouched.
   // The caller must delete the returned object.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t costheta = cos(thetarad);
   Double_t sintheta = sin(thetarad);
   Map2D *dst = new Map2D(*this);
   dst->Reset();
   for (Int_t i=1; i <= nxbins; ++i) {
      Double_t x = xmin+(i-0.5)*(xmax-xmin)/nxbins - x0;
      for (Int_t j=1; j <= nybins; ++j) {
         Double_t z = GetBinContent(i,j);
         if (z == 0) {
            dst->SetBinContent(i,j,0);
         }
         else {
            dst->SetBinContent(i,j,-x*sintheta/zunit2xy+(z-z0)*costheta+z0);
         }
         if (fabs((z-z0)*sintheta)*zunit2xy > dx) {
            std::cerr << "Warning in Map2D::rotateY - loss of accuracy"
                      << " trying to rotate too far away from normal."
                      << std::endl;
         }
      }
   }
   Map2D *redst = dst->rescale(costheta,1,1,x0,0,0);
   delete dst;
   return redst;
}

Map2D *Map2D::rotateZ(Double_t phirad, Double_t x0, Double_t y0) const
{
   // Rotates the map image about the z axis by angle thetarad in radians.
   // The rotation axis is located at (x0,y0) in map coordinates.  There
   // are no assumed bounds on phirad.  Regions of the image that move
   // outside the map range are clipped and lost.  Regions of the image
   // that move into the range from outside are zero.  A new Map2D object
   // is created to hold the result, and the original object is untouched.
   // The caller must delete the returned object.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   Double_t cosphi = cos(phirad);
   Double_t sinphi = sin(phirad);

   Map2D *dst = new Map2D(*this);
   dst->Reset();
   for (Int_t i=1; i <= nxbins; ++i) {
      Double_t x = xmin+(i-0.5)*dx - x0;
      for (Int_t j=1; j <= nybins; ++j) {
         Double_t y = ymin+(j-0.5)*dy - y0;
         Double_t xsrc = x*cosphi+y*sinphi + x0;
         Double_t ysrc = y*cosphi-x*sinphi + y0;
         Double_t xbins = (xsrc-xmin)/dx;
         Double_t ybins = (ysrc-ymin)/dy;
         if (xbins < 0 || xbins > nxbins ||
             ybins < 0 || ybins > nybins) {
            continue;
         }
         Int_t i0 = int(xbins+0.5);
         Int_t j0 = int(ybins+0.5);
         i0 = (i0 < 1)? 1 : i0;
         j0 = (j0 < 1)? 1 : j0;
         Int_t i1 = (i0 >= nxbins)? i0 : i0+1;
         Int_t j1 = (j0 >= nybins)? j0 : j0+1;
         Double_t fx0 = xbins+0.5-i0;
         Double_t fy0 = ybins+0.5-j0;
         Double_t fx1 = 1-fx0;
         Double_t fy1 = 1-fy0;
         Double_t z00 = GetBinContent(i0,j0);
         Double_t z10 = GetBinContent(i1,j0);
         Double_t z01 = GetBinContent(i0,j1);
         Double_t z11 = GetBinContent(i1,j1);
         Double_t zint0 = (z00 == 0)? ((fx0 > 0.5)? 0 : z10) :
                          (z10 == 0)? ((fx1 > 0.5)? 0 : z00) :
                          z00*fx0 + z10*fx1;
         Double_t zint1 = (z01 == 0)? ((fx0 > 0.5)? 0 : z11) :
                          (z11 == 0)? ((fx1 > 0.5)? 0 : z01) :
                          z01*fx0 + z11*fx1;
         Double_t zint = (zint0 == 0)? ((fy0 > 0.5)? 0 : zint1) :
                         (zint1 == 0)? ((fy1 > 0.5)? 0 : zint0) :
                         zint0*fy0 + zint1*fy1;
         dst->SetBinContent(i,j,zint);
      }
   }
   return dst;
}

Map2D &Map2D::Rescale(Double_t sx, Double_t sy, Double_t sz)
{
   // Rescales the map image along all three axes by independent scale
   // factors.  The origin of the rescaling is taken to be the center
   // of the map in x and y, and z=0.  The original map is overwritten.
   // Note: the special case of sx=-1,sy=1,sz=-1 or sx=1,sy=-1,sz=-1
   // can be used to invert the surface, effectively flipping it over.

   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Map2D *tmp = rescale(sx,sy,sz,(xmax+xmin)/2,(ymax-ymin)/2,0);
   *this = *tmp;
   delete tmp;
   return *this;
}

Map2D &Map2D::Resize(Double_t xlow, Double_t ylow,
                     Double_t xhigh, Double_t yhigh, Int_t pixels) const
{
   // Creates a new map with bounds set by corner coordinates (xlow,ylow)
   // and (xhigh,yhigh), then copies this map into the new one.  The
   // original map (*this) is not modified.  The name of the new map is
   // the name of this map with the suffix "_rN", where N is an integer
   // starting with 1 and counting forward.

   std::string name = GetName();
   size_t sufi = name.rfind("_r");
   for (int revno=1; revno < 99999999; ++revno) {
      char str[99];
      sprintf(str,"_r%d",revno);
      name = name.substr(0,sufi) + str;
      if (gDirectory->FindObject(name.c_str()) == 0) {
         break;
      }
      sufi = name.rfind("_r");
   }
   std::cout << "New map created with name " << name << std::endl;

   Double_t deltaX = GetXaxis()->GetBinWidth(1);
   Double_t deltaY = GetYaxis()->GetBinWidth(1);
   Int_t nxbins,nybins;
   if (pixels) {
      nxbins = (int)(xhigh-xlow)+1;
      nybins = (int)(yhigh-ylow)+1;
      xlow = GetXaxis()->GetXmin()+(xlow-1)*deltaX;
      ylow = GetYaxis()->GetXmin()+(ylow-1)*deltaY;
   }
   else {
      nxbins = (int)((xhigh-xlow)/deltaX + 0.999);
      nybins = (int)((yhigh-ylow)/deltaY + 0.999);
   }
   xhigh = xlow+nxbins*deltaX;
   yhigh = ylow+nybins*deltaY;
   TH2D hnew(name.c_str(),GetTitle(),nxbins,xlow,xhigh,nybins,ylow,yhigh);
   Map2D *newmap = new Map2D(hnew);
   newmap->Fill(this);
   return *newmap;
}

Map2D *Map2D::rescale(Double_t sx, Double_t sy, Double_t sz,
                      Double_t x0, Double_t y0, Double_t z0) const
{
   // Rescales the map image along all three axes by independent scale
   // factors.  The origin of the rescaling is (x0,y0) in map coordinates,
   // and z0 in height units.  A new object is created to hold the result
   // and the original object is untouched.  The caller must delete the
   // returned object.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;

   Map2D *dst = new Map2D(*this);
   dst->Reset();
   for (Int_t i=1; i <= nxbins; ++i) {
      Double_t x = (xmin+(i-0.5)*dx-x0)/sx + x0;
      for (Int_t j=1; j <=nybins; ++j) {
         Double_t y = (ymin+(j-0.5)*dy-y0)/sy + y0;
         Double_t xbins = (x-xmin)/dx;
         Double_t ybins = (y-ymin)/dy;
         Int_t i0 = int(xbins+0.5);
         Int_t j0 = int(ybins+0.5);
         i0 = (i0 < 1)? 1 : i0;
         j0 = (j0 < 1)? 1 : j0;
         Int_t i1 = (i0 >= nxbins)? i0 : i0+1;
         Int_t j1 = (j0 >= nybins)? j0 : j0+1;
         Double_t fx0 = i1-xbins-0.5;
         Double_t fy0 = j1-ybins-0.5;
         Double_t fx1 = 1-fx0;
         Double_t fy1 = 1-fy0;
         Double_t z00 = GetBinContent(i0,j0);
         Double_t z10 = GetBinContent(i1,j0);
         Double_t z01 = GetBinContent(i0,j1);
         Double_t z11 = GetBinContent(i1,j1);
         Double_t zint0 = (z00 == 0)? ((fx0 > 0.5)? 0 : z10) :
                          (z10 == 0)? ((fx1 > 0.5)? 0 : z00) :
                          z00*fx0 + z10*fx1;
         Double_t zint1 = (z01 == 0)? ((fx0 > 0.5)? 0 : z11) :
                          (z11 == 0)? ((fx1 > 0.5)? 0 : z01) :
                          z01*fx0 + z11*fx1;
         Double_t zint = (zint0 == 0)? ((fy0 > 0.5)? 0 : zint1) :
                         (zint1 == 0)? ((fy1 > 0.5)? 0 : zint0) :
                         zint0*fy0 + zint1*fy1;
         dst->SetBinContent(i,j,(zint-z0)*sz+z0);
      }
   }
   return dst;
}

Double_t Map2D::Correlation(const Map2D *map, Double_t contrast) const
{
   // Computes the height correlation with another map, useful for
   // aligning two images of the same terrain.  The contrast
   // argument serves as a weight factor on terms in the sum
   // where one or the other of the two maps is zero.  This allows
   // control over how zero regions are treated, such that
   //	contrast=0 : ignore zero regions
   //	contrast=1 : treat them like plateaus with z=0
   //   contrast=BIG : boundaries dominate, maps are B/W
   // Agreement between their coordinate ranges is not required,
   // but the two maps must have the same dimensions. 

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   if (map->GetXaxis()->GetNbins() != nxbins ||
       map->GetYaxis()->GetNbins() != nybins) {
      std::cerr << "Error in Map2D::Correlation - "
                << "maps must have the same dimensions."
                << std::endl;
      return 0;
   }
   Double_t sum00=0;
   Double_t sum10=0;
   Double_t sum01=0;
   Double_t sum11=0;
   Double_t sum20=0;
   Double_t sum02=0;
   for (Int_t i=1; i <= nxbins; ++i) {
      for (Int_t j=1; j <= nybins; ++j) {
         Double_t z0 = GetBinContent(i,j);
         Double_t z1 = map->GetBinContent(i,j);
         Double_t wgt = (z0*z1 == 0)? contrast : 1;
         if (wgt > 1) {
            z0 = (z0==0)? -wgt : z0;
            z1 = (z1==0)? -wgt : z1;
            wgt = 1;
         }
         sum00 += wgt;
         sum10 += wgt*z0;
         sum01 += wgt*z1;
         sum11 += wgt*z0*z1;
         sum20 += wgt*z0*z0;
         sum02 += wgt*z1*z1;
      }
   }
   Double_t covar = sum11/sum00-(sum10/sum00)*(sum01/sum00);
   Double_t var0 = sum20/sum00-(sum10/sum00)*(sum10/sum00);
   Double_t var1 = sum02/sum00-(sum01/sum00)*(sum01/sum00);
   return covar/sqrt((var0*var1 > 0)? var0*var1 : 1e-100);
}

Map2D &Map2D::Fill(const Map2D *map)
{
   // Copies the map from the source Map2D object into this object,
   // overwriting this object's data but preserving its dimensions and
   // axis limits.  Matte pixels in the source map are ignored, making
   // this method an effective way to overlay two non-overlapping images.
   // No new objects are created.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   Int_t nxbins_s = map->GetXaxis()->GetNbins();
   Int_t nybins_s = map->GetYaxis()->GetNbins();
   Double_t xmin_s = map->GetXaxis()->GetXmin();
   Double_t xmax_s = map->GetXaxis()->GetXmax();
   Double_t ymin_s = map->GetYaxis()->GetXmin();
   Double_t ymax_s = map->GetYaxis()->GetXmax();
   Double_t dx_s = (xmax_s-xmin_s)/nxbins_s;
   Double_t dy_s = (ymax_s-ymin_s)/nybins_s;

   Int_t pwritten=0;
   for (Int_t i=1; i <= nxbins; ++i) {
      Double_t x = xmin+(i-0.5)*dx;
      for (Int_t j=1; j <=nybins; ++j) {
         Double_t y = ymin+(j-0.5)*dy;
         Double_t xbin_s = (x-xmin_s)/dx_s;
         Double_t ybin_s = (y-ymin_s)/dy_s;
         Int_t i0 = int(xbin_s+0.5);
         Int_t j0 = int(ybin_s+0.5);
         i0 = (i0 < 1)? 1 : i0;
         j0 = (j0 < 1)? 1 : j0;
         Int_t i1 = (i0 >= nxbins_s)? i0 : i0+1;
         Int_t j1 = (j0 >= nybins_s)? j0 : j0+1;
         Double_t fx0 = i1-xbin_s-0.5;
         Double_t fy0 = j1-ybin_s-0.5;
         Double_t fx1 = 1-fx0;
         Double_t fy1 = 1-fy0;
         Double_t z00 = map->GetBinContent(i0,j0);
         Double_t z10 = map->GetBinContent(i1,j0);
         Double_t z01 = map->GetBinContent(i0,j1);
         Double_t z11 = map->GetBinContent(i1,j1);
         Double_t zint0 = (z00 == 0)? ((fx0 > 0.5)? 0 : z10) :
                          (z10 == 0)? ((fx1 > 0.5)? 0 : z00) :
                          z00*fx0 + z10*fx1;
         Double_t zint1 = (z01 == 0)? ((fx0 > 0.5)? 0 : z11) :
                          (z11 == 0)? ((fx1 > 0.5)? 0 : z01) :
                          z01*fx0 + z11*fx1;
         Double_t zint = (zint0 == 0)? ((fy0 > 0.5)? 0 : zint1) :
                         (zint1 == 0)? ((fy1 > 0.5)? 0 : zint0) :
                         zint0*fy0 + zint1*fy1;
         if (zint) {
            SetBinContent(i,j,zint);
            ++pwritten;
         }
      }
   }
   std::cout << "Map2D::Fill overlayed " << pwritten
             << " pixels." << std::endl;
   return *this;
}

Map2D &Map2D::Mask(const Map2D *map, Int_t neg, Double_t value)
{
   // Overlays the input map on the output, and masks (overwrites with matte)
   // any cells that are zero in the input map.  Cells in the input map
   // that are non-zero are overwritten in *this with the constant "value"
   // unless value=0, in which case those pixels are preserved.  If argument
   // "neg" is non-zero, the input map is treated as a negative.  Both input
   // and output maps must have the same dimensions.  No new objects are
   // created. 

   Int_t nxbins = map->GetXaxis()->GetNbins();
   Int_t nybins = map->GetYaxis()->GetNbins();
   if (GetXaxis()->GetNbins() != nxbins || GetYaxis()->GetNbins() != nybins) {
      std::cerr << "Error in Map2D::Mask - argument map has different"
                << " pixel dimensions from the object being masked."
                << std::endl;
      return *this;
   }
   for (Int_t i=1; i <= nxbins; ++i) {
      for (Int_t j=1; j <= nybins; ++j) {
         double cont = map->GetBinContent(i,j);
         if (!neg && cont == 0 || neg && cont != 0) {
            SetBinContent(i,j,0);
         }
         else if (value) {
            SetBinContent(i,j,value);
         }
      }
   }
   return *this;
}

Int_t Map2D::ZeroSuppress(Double_t threshold, Double_t zero)
{
   // Scans through the map and finds any pixels less than the supplied
   // threshold value, replacing them with the value zero.

   int count=0;
   for (int ix=1; ix <= GetXaxis()->GetNbins(); ++ix) {
      for (int iy=1; iy <= GetYaxis()->GetNbins(); ++iy) {
         if (GetBinContent(ix,iy) < threshold) {
            SetBinContent(ix,iy,zero);
            ++count;
         }
      }
   }
   return count;
}

Int_t Map2D::Despeckle(Int_t maxsize)
{
   // Searches the map for regions.  Pixels belong to the same region if
   // an unbroken path of contiguous pixels can be found that joins them.
   // Two pixels are contiguous if they share a common edge; touching at the
   // corners does not count.  Regions up to maxsize pixels are erased
   // (overwritten with matte) and a count of the remaining islands of greater
   // than maxsize pixels is returned.  If called with maxsize=0 then the
   // total number of islands is returned, and the map is not modified.

   std::vector<Int_t> rsize;
   std::vector<Int_t> rjoin;
   rsize.push_back(0);
   rjoin.push_back(0);
   Int_t regions=1;

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Int_t *mask = new Int_t[nxbins*nybins];
   Int_t region = 0;
   for (Int_t j=0; j < nybins; ++j) {
      if (GetBinContent(1,j+1) == 0) {
         mask[j] = region = 0;
         ++rsize[0];
      }
      else if (region > 0) {
         mask[j] = region;
         ++rsize[region];
      }
      else {
         mask[j] = region = regions++;
         rsize.push_back(1);
         rjoin.push_back(0);
      }
   }
   for (Int_t i=1; i < nxbins; ++i) {
      region = 0;
      Int_t j0 = i*nybins;
      for (Int_t j=0; j < nybins; ++j) {
         if (GetBinContent(i+1,j+1) == 0) {
            mask[j+j0] = region = 0;
            ++rsize[0];
            continue;
         }
         else if (mask[j+j0-nybins] != 0) {
            Int_t left = mask[j+j0-nybins];
            if (region > left) {
               rjoin[region] = left;
               rsize[left] += rsize[region];
               rsize[region] = 0;
               region = left;
            }
            else if (region == 0) {
               region = left;
            }
            else if (region < left) {
               rjoin[left] = region;
               rsize[region] += rsize[left];
               rsize[left] = 0;
            }
         }
         else if (region == 0) {
            region = regions++;
            rsize.push_back(0);
            rjoin.push_back(0);
         }
         mask[j+j0] = region;
         ++rsize[region];
      }
   }

   for (Int_t i=0; i < nxbins; ++i) {
      Int_t j0 = i*nybins;
      for (Int_t j=0; j < nybins; ++j) {
         region = mask[j+j0];
         while (rjoin[region]) {
            region = rjoin[region];
         }
         if (rsize[region] <= maxsize) {
            SetBinContent(i+1,j+1,0);
         }
      }
   }

   Int_t surviving = 0;
   for (Int_t r=1; r < regions; ++r) {
      if (rjoin[r] == 0 && rsize[r] > maxsize) {
         ++surviving;
      }
   }
   delete [] mask;
   return surviving;
}

Int_t Map2D::Despike(Double_t maxheight, Int_t regionsize)
{
   // Searches the map for localized spikes that appear in regions of
   // smoothly varying height, and replaces any spikes that are greater
   // than maxheight above or below the local average surface height with
   // the local neighborhood average.  The size of the neighborhood is
   // (2*regionsize+1)x(2*regionsize+1) in pixels.  This operation is not
   // reversible.

   Int_t nxbins = GetXaxis()->GetNbins()+1;
   Int_t nybins = GetYaxis()->GetNbins()+1;
   Double_t *zsum = new Double_t[nxbins*nybins];
   Int_t *nsum = new Int_t[nxbins*nybins];
   Int_t *mask = new Int_t[nxbins*nybins];
   for (Int_t ix=0; ix < nxbins; ++ix) {
      mask[ix] = 0;
      zsum[ix] = 0;
      nsum[ix] = 0;
   }

   for (Int_t iy=1; iy < nybins; ++iy) {
      Int_t rowsum0[nxbins];
      Double_t rowsum1[nxbins];
      Int_t sum0 = rowsum0[0] = 0;
      Double_t sum1 = rowsum1[0] = 0;
      for (Int_t ix=1; ix < nxbins; ++ix) {
        Double_t content = GetBinContent(ix,iy);
        mask[iy*nxbins+ix] = (content > 0)? 1 : 0;
        rowsum0[ix] = (content > 0)? ++sum0 : sum0;
        rowsum1[ix] = sum1 += content;
      }
      zsum[iy*nxbins] = 0;
      nsum[iy*nxbins] = 0;
      for (Int_t ix=1; ix < nxbins; ++ix) {
        Int_t start = ix-regionsize;
        Int_t stop = ix+regionsize;
        start = (start < 1)? 0 : start-1;
        stop = (stop >= nxbins)? nxbins-1 : stop;
        nsum[iy*nxbins+ix] = rowsum0[stop] - rowsum0[start];
        zsum[iy*nxbins+ix] = rowsum1[stop] - rowsum1[start];
      }
   }
   for (Int_t ix=1; ix < nxbins; ++ix) {
      Int_t colsum0[nybins];
      Double_t colsum1[nybins];
      Int_t sum0 = colsum0[0] = 0;
      Double_t sum1 = colsum1[0] = 0;
      for (Int_t iy=1; iy < nybins; ++iy) {
        colsum0[iy] = sum0 += nsum[iy*nxbins+ix];
        colsum1[iy] = sum1 += zsum[iy*nxbins+ix];
      }
      for (Int_t iy=1; iy < nybins; ++iy) {
        Int_t start = iy-regionsize;
        Int_t stop = iy+regionsize;
        start = (start < 1)? 0 : start-1;
        stop = (stop >= nybins)? nybins-1 : stop;
        nsum[iy*nxbins+ix] = colsum0[stop] - colsum0[start];
        zsum[iy*nxbins+ix] = colsum1[stop] - colsum1[start];
      }
   }

   // mask(ix,iy) encodes the status of pixel ix,iy as follows:
   //   0 : matte pixel, ignore.
   //   1 : pixel has height data, include in local neighborhood average.
   //  -1 : pixel has height data, but exclude from local neighborhood average
   //       and mark as a spike to be overwritten later.

   Int_t changes=1;
   while (changes) {
      changes = 0;
      for (Int_t ix=1; ix < nxbins; ++ix) {
         for (Int_t iy=1; iy < nybins; ++iy) {
            if (mask[iy*nxbins+ix] == 1) {
               Double_t z = GetBinContent(ix,iy);
               Double_t zave = zsum[iy*nxbins+ix]/nsum[iy*nxbins+ix];
               if (fabs(z-zave) > maxheight) {
                  Int_t rx0 = ix-regionsize;
                  Int_t rx1 = ix+regionsize;
                  Int_t ry0 = iy-regionsize;
                  Int_t ry1 = iy+regionsize;
                  rx0 = (rx0 < 1)? 1 : rx0;
                  ry0 = (ry0 < 1)? 1 : ry0;
                  rx1 = (rx1 < nxbins)? rx1 : nxbins-1;
                  ry1 = (ry1 < nybins)? ry1 : nybins-1;
                  for (Int_t rx=rx0; rx <= rx1; ++rx) {
                     for (Int_t ry=ry0; ry <= ry1; ++ry) {
                       zsum[ry*nxbins+rx] -= z;
                       nsum[ry*nxbins+rx] -= 1;
                     }
                  }
                  mask[iy*nxbins+ix] = -1;
                  ++changes;
               }
            }
         }
      }
      // std::cout << "changes: " << changes << std::endl;
   }

   Int_t spikes = 0;
   for (Int_t ix=1; ix < nxbins; ++ix) {
      for (Int_t iy=1; iy < nybins; ++iy) {
         Int_t mask_ = mask[iy*nxbins+ix];
         Int_t nsum_ = nsum[iy*nxbins+ix];
         if (mask_ == 1) {
            SetBinContent(ix,iy,GetBinContent(ix,iy));
         }
         else if (mask_ == -1 && nsum_ > 0) {
            SetBinContent(ix,iy,zsum[iy*nxbins+ix]/nsum_);
            ++spikes;
         }
      }
   }

   delete [] zsum;
   delete [] nsum;
   delete [] mask;
   return spikes;
}

Map2D &Map2D::Center()
{
   // Finds the center of gravity of the non-zero portion of the map
   // and applies a shift in x,y to locate the center at the midpoint.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;

   Double_t sum=0;
   Double_t sumx=0;
   Double_t sumy=0;
   for (Int_t ix=1; ix < nxbins; ++ix) {
      for (Int_t iy=1; iy < nybins; ++iy) {
         if (GetBinContent(ix,iy) == 0) {
            continue;
         }
         sumx += ix;
         sumy += iy;
         ++sum;
      }
   }
   if (sum == 0) {
      return *this;
   }
   Double_t xshift = ((nxbins+1)/2 - sumx/sum) * dx;
   Double_t yshift = ((nybins+1)/2 - sumy/sum) * dy;
   return Shift(xshift,yshift);
}

Map2D &Map2D::Level()
{
   // Locates the left-most and the right-most pixels in the map with
   // non-zero values and applies an in-plane rotation such that those
   // two pixels are in the same row (y-value) of the map.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;

   Int_t left[3] = {nxbins,0,0};
   Int_t right[3] = {0,0,0};
   for (Int_t ix=1; ix <= nxbins; ++ix) {
      for (Int_t iy=1; iy <= nybins; ++iy) {
         double cont = GetBinContent(ix,iy);
         if (cont == 0) {
            continue;
         }
         if (ix < left[0]) {
            left[0] = ix;
            left[1] = iy;
            left[2] = 1;
         }
         else if (ix == left[0]) {
            left[1] += iy;
            left[2] += 1;
         }
         else if (ix > right[0]) {
            right[0] = ix;
            right[1] = iy;
            right[2] = 1;
         }
         else if (ix == right[0]) {
            right[1] += iy;
            right[2] += 1;
         }
      }
   }

   Double_t alpha = atan2((right[1]/right[2] - left[1]/left[2]) * dy,
                          (right[0] - left[0]) * dx);
   return Rotate(0,0,-alpha);
}

Map2D &Map2D::Normalize()
{
   // Finds the maximum and minimum values of the map, excluding cells
   // with zero value, and sets the Max and Min to the corresponding values.

   double max = -1e300;
   double min = +1e300;
   for (int ix=1; ix <= GetNbinsX(); ++ix) {
      for (int iy=1; iy <= GetNbinsY(); ++iy) {
         double cont = GetBinContent(ix,iy);
         if (cont == 0) {
            continue;
         }
         max = (cont > max)? cont : max;
         min = (cont < min)? cont : min;
      }
   }
   SetMaximum(max);
   SetMinimum(min);
   return *this;
}


Double_t Map2D::TotalCurl(const Map2D *Vx, const Map2D *Vy,
                          Map2D **silhouette)
{
   // Forms a closed path around the outermost periphery of the map, and
   // computes the total curl of a 2D vector field within that path using
   // Stoke's theorem.  The two components of the vector field within the
   // plane of the map are accepted as arguments Vx,Vy.  The result is
   // returned in the units of V times coordinate length.  The two maps
   // given as arguments must have the same dimensions and bounds in x and 
   // y.  The object being used to compute the curl is overwritten with a
   // map filled with zeros everywhere except on the contour around which
   // the Stokes integral was performed, with each cell on the contour
   // containing the running integral value up to that point.  If optional
   // argument silhouette is provided by the caller, it is overwritten upon
   // return with a pointer to a new Map2D object with the same shape as
   // Vx,Vy whose contents represent an edge-enhanced silhouette of the
   // overlaid input maps, with bin values defined as follows. 
   //        0    : the bin is outside the contour,
   //      (0,1)  : the bin is inside the contour,
   //     [1,inf) : the bin is on the contour.
   // It is up to the user to delete the silhouette object when it is no
   // longer needed.

   Int_t nxbins = Vx->GetXaxis()->GetNbins();
   Int_t nybins = Vx->GetYaxis()->GetNbins();
   Double_t xmin = Vx->GetXaxis()->GetXmin();
   Double_t xmax = Vx->GetXaxis()->GetXmax();
   Double_t ymin = Vx->GetYaxis()->GetXmin();
   Double_t ymax = Vx->GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   if (Vy->GetXaxis()->GetNbins() != nxbins ||
       Vy->GetYaxis()->GetNbins() != nybins ||
       Vy->GetXaxis()->GetXmin() != xmin ||
       Vy->GetXaxis()->GetXmax() != xmax ||
       Vy->GetYaxis()->GetXmin() != ymin ||
       Vy->GetYaxis()->GetXmax() != ymax) {
      std::cerr << "Error in Map2D::TotalCurl - the two input maps have"
                << " different pixel dimensions or axis bounds."
                << std::endl;
      return 0;
   }

   *this = *Vx;
   Mask(Vy);
   int specksize=10;
   while (Despeckle(specksize) > 1) {
      specksize *= 2;
   }
   if (Despeckle() != 1) {
      std::cerr << "Error in Map2D::TotalCurl - the map is too fragmented"
                << " for this algorithm, please despeckle and try again."
                << std::endl;
      return 0;
   }

   // Highlight the boundary pixels
   Double_t *bound = new Double_t[nxbins*nybins];
   for (Int_t i=1; i <= nxbins; ++i) {
      Int_t j0 = (i-1)*nybins-1;
      for (Int_t j=1; j <= nybins; ++j) {
         if (GetBinContent(i,j) != 0) {
            Double_t south = (j == 1)?      0 : GetBinContent(i,j-1);
            Double_t north = (j == nybins)? 0 : GetBinContent(i,j+1);
            Double_t west  = (i == 1)?      0 : GetBinContent(i-1,j);
            Double_t east  = (i == nxbins)? 0 : GetBinContent(i+1,j);
            bound[j+j0] = 4.1 - ((south == 0)? 0 : 1) - ((north == 0)? 0 : 1)
                              - ((west == 0)?  0 : 1) - ((east == 0)?  0 : 1);
         }
         else {
            bound[j+j0] = 0;
         }
      }
   }
   for (Int_t i=1; i <= nxbins; ++i) {
      Int_t j0 = (i-1)*nybins-1;
      for (Int_t j=1; j <= nybins; ++j) {
         SetBinContent(i,j,bound[j+j0]);
      }
   }

   // Direction codes are: 0=east, 1=north, 2=west, 3=south
   Int_t di[4] = {+1,0,-1,0};
   Int_t dj[4] = {0,+1,0,-1};
   Int_t back[4] = {2,3,0,1};

   // Start at the center of the east coast and head northeast
   Int_t istep=0;
   Int_t jstep=0;
   for (Int_t i=nxbins/2,j=nybins/2; i < nxbins; ++i) {
      if (bound[i*nybins+j] > 1) {
         istep = i;
         jstep = j;
      }
   }
   Int_t istart=istep;
   Int_t jstart=jstep;
   Int_t stepdir=2;

   // Dig an outer perimeter loop around the primary region
   std::list<Int_t> dirseq;
   Int_t steps=0;
#ifdef MANUAL_STEPPING_IN_TOTALCURL
   Int_t skip=0;
   TCanvas *c1=0;
#endif
   while (1 > 0) {
      SetBinContent(istep+1,jstep+1,5.1);

#ifdef MANUAL_STEPPING_IN_TOTALCURL
      if (skip-- == 0) {
         GetXaxis()->SetRangeUser(xmin+(istep-25)*dx,xmin+(istep+25)*dx);
         GetYaxis()->SetRangeUser(ymin+(jstep-25)*dy,ymin+(jstep+25)*dy);
         if (c1 == 0) {
            gDirectory->GetObject("c1",c1);
            if (c1 == 0) {
               c1 = new TCanvas("c1","c1",500,500);
            }
            c1->cd();
         }
         Draw("colz");
         c1->Update();
         std::cout << "Enter number of steps to skip before pausing next: ";
         std::cin >> skip;
         if (skip < 0) {
            return 0;
         }
      }
#endif

      Double_t score = 4;
      Int_t nextdir = -1;
      for (Int_t d=1; d < 4; ++d) {
         Int_t dir = back[(stepdir+d)%4];
         Double_t s = bound[(istep+di[dir])*nybins+jstep+dj[dir]];
         if (s > 1 && s < score) {
            nextdir = dir;
            score = s;
         }
      }
      if (score > 3) {
         for (Int_t d=1; d < 4; ++d) {
            Int_t dir = back[(stepdir+d)%4];
            Int_t itry = istep+di[dir];
            Int_t jtry = jstep+dj[dir];
            if (bound[itry*nybins+jtry] > 0) {
               Double_t sees=0;
               for (Int_t dtry=0; dtry < 4; ++dtry) {
                  Double_t s = bound[(itry+di[dtry])*nybins+jtry+dj[dtry]];
                  if (s > 1 && s < 3) {
                     ++sees;
                  }
               }
               if (sees > 0) {
                  nextdir = dir;
                  score = 3;
                  break;
               }
            }
         }
      }
      if (score > 3) {
         if (steps) {
            SetBinContent(istep+1,jstep+1,4.1);
            istep -= di[stepdir];
            jstep -= dj[stepdir];
            stepdir = dirseq.back();
            dirseq.pop_back();
            --steps;
            continue;
         }
         std::cerr << "Error in Map2D::TotalCurl - algorithm is stuck after "
                   << steps << " steps, and cannot continue."
                   << std::endl;
         return 0;
      }
      SetBinContent(istep+1,jstep+1,3.8);
      dirseq.push_back(stepdir);
      stepdir = nextdir;
      istep += di[stepdir];
      jstep += dj[stepdir];
      bound[istep*nybins+jstep] = -1;
      steps++;
      if (istep == istart && jstep == jstart) {
         dirseq.push_back(stepdir);
         break;
      }
   }

   // Fill in any voids in the interior

   for (Int_t ix=2; ix < nxbins; ++ix) {
      for (Int_t iy=2; iy < nybins; ++iy) {
         if (GetBinContent(ix,iy) < 2) {
            SetBinContent(ix,iy,0.1);
         }
      }
   }
   int voids=1;
   while (voids) {
      voids = 0;
      for (Int_t ix=2; ix < nxbins; ++ix) {
         for (Int_t iy=2; iy < nybins; ++iy) {
            double u = GetBinContent(ix,iy);
            if (u > 0 && u < 1 && (
                GetBinContent(ix,iy-1) == 0 ||
                GetBinContent(ix-1,iy) == 0 ))
            {
               SetBinContent(ix,iy,0);
               ++voids;
            }
         }
      }
      for (Int_t ix=nxbins-1; ix > 1; --ix) {
         for (Int_t iy=nybins-1; iy > 1; --iy) {
            double u = GetBinContent(ix,iy);
            if (u > 0 && u < 1 && (
                GetBinContent(ix,iy+1) == 0 ||
                GetBinContent(ix+1,iy) == 0 ))
            {
               SetBinContent(ix,iy,0);
               ++voids;
            }
         }
      }
   }

   // Save the silhouette map, if requested

   GetXaxis()->SetRange();
   GetYaxis()->SetRange();
   if (silhouette) {
      if (*silhouette) {
         delete *silhouette;
      }
      *silhouette = new Map2D(*this);
   }

   // Now follow the loop and compute the path integral

   Reset();
   istep = istart;
   jstep = jstart;
   Double_t curl=0;
   std::list<Int_t>::iterator stepit = dirseq.begin();
   for (++stepit; stepit != dirseq.end(); ++stepit) {
      switch (*stepit) {
       case 0: //east
         curl += Vx->GetBinContent(istep+1,jstep+1)*dx;
         ++istep;
         break;
       case 1: //north
         curl += Vy->GetBinContent(istep+1,jstep+1)*dy;
         ++jstep;
         break;
       case 2: //west
         curl -= Vx->GetBinContent(istep,jstep+1)*dx;
         --istep;
         break;
       case 3: //south
         curl -= Vy->GetBinContent(istep+1,jstep)*dy;
         --jstep;
         break;
      }
      SetBinContent(istep+1,jstep+1,curl);
   }
   if (istep != istart || jstep != jstart) {
      std::cerr << "Error in Map2D::TotalCurl - path around map periphery"
                << " is not closed, and cannot continue."
                << std::endl;
      std::cerr << "   istart,jstart=" << istart << "," << jstart << std::endl
                << "   istop,jstop=" << istep << "," << jstep << std::endl;
      return 0;
   }

   delete [] bound;
   return curl;
}

Double_t Map2D::Curl(const Map2D *Vx, const Map2D *Vy)
{
   // Computes the curl of the 2D vector field represented by the components
   // Vx,Vy and stores the result in the *this map.  The value stored in
   // each cell of the output map represents the integral of the curl within
   // that cell, so that the sum over all cells in the map should equal the
   // line integral of the field around a closed loop enclosing the non-zero
   // field region, according to Stoke's theorem.  The complementary method
   // TotalCurl() is provided to compute the same result using the loop line
   // integral.  The return value is the surface integral of the curl.

   Int_t nxbins = Vx->GetXaxis()->GetNbins();
   Int_t nybins = Vx->GetYaxis()->GetNbins();
   Double_t xmin = Vx->GetXaxis()->GetXmin();
   Double_t xmax = Vx->GetXaxis()->GetXmax();
   Double_t ymin = Vx->GetYaxis()->GetXmin();
   Double_t ymax = Vx->GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   if (Vy->GetXaxis()->GetNbins() != nxbins ||
       Vy->GetYaxis()->GetNbins() != nybins ||
       Vy->GetXaxis()->GetXmin() != xmin ||
       Vy->GetXaxis()->GetXmax() != xmax ||
       Vy->GetYaxis()->GetXmin() != ymin ||
       Vy->GetYaxis()->GetXmax() != ymax) {
      std::cerr << "Error in Map2D::Curl - the two input maps have"
                << " different pixel dimensions or axis bounds."
                << std::endl;
      return 0;
   }

   *this = *Vx;
   Mask(Vy);
   int specksize=10;
   while (Despeckle(specksize) > 1) {
      specksize *= 2;
   }
   if (Despeckle() != 1) {
      std::cerr << "Error in Map2D::Curl - the map is too fragmented"
                << " for this algorithm, please despeckle and try again."
                << std::endl;
      return 0;
   }

   for (Int_t ix=2; ix < nxbins; ++ix) {
      for (Int_t iy=2; iy < nybins; ++iy) {
         SetBinContent(ix,iy,0);
      }
   }
   Double_t sum = 0;
   for (Int_t ix=2; ix < nxbins; ++ix) {
      for (Int_t iy=2; iy < nybins; ++iy) {
         double Vx_i_j = Vx->GetBinContent(ix,iy);
         double Vy_i_j = Vy->GetBinContent(ix,iy);
         double Vx_i_jp = Vx->GetBinContent(ix,iy+1);
         double Vy_i_jp = Vy->GetBinContent(ix,iy+1);
         double Vx_ip_j = Vx->GetBinContent(ix+1,iy);
         double Vy_ip_j = Vy->GetBinContent(ix+1,iy);
         double Vx_ip_jp = Vx->GetBinContent(ix+1,iy+1);
         double Vy_ip_jp = Vy->GetBinContent(ix+1,iy+1);
         if (Vx_i_j != 0 && Vx_i_jp != 0 && Vx_ip_j != 0 && Vx_ip_jp != 0 &&
             Vy_i_j != 0 && Vy_i_jp != 0 && Vy_ip_j != 0 && Vy_ip_jp != 0)
         {
            double curlint = (Vy_ip_j - Vy_i_j) * dy
                           - (Vx_i_jp - Vx_i_j) * dx;
            //SetBinContent(ix,iy,GetBinContent(ix,iy)-9);
            //SetBinContent(ix+1,iy,GetBinContent(ix+1,iy)+10);
            //SetBinContent(ix,iy+1,GetBinContent(ix,iy+1)-1);
            SetBinContent(ix,iy,curlint);
            sum += curlint;
         }
         else {
            SetBinContent(ix,iy,0);
         }
      }
   }
   return sum;
}

Map2D &Map2D::Uncurl(const Map2D *Vx, const Map2D *Vy, Int_t comp)
{
   // Computes a correction to a 2D vector field defined on a plane such
   // that the resulting field has zero curl.  This essentially replaces
   // one of the two degrees of freedom of the field with the constraint.
   // The argument "comp" tells which of the two components is replaced:
   //      comp = 0: return a replacement for Vx;
   //      comp = 1; return a replacement for Vy.
   // The read-only map arguments Vx,Vy are not modified.  
   //
   // Algorithm:
   // There are an infinite number of replacements for Vx,Vy such that
   // Del x (Vx,Vy) = 0, all of which are related to one another through
   // gauge transformations.  The purpose here is to modify the field in
   // as minimal a fashion as possible to eliminate the curl.  To simplify
   // the problem, consider only the subset of solutions that leaves Vx [Vy]
   // unchanged, as selected by the user.  This reduces the solution to
   // finding a set of independent columns [rows] that zeros the curl within
   // that column [row] subject to some criterion for minimal modification.
   // In 1D the zero-curl constrain reduces to a first-order differences
   // equation (first-order inhomogenous differential equation in 1D written
   // in finite-element form) which has just one overall undetermined offset.
   //  I chose the offset to minimize the sum of the squared differences
   // from the corresponding input map.  This uniquely determines the result.

   Int_t nxbins = GetXaxis()->GetNbins();
   Int_t nybins = GetYaxis()->GetNbins();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   if (Vx->GetXaxis()->GetNbins() != nxbins ||
       Vx->GetYaxis()->GetNbins() != nybins ||
       Vx->GetXaxis()->GetXmin() != xmin ||
       Vx->GetXaxis()->GetXmax() != xmax ||
       Vx->GetYaxis()->GetXmin() != ymin ||
       Vx->GetYaxis()->GetXmax() != ymax ||
       Vy->GetXaxis()->GetNbins() != nxbins ||
       Vy->GetYaxis()->GetNbins() != nybins ||
       Vy->GetXaxis()->GetXmin() != xmin ||
       Vy->GetXaxis()->GetXmax() != xmax ||
       Vy->GetYaxis()->GetXmin() != ymin ||
       Vy->GetYaxis()->GetXmax() != ymax) {
      std::cerr << "Error in Map2D::Uncurl - the input maps have"
                << " different pixel dimensions or axis bounds."
                << std::endl;
      return *this;
   }

   if (comp == 0) {
      for (Int_t ix=1; ix < nxbins; ++ix) {
         double sum0 = 0;
         double sum1 = 0;
         double Vxp[nybins];
         for (Int_t iy=1; iy < nybins; ++iy) {
            double Vx_i_j = Vx->GetBinContent(ix,iy);
            double Vy_i_j = Vy->GetBinContent(ix,iy);
            double Vx_i_jp = Vx->GetBinContent(ix,iy+1);
            double Vy_i_jp = Vy->GetBinContent(ix,iy+1);
            double Vx_ip_j = Vx->GetBinContent(ix+1,iy);
            double Vy_ip_j = Vy->GetBinContent(ix+1,iy);
            double Vx_ip_jp = Vx->GetBinContent(ix+1,iy+1);
            double Vy_ip_jp = Vy->GetBinContent(ix+1,iy+1);
            if (Vx_i_j != 0 && Vx_i_jp != 0 && Vx_ip_j != 0 && Vx_ip_jp != 0 &&
                Vy_i_j != 0 && Vy_i_jp != 0 && Vy_ip_j != 0 && Vy_ip_jp != 0)
            {
               if (Vxp[iy-1] == 0) {
                  Vxp[iy-1] = Vx_i_j;
                  ++sum0;
               }
               Vxp[iy] = Vxp[iy-1] + (Vy_ip_j - Vy_i_j) * dy/dx;
            }
            else {
               Vxp[iy] = 0;
            }
            sum1 += Vx_i_jp - Vxp[iy];
            ++sum0;
         }
         double offset = (sum0 > 0)? sum1/sum0 : 0;
         for (Int_t iy=1; iy <= nybins; ++iy) {
            if (Vxp[iy-1] != 0) {
               SetBinContent(ix,iy,Vxp[iy-1]+offset);
            }
            else {
               SetBinContent(ix,iy,0);
            }
         }
      }
   }
   else {
      for (Int_t iy=1; iy < nybins; ++iy) {
         double sum0 = 0;
         double sum1 = 0;
         double Vyp[nxbins];
         for (Int_t ix=1; ix < nxbins; ++ix) {
            double Vx_i_j = Vx->GetBinContent(ix,iy);
            double Vy_i_j = Vy->GetBinContent(ix,iy);
            double Vx_i_jp = Vx->GetBinContent(ix,iy+1);
            double Vy_i_jp = Vy->GetBinContent(ix,iy+1);
            double Vx_ip_j = Vx->GetBinContent(ix+1,iy);
            double Vy_ip_j = Vy->GetBinContent(ix+1,iy);
            double Vx_ip_jp = Vx->GetBinContent(ix+1,iy+1);
            double Vy_ip_jp = Vy->GetBinContent(ix+1,iy+1);
            if (Vx_i_j != 0 && Vx_i_jp != 0 && Vx_ip_j != 0 && Vx_ip_jp != 0 &&
                Vy_i_j != 0 && Vy_i_jp != 0 && Vy_ip_j != 0 && Vy_ip_jp != 0)
            {
               if (Vyp[ix-1] == 0) {
                  Vyp[ix-1] = Vy_i_j;
                  ++sum0;
               }
               Vyp[ix] = Vyp[ix-1] + (Vx_i_jp - Vx_i_j) * dx/dy;
               sum1 += Vy_ip_j - Vyp[ix];
               ++sum0;
            }
            else {
               Vyp[ix] = 0;
            }
         }
         double offset = (sum0 > 0)? sum1/sum0 : 0;
         for (Int_t ix=1; ix <= nxbins; ++ix) {
            if (Vyp[ix-1] != 0) {
               SetBinContent(ix,iy,Vyp[ix-1]+offset);
            }
            else {
               SetBinContent(ix,iy,0);
            }
         }
      }
   }
   return *this;
}

Double_t Map2D::Divergence(const Map2D *Vx, const Map2D *Vy)
{
   // Computes the divergence of the vector field V whose x,y components
   // are passed in as the maps Vx,Vy.  The contents of map *this are
   // overwritten with the result.  The return value is the total
   // divergence integrated over the output map, in units of V times
   // coordinate length.  The two maps given as arguments must have the
   // same dimensions and bounds in x and y.

   Int_t nxbins = Vx->GetXaxis()->GetNbins();
   Int_t nybins = Vx->GetYaxis()->GetNbins();
   Double_t xmin = Vx->GetXaxis()->GetXmin();
   Double_t xmax = Vx->GetXaxis()->GetXmax();
   Double_t ymin = Vx->GetYaxis()->GetXmin();
   Double_t ymax = Vx->GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   if (Vy->GetXaxis()->GetNbins() != nxbins ||
       Vy->GetYaxis()->GetNbins() != nybins ||
       Vy->GetXaxis()->GetXmin() != xmin ||
       Vy->GetXaxis()->GetXmax() != xmax ||
       Vy->GetYaxis()->GetXmin() != ymin ||
       Vy->GetYaxis()->GetXmax() != ymax) {
      std::cerr << "Error in Map2D::Divergence - the two input maps have"
                << " different pixel dimensions or axis bounds."
                << std::endl;
      return 0;
   }

   *this = *Vx;
   Double_t divsum = 0;
   for (Int_t iy=1; iy <= GetNbinsY(); ++iy) {
      for (Int_t ix=1; ix <= GetNbinsX(); ++ix) {
         if (ix > 1 && iy > 1) {
            Double_t Vx1 = Vx->GetBinContent(ix,iy);
            Double_t Vx0 = Vx->GetBinContent(ix-1,iy);
            Double_t Vy1 = Vy->GetBinContent(ix,iy);
            Double_t Vy0 = Vy->GetBinContent(ix,iy-1);
            if (Vx1*Vx0*Vy0*Vy1 != 0) {
               Double_t div = (Vx1-Vx0)/dx + (Vy1-Vy0)/dy;
               SetBinContent(ix,iy,div);
               divsum += div;
               continue;
            }
         }
         SetBinContent(ix,iy,0);
      }
   }
   return divsum*dx*dy;
}

Int_t Map2D::Gradient(Map2D *Vx, Map2D *Vy) const
{
   // Computes the gradient of the scalar field V in map *this and
   // stores the x,y components of the gradient in output maps Vx,Vy.
   // Arguments Vx,Vy must point to maps which have been created by the
   // user at input, otherwise the corresponding gradient component is not
   // computed.  At output these maps are overwritten by the computed
   // gradient components.  If both pointer arguments are zero at input
   // the routine does nothing and returns 0, otherwise it returns the
   // total number of pixels in the map *this for which the gradient vector
   // has been computed.
   
   if (Vx == 0 && Vy == 0) {
      return 0;
   }
   if (Vx) {
      *Vx = *this;
      Vx->Reset();
   }
   if (Vy) {
      *Vy = *this;
      Vy->Reset();
   }

   Double_t dx = GetXaxis()->GetBinWidth(1);
   Double_t dy = GetYaxis()->GetBinWidth(1);
   Int_t pixels=0;
   for (Int_t iy=1; iy < GetNbinsY(); ++iy) {
      for (Int_t ix=1; ix < GetNbinsX(); ++ix) {
         Double_t h00 = GetBinContent(ix,iy);
         Int_t count = 0;
         if (Vx) {
            Double_t h10 = GetBinContent(ix+1,iy);
            Double_t val = (h10*h00 != 0)? (h10-h00)/dx : 0;
            Vx->SetBinContent(ix,iy,val);
            count += (val != 0)? 1 : 0;
         }
         if (Vy) {
            Double_t h01 = GetBinContent(ix,iy+1);
            Double_t val = (h01*h00 != 0)? (h01-h00)/dy : 0;
            Vy->SetBinContent(ix,iy,val);
            count += (val != 0)? 1 : 0;
         }
         pixels += (count == 0)? 0 : 1;
      }
   }
   return pixels;
}

Int_t Map2D::PoissonSolve(const Map2D *rho, const Map2D *mask)
{
   // Solves the Poisson equation in two dimensions with a source given by
   // input map rho, subject to the boundary conditions contained in this
   // object at entry.  If input argument "mask" points to an existing object,
   // that object is interpreted as a mask for the pixels in map *this that
   // contain boundary values, otherwise all non-zero pixels in map *this
   // at input are considered to specify boundary values.  The algorithm
   // will make use of as many (or few) boundary values as the user provides,
   // although the solution may not satisfy all of them exactly if the
   // problem is over-determined, i.e. too many values are provided, or they
   // are clustered in too compact a region of the map.  In any case, the
   // solution will attempt to match all of the provided data points as
   // closely as possible, subject to the constraints of Poisson's equation
   // and the resolution of the map.  All cells in the map *this are over
   // written with the solution except those specifying boundary conditions.
   //
   // Return value:
   // The return value is the rank of the matrix that expresses LaPlace's
   // equation for the given boundary conditions.  The rank expresses the
   // effective number of independent boundary values that were used in
   // attempting the satisfy the stated boundary conditions.
   //
   // Algorithm:
   // First the Green's function for the 2D Poisson equation with a point
   // (single pixel) source is evaluated for each non-zero pixel in rho,
   // and the results are superimposed.  At this point, the solution obeys
   // Poisson's equation but with arbitrary boundary conditions. The map
   // is now subtracted from map *this that specify the boundary values
   // for the problem, and the resulting map is handed to LaplaceSolve()
   // to find the homogeneous part of the solution.  Adding the homogenous
   // and inhomogenous parts yields the full solution, which is returned
   // in this object.
   //
   // Timing:
   // The time required to complete this function increases quickly with
   // the dimensions of the map.  The following figures are for a single
   // Intel Core2 quad-core processor at 3.4GHz running a single thread.
   //
   //   t = (56 + 5.3e-8 Ndim^4.2 + 4.9e-9 Ndim^4.2) seconds
   //         ^    ^               ^
   //         |    |               |
   //         |    |                \.
   //         |    |                 \--- compute the homogeneous part
   //         |    |
   //         |     \--- compute the Greens function integral
   //         |
   //          \--- compute the Greens function (35x35 discrete kernel)
   //
   // Example: for a 1000 x 1000 pixel map, t = 229,000 seconds (64 hr)

#if REPORT_PROCESS_TIME
   struct timeval time_so_far;
   struct rusage resource_usage[9];
   getrusage(RUSAGE_SELF, &resource_usage[0]);
   printf("-- Entry to Map2D::PoissonSolve, starting the process clock.\n");
   printf("-- time to compute the Greens function for this grid: ");
#endif

   Int_t nxbins = rho->GetNbinsX();
   Int_t nybins = rho->GetNbinsY();
   Double_t xmin = rho->GetXaxis()->GetXmin();
   Double_t xmax = rho->GetXaxis()->GetXmax();
   Double_t ymin = rho->GetYaxis()->GetXmin();
   Double_t ymax = rho->GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   Double_t xrange = xmax-xmin-dx/2;
   Double_t yrange = ymax-ymin-dy/2;
   Map2D *G = new Map2D(TH2D("G","2D Green\'s Function",
                             2*nxbins-1,-xrange,xrange,
                             2*nybins-1,-yrange,yrange));
   G->GreensFunction(0,0);
   Int_t igx0 = nxbins;
   Int_t igy0 = nybins;

#if REPORT_PROCESS_TIME
   getrusage(RUSAGE_SELF, &resource_usage[1]);
   timersub(&resource_usage[1].ru_utime,
            &resource_usage[0].ru_utime,
            &time_so_far);
   printf("%f\n",time_so_far.tv_sec + time_so_far.tv_usec/1000000.);
   printf("-- time to do the Greens function integral for this grid: ");
#endif
   
   Map2D work(*rho);
   work.Reset();
   for (Int_t ix0=1; ix0 <= nxbins; ++ix0) {
      for (Int_t iy0=1; iy0 <= nybins; ++iy0) {
         Double_t q = rho->GetBinContent(ix0,iy0);
         if (q != 0) {
            for (Int_t ix=1; ix <= nxbins; ++ix) {
               for (Int_t iy=1; iy <= nybins; ++iy) {
                  Double_t sol = work.GetBinContent(ix,iy);
                  sol += q*G->GetBinContent(ix-ix0+igx0,iy-iy0+igy0);
                  work.SetBinContent(ix,iy,sol);
               }
            }
         }
      }
   }

#if REPORT_PROCESS_TIME
   getrusage(RUSAGE_SELF, &resource_usage[2]);
   timersub(&resource_usage[2].ru_utime,
            &resource_usage[1].ru_utime,
            &time_so_far);
   printf("%f\n",time_so_far.tv_sec + time_so_far.tv_usec/1000000.);
   printf("-- time to compute the homogeneous solution for this grid: ");
#endif

   Add(&work,-dx*dy);
   Int_t result = LaplaceSolve(mask);
   Add(&work,dx*dy);

#if REPORT_PROCESS_TIME
   getrusage(RUSAGE_SELF, &resource_usage[3]);
   timersub(&resource_usage[3].ru_utime,
            &resource_usage[2].ru_utime,
            &time_so_far);
   printf("%f\n",time_so_far.tv_sec + time_so_far.tv_usec/1000000.);
   printf("-- Exit from Map2D::PoissonSolve.\n");
#endif

   return result;
}

Double_t Map2D::PoissonIter(const Map2D *rho, const Map2D *mask)
{
   // Iterates the Poisson equation in two dimensions with a source given by
   // input map rho, subject to the boundary conditions contained in this
   // object at entry.  If input argument "mask" points to an existing object,
   // that object is interpreted as a mask for the pixels in map *this that
   // contain boundary values, otherwise all non-zero pixels in map *this
   // at input are considered to specify boundary values.  The algorithm
   // will make use of as many (or few) boundary values as the user provides,
   // although the solution may not satisfy all of them exactly if the
   // problem is over-determined, i.e. too many values are provided, or they
   // are clustered in too compact a region of the map.  In any case, the
   // solution will attempt to match all of the provided data points as
   // closely as possible, subject to the constraints of Poisson's equation
   // and the resolution of the map.  All cells in the map *this are over
   // written with the solution except those specifying boundary conditions.
   //
   // Return value:
   // The largest correction that was applied to any cell in the map in the
   // present iteration, or zero if no changes were made.
   //
   // Algorithm:
   // The following formula is applied to all cells except those with rho=0
   // and the ones that specify boundary conditions.  Boundary condition 
   // cells are identified by having a non-zero value in the corresponding
   // cell of the mask map, or if mask=0, by having non-zero values in the
   // *this map at entry.
   //
   //           / (n)    (n)   \    2   / (n)    (n)   \   2            2  2
   //          | u   +  u       | dy + | u   +  u       | dx  - rho   dx dy
   //  (n+1)    \ i-1,j  i+1,j /        \ i,j-1  i,j+1 /           i,j
   // u     =   -------------------------------------------------------------
   //  i,j                        /  2      2 \                            .
   //                          2 | dx  +  dy   |
   //                             \           /                            .
   //
   // Timing:
 
#if REPORT_PROCESS_TIME
   struct timeval time_so_far;
   struct rusage resource_usage[9];
   getrusage(RUSAGE_SELF, &resource_usage[0]);
#endif

   Int_t nxbins = rho->GetNbinsX();
   Int_t nybins = rho->GetNbinsY();
   Double_t xmin = rho->GetXaxis()->GetXmin();
   Double_t xmax = rho->GetXaxis()->GetXmax();
   Double_t ymin = rho->GetYaxis()->GetXmin();
   Double_t ymax = rho->GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   Double_t dx2 = dx*dx;
   Double_t dy2 = dy*dy;
   Double_t dx2dy2 = dx2*dy2;

   Map2D delta(TH2D("delta","delta",nxbins,xmin,xmax,nybins,ymin,ymax));

   for (int ix=2; ix < nxbins; ++ix) {
      for (int iy=2; iy < nybins; ++iy) {
         double rho_i_j = (rho == 0)? 0 : rho->GetBinContent(ix,iy);
         if (rho_i_j == 0) {
            continue;
         }
         double u_i_j = GetBinContent(ix,iy);
         double u_im1_j = GetBinContent(ix-1,iy);
         double u_ip1_j = GetBinContent(ix+1,iy);
         double u_i_jm1 = GetBinContent(ix,iy-1);
         double u_i_jp1 = GetBinContent(ix,iy+1);
         if (mask != 0 && mask->GetBinContent(ix,iy) == 0 || 
             mask == 0 && GetBinContent(ix,iy) == 0)
         {
            double new_u_i_j = (u_im1_j + u_ip1_j) * dy2
                             + (u_i_jm1 + u_i_jp1) * dx2
                             - rho_i_j * dx2dy2;
            new_u_i_j /= 2 * (dx2 + dy2);
            if (u_im1_j * u_ip1_j * u_i_jm1 * u_i_jp1 != 0) {
               delta.SetBinContent(ix,iy, new_u_i_j - u_i_j);
            }
         }
      }
   }

   Add(&delta);

#if REPORT_PROCESS_TIME
   getrusage(RUSAGE_SELF, &resource_usage[1]);
   timersub(&resource_usage[1].ru_utime,
            &resource_usage[0].ru_utime,
            &time_so_far);
   printf("Map2D::PoissonIter used %f cpu seconds\n",
          time_so_far.tv_sec + time_so_far.tv_usec/1000000.);
#endif

   double dmax = fabs(delta.GetMaximum());
   double dmin = fabs(delta.GetMinimum());
   return (dmax > dmin)? dmax : dmin;
}

Bool_t Map2D::GreensFunction(Double_t x0, Double_t y0)
{
   //  The Green's function for the 2D Poisson equation can be written most
   //  compactly as follows,
   //
   //                             1        |      2        2 |
   //             G(x,y;x ,y ) = ----  log |(x-x ) + (y-y )  |
   //                    0  0    4 pi      |    0        0   |
   //
   // where x0,y0 is the location of the source.  In two dimensions it is
   // not possible to set the boundary conditions for G->0 at x0,y0->infinity
   // so instead one choses that G=0 at some finite radius R.  The expression
   // above has been written with R=1, but adding a constant term can be used
   // to shift R to an arbitrary radius, except for the singular values 0 and
   // infinity.  The result is loaded into this map, overwriting any contents
   // that were present at input, but leaving the dimensions and bounds of
   // the original map unchanged.  The point x0,y0 is assumed to lie somewhere
   // within the bounds of the map.
   //
   // This is the correct expression for the continuum case, but it diverges
   // as x,y -> x0,y0, and has large discretization errors for pixels nearby.
   // For this reason, I take a NxN pixel square region centered around x0,y0
   // and replace the computed value of G for these pixels with the solution
   // to the discrete NxN matrix Poisson equation on a square grid, subject
   // to the boundary condition that the cells around the outer perimeter of
   // the NxN pixel block match onto the above continuum formula.  By chosing
   // N to be sufficiently large, the residual discretization errors are kept
   // to a negligible level.

   Int_t nxbins = GetNbinsX();
   Int_t nybins = GetNbinsY();
   Double_t xmin = GetXaxis()->GetXmin();
   Double_t xmax = GetXaxis()->GetXmax();
   Double_t ymin = GetYaxis()->GetXmin();
   Double_t ymax = GetYaxis()->GetXmax();
   Double_t dx = (xmax-xmin)/nxbins;
   Double_t dy = (ymax-ymin)/nybins;
   Int_t ix0 = int((x0-xmin)/dx)+1;
   Int_t iy0 = int((y0-ymin)/dy)+1;
   Double_t fourpi=8*acos(0);
   for (Int_t ix=1; ix <= nxbins; ++ix) {
      Double_t x = xmin+(ix-0.5)*dx-x0;
      for (Int_t iy=1; iy <= nybins; ++iy) {
         Double_t y = ymin+(iy-0.5)*dy-y0;
         Double_t r2 = x*x+y*y+1e-50;
         SetBinContent(ix,iy,log(r2)/fourpi);
      }
   }

   // Cut out a square region around the source and solve the discrete problem

   Int_t Ndim = 35;
   Int_t ix1 = ix0-Ndim/2;
   Int_t iy1 = iy0-Ndim/2;
   if (ix0 < 3 || ix0 > nxbins-4 ||
       iy0 < 3 || ix0 > nxbins-4) {
      std::cerr << "Map2D::GreensFunction error - computing the Green\'s"
                << " function with x0,y0 too close to the edge of the map,"
                << " cannot continue."
                << std::endl;
      return false;
   }
   else if (ix1 < 0 || ix1+Ndim >= nxbins ||
            iy1 < 0 || iy1+Ndim >= nybins) {
      std::cerr << "Map2D::GreensFunction warning - computing the Green\'s"
                << " function with x0,y0 close to the edge of the map,"
                << " loss of precision will result."
                << std::endl;
      Int_t margin=0;
      margin = (-ix1 > margin)? -ix1 : margin;
      margin = (-iy1 > margin)? -iy1 : margin;
      margin = (ix1+Ndim-nxbins >= margin)? ix1+Ndim-nxbins+1 : margin;
      margin = (iy1+Ndim-nybins >= margin)? iy1+Ndim-nybins+1 : margin;
      Ndim -= margin;
      ix1 = ix0-Ndim/2;
      iy1 = iy0-Ndim/2;
   }

   // Set up the discretized Poisson equation in the form
   //           A x = b
   // where A is a square matrix and x,b are column vectors of the same
   // dimension as A.  There is one row in this equation for each pixel
   // in a NxN pixel block surrounding the one pixel at ix0,iy0 where the
   // source is located.  The (N-2)*(N-2) rows in A corresponding to the
   // interior pixels of the block contain the discrete approximation to
   // the divergence operator.  For the 4N boundary pixels, A contains
   // just a 1 on the diagonal to enforce the constraints that the boundary
   // pixels should match the values computed above using the continuum
   // formula.  The b vector contains zero for all of the interior pixels
   // except the one at ix0,iy0 which is set to 1/(dx*dy), the discrete
   // approximation to the Dirac delta function in two dimensions.  For
   // the boundary pixels, b is set to the values computed using the
   // continuum formula.

   TMatrixD A(Ndim*Ndim,Ndim*Ndim);
   TVectorD b(Ndim*Ndim);
   A.Zero();
   Int_t eq=0;
   for (Int_t ix=0; ix < Ndim; ++ix) {
      for (Int_t iy=0; iy < Ndim; ++iy,++eq) {
         Int_t ci = iy*Ndim+ix;
         if (ix == 0 || ix == Ndim-1 || iy == 0 || iy == Ndim-1) {
            A(eq,ci) = 1;
            b(eq) = GetBinContent(ix+ix1,iy+iy1);
         }
         else {
            A(eq,ci) = -2/(dx*dx)-2/(dy*dy);
            A(eq,ci-Ndim) = 1/(dy*dy);
            A(eq,ci+Ndim) = 1/(dy*dy);
            A(eq,ci-1) = 1/(dx*dx);
            A(eq,ci+1) = 1/(dx*dx);
            b(eq) = (ix+ix1 == ix0 && iy+iy1 == iy0)? 1/(dx*dy) : 0;
         }
      }
   }

   // Use singular value decomposition to solve

   TDecompSVD svd(A);
   if (!svd.Decompose()) {
      std::cerr << "Map2D::GreensFunction error - TDecompSVD method"
                << " Decompose failed, unable to continue."
                << std::endl;
      return false;
   }
   svd.Solve(b);
   for (Int_t ix=0; ix < Ndim; ++ix) {
      for (Int_t iy=0; iy < Ndim; ++iy) {
         Int_t ci = iy*Ndim+ix;
         SetBinContent(ix+ix1,iy+iy1,b(ci));
      }
   }
   return true;
}

Int_t Map2D::LaplaceSolve(const Map2D *mask)
{
   // Solves the Laplace equation in two dimensions, subject to the boundary
   // conditions contained in this object at entry.  If input argument "mask"
   // points to an existing object, that object is interpreted as a mask for
   // the pixels in map *this that contain boundary values, otherwise all
   // non-zero pixels in map *this at input are considered to specify
   // boundary values.  The algorithm will make use of as many (or few)
   // boundary values as the user provides, although the solution may not
   // satisfy all of them exactly if the problem is over-determined, i.e.
   // too many values are provided, or they are clustered in too compact a
   // region of the map.  In any case, the solution will attempt to match all
   // of the provided data points as closely as possible, subject to the
   // constraints of the Laplace equation and the resolution of the map.
   //  All cells in the map *this are over written with the solution.
   //
   // Algorithm:
   // The problem is solved in the basis of solutions to Laplace's equation
   // in polar coordinates.  Here f_n is a stackable column vector of two
   // basis functions
   //
   //                             /                              .
   //                         m  |  cos(m phi)
   //            f (x,y) =   r  < 
   //             m              |  sin(m phi)
   //                             \                              .
   //
   // There is a second set of solutions that are singular at the origin,
   // but these are excluded because the interior region of the map is
   // assumed to include the origin, taken to lie at the geometric center
   // of the map.  Note that for n=0 the second element of f_n is identically
   // zero.  To form a column vector of basis functions F_i, imagine a stack
   // of f_m column vectors starting with m=0 at the top, and increasing
   // downward until the number N of elements in F equals at least M, the
   // number the pixels (x_j,y_j), j=0..M that are provided as boundary
   // values in the input map.  This results in a large MxN matrix A_ji
   // such that
   //                   A     = F ( x , y )
   //                    j,i     i   j   j
   //
   // Solving Laplace's equation now amounts to solving the linear equation
   //
   //                   A    C  = z
   //                    j,i  i    j
   //
   // for unknown coefficients C_i in terms of initial values z_j for pixel j.
   // In general A is not a square matrix, and has many singular values.  I
   // solve this equation using singular value decomposition, and identify
   // the rank of the matrix A, which is passed as the return value from this
   // method.  The results of the singular value decomposition are used to
   // filter the given values z_j into the unknown coefficients C_i, which 
   // are then used to fill the map with the optimal solution to the boundary
   // value problem.
   //
   // The user should pay careful attention to this value, as it may
   // indicate that the problem is ill-posed.  In general, one expects that
   // the rank will be close to the number of boundary values provided.  It
   // will never be greater.  If the rank is significantly less than the
   // number of boundary values provided, it may indicate that the boundary
   // values over-determine the solution, or it may simply mean that fewer
   // basis functions are needed to describe the solution than the number
   // of boundary values would naively suggest.

   class basis_function {
    public:

      basis_function(const Map2D *map)
      {
         fNxbins = map->GetNbinsX();
         fNybins = map->GetNbinsY();
         fXlow = map->GetXaxis()->GetXmin();
         fXhigh = map->GetXaxis()->GetXmax();
         fYlow = map->GetYaxis()->GetXmin();
         fYhigh = map->GetYaxis()->GetXmax();
         fXwidth = fXhigh-fXlow;
         fYwidth = fYhigh-fYlow;
         fXcenter = (fXhigh+fXlow)/2;
         fYcenter = (fYhigh+fYlow)/2;
         fXdelta = (fXhigh-fXlow)/fNxbins;
         fYdelta = (fYhigh-fYlow)/fNybins;
         fRscale2 = (fXwidth*fXwidth + fYwidth*fYwidth)/4;
      }

      Double_t F(Int_t i, Double_t x, Double_t y)
      {
         x -= fXcenter;
         y -= fYcenter;
         Double_t r2 = (x*x+y*y)/fRscale2;
         Double_t phi = atan2(y,x);
         Int_t m = (i+1)/2;
         switch (i-m*2) {
          case 0:
            return pow(r2,m/2.) * cos(m*phi);
          case -1:
            return pow(r2,m/2.) * sin(m*phi);
          default:
            std::cerr << "Map2D::LaplaceSolve::basis_function error - "
                      << "method F called with negative index i = "
                      << i << std::endl;
            return 0;
         }
      }

      Double_t F(Int_t i, Int_t ix, Int_t iy)
      {
         return F(i,(ix-0.5)*fXdelta+fXlow,(iy-0.5)*fYdelta+fYlow);
      }

    private:
      Int_t fNxbins;
      Int_t fNybins;
      Double_t fXlow;
      Double_t fXhigh;
      Double_t fYlow;
      Double_t fYhigh;
      Double_t fXwidth;
      Double_t fYwidth;
      Double_t fXcenter;
      Double_t fYcenter;
      Double_t fXdelta;
      Double_t fYdelta;
      Double_t fRscale2;
   };

   Int_t nxbins = GetNbinsX();
   Int_t nybins = GetNbinsY();

   // Make a list of input boundary values

   std::vector<Double_t> bX,bY,bValue;
   std::vector<Int_t> iX,iY;
   for (Int_t ix=1; ix <= nxbins; ++ix) {
      for (Int_t iy=1; iy <= nybins; ++iy) {
         if (mask != 0 && mask->GetBinContent(ix,iy) != 0 ||
             mask == 0 && GetBinContent(ix,iy) != 0)
         {
            iX.push_back(ix);
            iY.push_back(iy);
            bX.push_back(GetXaxis()->GetBinCenter(ix));
            bY.push_back(GetYaxis()->GetBinCenter(iy));
            bValue.push_back(GetBinContent(ix,iy));
         }
      }
   }

   // Fill the A matrix with basis functions evaluated at bValue points

   basis_function f(this);
   Int_t Mvalues = bValue.size();
   Int_t Nbases = Mvalues;
   Mvalues = (Mvalues < Nbases)? Nbases : Mvalues;
   TMatrixD A(Mvalues,Nbases);
   TVectorD b(Mvalues);
   for (UInt_t m=0; m < bValue.size(); ++m) {
      for (Int_t n=0; n < Nbases; ++n) {
         A(m,n) = f.F(n,bX[m],bY[m]);
      }
      b(m) = bValue[m];
   }

   // Add null rows and bValue points to bring Mvalues up to at least Nbases

   for (Int_t m=bValue.size(); m < Mvalues; ++m) {
      for (Int_t n=0; n < Nbases; ++n) {
         A(m,n) = 0;
      }
      b(m) = 0;
   }

   // Compute the SVD of A and compute its rank

   TDecompSVD svd(A);
   if (!svd.Decompose()) {
      std::cerr << "Map2D::LaplaceSolve error - TDecompSVD method"
                << " Decompose failed, unable to continue."
                << std::endl;
      return 0;
   }
   svd.Solve(b);
   TVectorD sig = svd.GetSig();
   Int_t rank;
   for (rank=0; rank < sig.GetNrows(); ++rank) {
      if (sig(rank) < 1e-300) {
         break;
      }
   }

   // Overwrite the map with this solution

   for (Int_t ix=1; ix <= nxbins; ++ix) {
      for (Int_t iy=1; iy <= nybins; ++iy) {
         Double_t val=0;
         for (Int_t n=0; n < Nbases; ++n) {
            val += b(n)*f.F(n,ix,iy);
         }
         SetBinContent(ix,iy,val);
      }
   }

   for (unsigned int m=0; m < bValue.size(); ++m) {
      double val=0;
      for (int n=0; n < Nbases; ++n) {
         val += b(n)*f.F(n,bX[m],bY[m]);
      }
      printf("point %d: input %f output %f\n",m,bValue[m],val);
   }

   return rank;
}

TH1D &Map2D::Profile(const char *name, const char *title,
                     Int_t nbins, Double_t xlow, Double_t xhigh)
{
   // Scans over the map and forms a histogram of the cell heights,
   // omitting the cells that contain zero.

   if (xlow == 999) {
      xlow = 1e30;
      for (Int_t ix=1; ix <= GetNbinsX(); ++ix) {
         for (Int_t iy=1; iy <= GetNbinsY(); ++iy) {
            double cont = GetBinContent(ix,iy);
            if (cont == 0) {
               continue;
            }
            xlow = (cont < xlow)? cont : xlow;
	 }
      }
   }
   if (xhigh == 999) {
      xhigh = -1e30;
      for (Int_t ix=1; ix <= GetNbinsX(); ++ix) {
         for (Int_t iy=1; iy <= GetNbinsY(); ++iy) {
            double cont = GetBinContent(ix,iy);
            if (cont == 0) {
               continue;
            }
            xhigh = (cont > xhigh)? cont : xhigh;
	 }
      }
   }

   TH1D *prof = new TH1D(name,title,nbins,xlow,xhigh);
   for (Int_t ix=1; ix <= GetNbinsX(); ++ix) {
      for (Int_t iy=1; iy <= GetNbinsY(); ++iy) {
         double cont = GetBinContent(ix,iy);
         if (cont) {
            prof->Fill(cont);
         }
      }
   }
   return *prof;
}
 Map2D.cc:1
 Map2D.cc:2
 Map2D.cc:3
 Map2D.cc:4
 Map2D.cc:5
 Map2D.cc:6
 Map2D.cc:7
 Map2D.cc:8
 Map2D.cc:9
 Map2D.cc:10
 Map2D.cc:11
 Map2D.cc:12
 Map2D.cc:13
 Map2D.cc:14
 Map2D.cc:15
 Map2D.cc:16
 Map2D.cc:17
 Map2D.cc:18
 Map2D.cc:19
 Map2D.cc:20
 Map2D.cc:21
 Map2D.cc:22
 Map2D.cc:23
 Map2D.cc:24
 Map2D.cc:25
 Map2D.cc:26
 Map2D.cc:27
 Map2D.cc:28
 Map2D.cc:29
 Map2D.cc:30
 Map2D.cc:31
 Map2D.cc:32
 Map2D.cc:33
 Map2D.cc:34
 Map2D.cc:35
 Map2D.cc:36
 Map2D.cc:37
 Map2D.cc:38
 Map2D.cc:39
 Map2D.cc:40
 Map2D.cc:41
 Map2D.cc:42
 Map2D.cc:43
 Map2D.cc:44
 Map2D.cc:45
 Map2D.cc:46
 Map2D.cc:47
 Map2D.cc:48
 Map2D.cc:49
 Map2D.cc:50
 Map2D.cc:51
 Map2D.cc:52
 Map2D.cc:53
 Map2D.cc:54
 Map2D.cc:55
 Map2D.cc:56
 Map2D.cc:57
 Map2D.cc:58
 Map2D.cc:59
 Map2D.cc:60
 Map2D.cc:61
 Map2D.cc:62
 Map2D.cc:63
 Map2D.cc:64
 Map2D.cc:65
 Map2D.cc:66
 Map2D.cc:67
 Map2D.cc:68
 Map2D.cc:69
 Map2D.cc:70
 Map2D.cc:71
 Map2D.cc:72
 Map2D.cc:73
 Map2D.cc:74
 Map2D.cc:75
 Map2D.cc:76
 Map2D.cc:77
 Map2D.cc:78
 Map2D.cc:79
 Map2D.cc:80
 Map2D.cc:81
 Map2D.cc:82
 Map2D.cc:83
 Map2D.cc:84
 Map2D.cc:85
 Map2D.cc:86
 Map2D.cc:87
 Map2D.cc:88
 Map2D.cc:89
 Map2D.cc:90
 Map2D.cc:91
 Map2D.cc:92
 Map2D.cc:93
 Map2D.cc:94
 Map2D.cc:95
 Map2D.cc:96
 Map2D.cc:97
 Map2D.cc:98
 Map2D.cc:99
 Map2D.cc:100
 Map2D.cc:101
 Map2D.cc:102
 Map2D.cc:103
 Map2D.cc:104
 Map2D.cc:105
 Map2D.cc:106
 Map2D.cc:107
 Map2D.cc:108
 Map2D.cc:109
 Map2D.cc:110
 Map2D.cc:111
 Map2D.cc:112
 Map2D.cc:113
 Map2D.cc:114
 Map2D.cc:115
 Map2D.cc:116
 Map2D.cc:117
 Map2D.cc:118
 Map2D.cc:119
 Map2D.cc:120
 Map2D.cc:121
 Map2D.cc:122
 Map2D.cc:123
 Map2D.cc:124
 Map2D.cc:125
 Map2D.cc:126
 Map2D.cc:127
 Map2D.cc:128
 Map2D.cc:129
 Map2D.cc:130
 Map2D.cc:131
 Map2D.cc:132
 Map2D.cc:133
 Map2D.cc:134
 Map2D.cc:135
 Map2D.cc:136
 Map2D.cc:137
 Map2D.cc:138
 Map2D.cc:139
 Map2D.cc:140
 Map2D.cc:141
 Map2D.cc:142
 Map2D.cc:143
 Map2D.cc:144
 Map2D.cc:145
 Map2D.cc:146
 Map2D.cc:147
 Map2D.cc:148
 Map2D.cc:149
 Map2D.cc:150
 Map2D.cc:151
 Map2D.cc:152
 Map2D.cc:153
 Map2D.cc:154
 Map2D.cc:155
 Map2D.cc:156
 Map2D.cc:157
 Map2D.cc:158
 Map2D.cc:159
 Map2D.cc:160
 Map2D.cc:161
 Map2D.cc:162
 Map2D.cc:163
 Map2D.cc:164
 Map2D.cc:165
 Map2D.cc:166
 Map2D.cc:167
 Map2D.cc:168
 Map2D.cc:169
 Map2D.cc:170
 Map2D.cc:171
 Map2D.cc:172
 Map2D.cc:173
 Map2D.cc:174
 Map2D.cc:175
 Map2D.cc:176
 Map2D.cc:177
 Map2D.cc:178
 Map2D.cc:179
 Map2D.cc:180
 Map2D.cc:181
 Map2D.cc:182
 Map2D.cc:183
 Map2D.cc:184
 Map2D.cc:185
 Map2D.cc:186
 Map2D.cc:187
 Map2D.cc:188
 Map2D.cc:189
 Map2D.cc:190
 Map2D.cc:191
 Map2D.cc:192
 Map2D.cc:193
 Map2D.cc:194
 Map2D.cc:195
 Map2D.cc:196
 Map2D.cc:197
 Map2D.cc:198
 Map2D.cc:199
 Map2D.cc:200
 Map2D.cc:201
 Map2D.cc:202
 Map2D.cc:203
 Map2D.cc:204
 Map2D.cc:205
 Map2D.cc:206
 Map2D.cc:207
 Map2D.cc:208
 Map2D.cc:209
 Map2D.cc:210
 Map2D.cc:211
 Map2D.cc:212
 Map2D.cc:213
 Map2D.cc:214
 Map2D.cc:215
 Map2D.cc:216
 Map2D.cc:217
 Map2D.cc:218
 Map2D.cc:219
 Map2D.cc:220
 Map2D.cc:221
 Map2D.cc:222
 Map2D.cc:223
 Map2D.cc:224
 Map2D.cc:225
 Map2D.cc:226
 Map2D.cc:227
 Map2D.cc:228
 Map2D.cc:229
 Map2D.cc:230
 Map2D.cc:231
 Map2D.cc:232
 Map2D.cc:233
 Map2D.cc:234
 Map2D.cc:235
 Map2D.cc:236
 Map2D.cc:237
 Map2D.cc:238
 Map2D.cc:239
 Map2D.cc:240
 Map2D.cc:241
 Map2D.cc:242
 Map2D.cc:243
 Map2D.cc:244
 Map2D.cc:245
 Map2D.cc:246
 Map2D.cc:247
 Map2D.cc:248
 Map2D.cc:249
 Map2D.cc:250
 Map2D.cc:251
 Map2D.cc:252
 Map2D.cc:253
 Map2D.cc:254
 Map2D.cc:255
 Map2D.cc:256
 Map2D.cc:257
 Map2D.cc:258
 Map2D.cc:259
 Map2D.cc:260
 Map2D.cc:261
 Map2D.cc:262
 Map2D.cc:263
 Map2D.cc:264
 Map2D.cc:265
 Map2D.cc:266
 Map2D.cc:267
 Map2D.cc:268
 Map2D.cc:269
 Map2D.cc:270
 Map2D.cc:271
 Map2D.cc:272
 Map2D.cc:273
 Map2D.cc:274
 Map2D.cc:275
 Map2D.cc:276
 Map2D.cc:277
 Map2D.cc:278
 Map2D.cc:279
 Map2D.cc:280
 Map2D.cc:281
 Map2D.cc:282
 Map2D.cc:283
 Map2D.cc:284
 Map2D.cc:285
 Map2D.cc:286
 Map2D.cc:287
 Map2D.cc:288
 Map2D.cc:289
 Map2D.cc:290
 Map2D.cc:291
 Map2D.cc:292
 Map2D.cc:293
 Map2D.cc:294
 Map2D.cc:295
 Map2D.cc:296
 Map2D.cc:297
 Map2D.cc:298
 Map2D.cc:299
 Map2D.cc:300
 Map2D.cc:301
 Map2D.cc:302
 Map2D.cc:303
 Map2D.cc:304
 Map2D.cc:305
 Map2D.cc:306
 Map2D.cc:307
 Map2D.cc:308
 Map2D.cc:309
 Map2D.cc:310
 Map2D.cc:311
 Map2D.cc:312
 Map2D.cc:313
 Map2D.cc:314
 Map2D.cc:315
 Map2D.cc:316
 Map2D.cc:317
 Map2D.cc:318
 Map2D.cc:319
 Map2D.cc:320
 Map2D.cc:321
 Map2D.cc:322
 Map2D.cc:323
 Map2D.cc:324
 Map2D.cc:325
 Map2D.cc:326
 Map2D.cc:327
 Map2D.cc:328
 Map2D.cc:329
 Map2D.cc:330
 Map2D.cc:331
 Map2D.cc:332
 Map2D.cc:333
 Map2D.cc:334
 Map2D.cc:335
 Map2D.cc:336
 Map2D.cc:337
 Map2D.cc:338
 Map2D.cc:339
 Map2D.cc:340
 Map2D.cc:341
 Map2D.cc:342
 Map2D.cc:343
 Map2D.cc:344
 Map2D.cc:345
 Map2D.cc:346
 Map2D.cc:347
 Map2D.cc:348
 Map2D.cc:349
 Map2D.cc:350
 Map2D.cc:351
 Map2D.cc:352
 Map2D.cc:353
 Map2D.cc:354
 Map2D.cc:355
 Map2D.cc:356
 Map2D.cc:357
 Map2D.cc:358
 Map2D.cc:359
 Map2D.cc:360
 Map2D.cc:361
 Map2D.cc:362
 Map2D.cc:363
 Map2D.cc:364
 Map2D.cc:365
 Map2D.cc:366
 Map2D.cc:367
 Map2D.cc:368
 Map2D.cc:369
 Map2D.cc:370
 Map2D.cc:371
 Map2D.cc:372
 Map2D.cc:373
 Map2D.cc:374
 Map2D.cc:375
 Map2D.cc:376
 Map2D.cc:377
 Map2D.cc:378
 Map2D.cc:379
 Map2D.cc:380
 Map2D.cc:381
 Map2D.cc:382
 Map2D.cc:383
 Map2D.cc:384
 Map2D.cc:385
 Map2D.cc:386
 Map2D.cc:387
 Map2D.cc:388
 Map2D.cc:389
 Map2D.cc:390
 Map2D.cc:391
 Map2D.cc:392
 Map2D.cc:393
 Map2D.cc:394
 Map2D.cc:395
 Map2D.cc:396
 Map2D.cc:397
 Map2D.cc:398
 Map2D.cc:399
 Map2D.cc:400
 Map2D.cc:401
 Map2D.cc:402
 Map2D.cc:403
 Map2D.cc:404
 Map2D.cc:405
 Map2D.cc:406
 Map2D.cc:407
 Map2D.cc:408
 Map2D.cc:409
 Map2D.cc:410
 Map2D.cc:411
 Map2D.cc:412
 Map2D.cc:413
 Map2D.cc:414
 Map2D.cc:415
 Map2D.cc:416
 Map2D.cc:417
 Map2D.cc:418
 Map2D.cc:419
 Map2D.cc:420
 Map2D.cc:421
 Map2D.cc:422
 Map2D.cc:423
 Map2D.cc:424
 Map2D.cc:425
 Map2D.cc:426
 Map2D.cc:427
 Map2D.cc:428
 Map2D.cc:429
 Map2D.cc:430
 Map2D.cc:431
 Map2D.cc:432
 Map2D.cc:433
 Map2D.cc:434
 Map2D.cc:435
 Map2D.cc:436
 Map2D.cc:437
 Map2D.cc:438
 Map2D.cc:439
 Map2D.cc:440
 Map2D.cc:441
 Map2D.cc:442
 Map2D.cc:443
 Map2D.cc:444
 Map2D.cc:445
 Map2D.cc:446
 Map2D.cc:447
 Map2D.cc:448
 Map2D.cc:449
 Map2D.cc:450
 Map2D.cc:451
 Map2D.cc:452
 Map2D.cc:453
 Map2D.cc:454
 Map2D.cc:455
 Map2D.cc:456
 Map2D.cc:457
 Map2D.cc:458
 Map2D.cc:459
 Map2D.cc:460
 Map2D.cc:461
 Map2D.cc:462
 Map2D.cc:463
 Map2D.cc:464
 Map2D.cc:465
 Map2D.cc:466
 Map2D.cc:467
 Map2D.cc:468
 Map2D.cc:469
 Map2D.cc:470
 Map2D.cc:471
 Map2D.cc:472
 Map2D.cc:473
 Map2D.cc:474
 Map2D.cc:475
 Map2D.cc:476
 Map2D.cc:477
 Map2D.cc:478
 Map2D.cc:479
 Map2D.cc:480
 Map2D.cc:481
 Map2D.cc:482
 Map2D.cc:483
 Map2D.cc:484
 Map2D.cc:485
 Map2D.cc:486
 Map2D.cc:487
 Map2D.cc:488
 Map2D.cc:489
 Map2D.cc:490
 Map2D.cc:491
 Map2D.cc:492
 Map2D.cc:493
 Map2D.cc:494
 Map2D.cc:495
 Map2D.cc:496
 Map2D.cc:497
 Map2D.cc:498
 Map2D.cc:499
 Map2D.cc:500
 Map2D.cc:501
 Map2D.cc:502
 Map2D.cc:503
 Map2D.cc:504
 Map2D.cc:505
 Map2D.cc:506
 Map2D.cc:507
 Map2D.cc:508
 Map2D.cc:509
 Map2D.cc:510
 Map2D.cc:511
 Map2D.cc:512
 Map2D.cc:513
 Map2D.cc:514
 Map2D.cc:515
 Map2D.cc:516
 Map2D.cc:517
 Map2D.cc:518
 Map2D.cc:519
 Map2D.cc:520
 Map2D.cc:521
 Map2D.cc:522
 Map2D.cc:523
 Map2D.cc:524
 Map2D.cc:525
 Map2D.cc:526
 Map2D.cc:527
 Map2D.cc:528
 Map2D.cc:529
 Map2D.cc:530
 Map2D.cc:531
 Map2D.cc:532
 Map2D.cc:533
 Map2D.cc:534
 Map2D.cc:535
 Map2D.cc:536
 Map2D.cc:537
 Map2D.cc:538
 Map2D.cc:539
 Map2D.cc:540
 Map2D.cc:541
 Map2D.cc:542
 Map2D.cc:543
 Map2D.cc:544
 Map2D.cc:545
 Map2D.cc:546
 Map2D.cc:547
 Map2D.cc:548
 Map2D.cc:549
 Map2D.cc:550
 Map2D.cc:551
 Map2D.cc:552
 Map2D.cc:553
 Map2D.cc:554
 Map2D.cc:555
 Map2D.cc:556
 Map2D.cc:557
 Map2D.cc:558
 Map2D.cc:559
 Map2D.cc:560
 Map2D.cc:561
 Map2D.cc:562
 Map2D.cc:563
 Map2D.cc:564
 Map2D.cc:565
 Map2D.cc:566
 Map2D.cc:567
 Map2D.cc:568
 Map2D.cc:569
 Map2D.cc:570
 Map2D.cc:571
 Map2D.cc:572
 Map2D.cc:573
 Map2D.cc:574
 Map2D.cc:575
 Map2D.cc:576
 Map2D.cc:577
 Map2D.cc:578
 Map2D.cc:579
 Map2D.cc:580
 Map2D.cc:581
 Map2D.cc:582
 Map2D.cc:583
 Map2D.cc:584
 Map2D.cc:585
 Map2D.cc:586
 Map2D.cc:587
 Map2D.cc:588
 Map2D.cc:589
 Map2D.cc:590
 Map2D.cc:591
 Map2D.cc:592
 Map2D.cc:593
 Map2D.cc:594
 Map2D.cc:595
 Map2D.cc:596
 Map2D.cc:597
 Map2D.cc:598
 Map2D.cc:599
 Map2D.cc:600
 Map2D.cc:601
 Map2D.cc:602
 Map2D.cc:603
 Map2D.cc:604
 Map2D.cc:605
 Map2D.cc:606
 Map2D.cc:607
 Map2D.cc:608
 Map2D.cc:609
 Map2D.cc:610
 Map2D.cc:611
 Map2D.cc:612
 Map2D.cc:613
 Map2D.cc:614
 Map2D.cc:615
 Map2D.cc:616
 Map2D.cc:617
 Map2D.cc:618
 Map2D.cc:619
 Map2D.cc:620
 Map2D.cc:621
 Map2D.cc:622
 Map2D.cc:623
 Map2D.cc:624
 Map2D.cc:625
 Map2D.cc:626
 Map2D.cc:627
 Map2D.cc:628
 Map2D.cc:629
 Map2D.cc:630
 Map2D.cc:631
 Map2D.cc:632
 Map2D.cc:633
 Map2D.cc:634
 Map2D.cc:635
 Map2D.cc:636
 Map2D.cc:637
 Map2D.cc:638
 Map2D.cc:639
 Map2D.cc:640
 Map2D.cc:641
 Map2D.cc:642
 Map2D.cc:643
 Map2D.cc:644
 Map2D.cc:645
 Map2D.cc:646
 Map2D.cc:647
 Map2D.cc:648
 Map2D.cc:649
 Map2D.cc:650
 Map2D.cc:651
 Map2D.cc:652
 Map2D.cc:653
 Map2D.cc:654
 Map2D.cc:655
 Map2D.cc:656
 Map2D.cc:657
 Map2D.cc:658
 Map2D.cc:659
 Map2D.cc:660
 Map2D.cc:661
 Map2D.cc:662
 Map2D.cc:663
 Map2D.cc:664
 Map2D.cc:665
 Map2D.cc:666
 Map2D.cc:667
 Map2D.cc:668
 Map2D.cc:669
 Map2D.cc:670
 Map2D.cc:671
 Map2D.cc:672
 Map2D.cc:673
 Map2D.cc:674
 Map2D.cc:675
 Map2D.cc:676
 Map2D.cc:677
 Map2D.cc:678
 Map2D.cc:679
 Map2D.cc:680
 Map2D.cc:681
 Map2D.cc:682
 Map2D.cc:683
 Map2D.cc:684
 Map2D.cc:685
 Map2D.cc:686
 Map2D.cc:687
 Map2D.cc:688
 Map2D.cc:689
 Map2D.cc:690
 Map2D.cc:691
 Map2D.cc:692
 Map2D.cc:693
 Map2D.cc:694
 Map2D.cc:695
 Map2D.cc:696
 Map2D.cc:697
 Map2D.cc:698
 Map2D.cc:699
 Map2D.cc:700
 Map2D.cc:701
 Map2D.cc:702
 Map2D.cc:703
 Map2D.cc:704
 Map2D.cc:705
 Map2D.cc:706
 Map2D.cc:707
 Map2D.cc:708
 Map2D.cc:709
 Map2D.cc:710
 Map2D.cc:711
 Map2D.cc:712
 Map2D.cc:713
 Map2D.cc:714
 Map2D.cc:715
 Map2D.cc:716
 Map2D.cc:717
 Map2D.cc:718
 Map2D.cc:719
 Map2D.cc:720
 Map2D.cc:721
 Map2D.cc:722
 Map2D.cc:723
 Map2D.cc:724
 Map2D.cc:725
 Map2D.cc:726
 Map2D.cc:727
 Map2D.cc:728
 Map2D.cc:729
 Map2D.cc:730
 Map2D.cc:731
 Map2D.cc:732
 Map2D.cc:733
 Map2D.cc:734
 Map2D.cc:735
 Map2D.cc:736
 Map2D.cc:737
 Map2D.cc:738
 Map2D.cc:739
 Map2D.cc:740
 Map2D.cc:741
 Map2D.cc:742
 Map2D.cc:743
 Map2D.cc:744
 Map2D.cc:745
 Map2D.cc:746
 Map2D.cc:747
 Map2D.cc:748
 Map2D.cc:749
 Map2D.cc:750
 Map2D.cc:751
 Map2D.cc:752
 Map2D.cc:753
 Map2D.cc:754
 Map2D.cc:755
 Map2D.cc:756
 Map2D.cc:757
 Map2D.cc:758
 Map2D.cc:759
 Map2D.cc:760
 Map2D.cc:761
 Map2D.cc:762
 Map2D.cc:763
 Map2D.cc:764
 Map2D.cc:765
 Map2D.cc:766
 Map2D.cc:767
 Map2D.cc:768
 Map2D.cc:769
 Map2D.cc:770
 Map2D.cc:771
 Map2D.cc:772
 Map2D.cc:773
 Map2D.cc:774
 Map2D.cc:775
 Map2D.cc:776
 Map2D.cc:777
 Map2D.cc:778
 Map2D.cc:779
 Map2D.cc:780
 Map2D.cc:781
 Map2D.cc:782
 Map2D.cc:783
 Map2D.cc:784
 Map2D.cc:785
 Map2D.cc:786
 Map2D.cc:787
 Map2D.cc:788
 Map2D.cc:789
 Map2D.cc:790
 Map2D.cc:791
 Map2D.cc:792
 Map2D.cc:793
 Map2D.cc:794
 Map2D.cc:795
 Map2D.cc:796
 Map2D.cc:797
 Map2D.cc:798
 Map2D.cc:799
 Map2D.cc:800
 Map2D.cc:801
 Map2D.cc:802
 Map2D.cc:803
 Map2D.cc:804
 Map2D.cc:805
 Map2D.cc:806
 Map2D.cc:807
 Map2D.cc:808
 Map2D.cc:809
 Map2D.cc:810
 Map2D.cc:811
 Map2D.cc:812
 Map2D.cc:813
 Map2D.cc:814
 Map2D.cc:815
 Map2D.cc:816
 Map2D.cc:817
 Map2D.cc:818
 Map2D.cc:819
 Map2D.cc:820
 Map2D.cc:821
 Map2D.cc:822
 Map2D.cc:823
 Map2D.cc:824
 Map2D.cc:825
 Map2D.cc:826
 Map2D.cc:827
 Map2D.cc:828
 Map2D.cc:829
 Map2D.cc:830
 Map2D.cc:831
 Map2D.cc:832
 Map2D.cc:833
 Map2D.cc:834
 Map2D.cc:835
 Map2D.cc:836
 Map2D.cc:837
 Map2D.cc:838
 Map2D.cc:839
 Map2D.cc:840
 Map2D.cc:841
 Map2D.cc:842
 Map2D.cc:843
 Map2D.cc:844
 Map2D.cc:845
 Map2D.cc:846
 Map2D.cc:847
 Map2D.cc:848
 Map2D.cc:849
 Map2D.cc:850
 Map2D.cc:851
 Map2D.cc:852
 Map2D.cc:853
 Map2D.cc:854
 Map2D.cc:855
 Map2D.cc:856
 Map2D.cc:857
 Map2D.cc:858
 Map2D.cc:859
 Map2D.cc:860
 Map2D.cc:861
 Map2D.cc:862
 Map2D.cc:863
 Map2D.cc:864
 Map2D.cc:865
 Map2D.cc:866
 Map2D.cc:867
 Map2D.cc:868
 Map2D.cc:869
 Map2D.cc:870
 Map2D.cc:871
 Map2D.cc:872
 Map2D.cc:873
 Map2D.cc:874
 Map2D.cc:875
 Map2D.cc:876
 Map2D.cc:877
 Map2D.cc:878
 Map2D.cc:879
 Map2D.cc:880
 Map2D.cc:881
 Map2D.cc:882
 Map2D.cc:883
 Map2D.cc:884
 Map2D.cc:885
 Map2D.cc:886
 Map2D.cc:887
 Map2D.cc:888
 Map2D.cc:889
 Map2D.cc:890
 Map2D.cc:891
 Map2D.cc:892
 Map2D.cc:893
 Map2D.cc:894
 Map2D.cc:895
 Map2D.cc:896
 Map2D.cc:897
 Map2D.cc:898
 Map2D.cc:899
 Map2D.cc:900
 Map2D.cc:901
 Map2D.cc:902
 Map2D.cc:903
 Map2D.cc:904
 Map2D.cc:905
 Map2D.cc:906
 Map2D.cc:907
 Map2D.cc:908
 Map2D.cc:909
 Map2D.cc:910
 Map2D.cc:911
 Map2D.cc:912
 Map2D.cc:913
 Map2D.cc:914
 Map2D.cc:915
 Map2D.cc:916
 Map2D.cc:917
 Map2D.cc:918
 Map2D.cc:919
 Map2D.cc:920
 Map2D.cc:921
 Map2D.cc:922
 Map2D.cc:923
 Map2D.cc:924
 Map2D.cc:925
 Map2D.cc:926
 Map2D.cc:927
 Map2D.cc:928
 Map2D.cc:929
 Map2D.cc:930
 Map2D.cc:931
 Map2D.cc:932
 Map2D.cc:933
 Map2D.cc:934
 Map2D.cc:935
 Map2D.cc:936
 Map2D.cc:937
 Map2D.cc:938
 Map2D.cc:939
 Map2D.cc:940
 Map2D.cc:941
 Map2D.cc:942
 Map2D.cc:943
 Map2D.cc:944
 Map2D.cc:945
 Map2D.cc:946
 Map2D.cc:947
 Map2D.cc:948
 Map2D.cc:949
 Map2D.cc:950
 Map2D.cc:951
 Map2D.cc:952
 Map2D.cc:953
 Map2D.cc:954
 Map2D.cc:955
 Map2D.cc:956
 Map2D.cc:957
 Map2D.cc:958
 Map2D.cc:959
 Map2D.cc:960
 Map2D.cc:961
 Map2D.cc:962
 Map2D.cc:963
 Map2D.cc:964
 Map2D.cc:965
 Map2D.cc:966
 Map2D.cc:967
 Map2D.cc:968
 Map2D.cc:969
 Map2D.cc:970
 Map2D.cc:971
 Map2D.cc:972
 Map2D.cc:973
 Map2D.cc:974
 Map2D.cc:975
 Map2D.cc:976
 Map2D.cc:977
 Map2D.cc:978
 Map2D.cc:979
 Map2D.cc:980
 Map2D.cc:981
 Map2D.cc:982
 Map2D.cc:983
 Map2D.cc:984
 Map2D.cc:985
 Map2D.cc:986
 Map2D.cc:987
 Map2D.cc:988
 Map2D.cc:989
 Map2D.cc:990
 Map2D.cc:991
 Map2D.cc:992
 Map2D.cc:993
 Map2D.cc:994
 Map2D.cc:995
 Map2D.cc:996
 Map2D.cc:997
 Map2D.cc:998
 Map2D.cc:999
 Map2D.cc:1000
 Map2D.cc:1001
 Map2D.cc:1002
 Map2D.cc:1003
 Map2D.cc:1004
 Map2D.cc:1005
 Map2D.cc:1006
 Map2D.cc:1007
 Map2D.cc:1008
 Map2D.cc:1009
 Map2D.cc:1010
 Map2D.cc:1011
 Map2D.cc:1012
 Map2D.cc:1013
 Map2D.cc:1014
 Map2D.cc:1015
 Map2D.cc:1016
 Map2D.cc:1017
 Map2D.cc:1018
 Map2D.cc:1019
 Map2D.cc:1020
 Map2D.cc:1021
 Map2D.cc:1022
 Map2D.cc:1023
 Map2D.cc:1024
 Map2D.cc:1025
 Map2D.cc:1026
 Map2D.cc:1027
 Map2D.cc:1028
 Map2D.cc:1029
 Map2D.cc:1030
 Map2D.cc:1031
 Map2D.cc:1032
 Map2D.cc:1033
 Map2D.cc:1034
 Map2D.cc:1035
 Map2D.cc:1036
 Map2D.cc:1037
 Map2D.cc:1038
 Map2D.cc:1039
 Map2D.cc:1040
 Map2D.cc:1041
 Map2D.cc:1042
 Map2D.cc:1043
 Map2D.cc:1044
 Map2D.cc:1045
 Map2D.cc:1046
 Map2D.cc:1047
 Map2D.cc:1048
 Map2D.cc:1049
 Map2D.cc:1050
 Map2D.cc:1051
 Map2D.cc:1052
 Map2D.cc:1053
 Map2D.cc:1054
 Map2D.cc:1055
 Map2D.cc:1056
 Map2D.cc:1057
 Map2D.cc:1058
 Map2D.cc:1059
 Map2D.cc:1060
 Map2D.cc:1061
 Map2D.cc:1062
 Map2D.cc:1063
 Map2D.cc:1064
 Map2D.cc:1065
 Map2D.cc:1066
 Map2D.cc:1067
 Map2D.cc:1068
 Map2D.cc:1069
 Map2D.cc:1070
 Map2D.cc:1071
 Map2D.cc:1072
 Map2D.cc:1073
 Map2D.cc:1074
 Map2D.cc:1075
 Map2D.cc:1076
 Map2D.cc:1077
 Map2D.cc:1078
 Map2D.cc:1079
 Map2D.cc:1080
 Map2D.cc:1081
 Map2D.cc:1082
 Map2D.cc:1083
 Map2D.cc:1084
 Map2D.cc:1085
 Map2D.cc:1086
 Map2D.cc:1087
 Map2D.cc:1088
 Map2D.cc:1089
 Map2D.cc:1090
 Map2D.cc:1091
 Map2D.cc:1092
 Map2D.cc:1093
 Map2D.cc:1094
 Map2D.cc:1095
 Map2D.cc:1096
 Map2D.cc:1097
 Map2D.cc:1098
 Map2D.cc:1099
 Map2D.cc:1100
 Map2D.cc:1101
 Map2D.cc:1102
 Map2D.cc:1103
 Map2D.cc:1104
 Map2D.cc:1105
 Map2D.cc:1106
 Map2D.cc:1107
 Map2D.cc:1108
 Map2D.cc:1109
 Map2D.cc:1110
 Map2D.cc:1111
 Map2D.cc:1112
 Map2D.cc:1113
 Map2D.cc:1114
 Map2D.cc:1115
 Map2D.cc:1116
 Map2D.cc:1117
 Map2D.cc:1118
 Map2D.cc:1119
 Map2D.cc:1120
 Map2D.cc:1121
 Map2D.cc:1122
 Map2D.cc:1123
 Map2D.cc:1124
 Map2D.cc:1125
 Map2D.cc:1126
 Map2D.cc:1127
 Map2D.cc:1128
 Map2D.cc:1129
 Map2D.cc:1130
 Map2D.cc:1131
 Map2D.cc:1132
 Map2D.cc:1133
 Map2D.cc:1134
 Map2D.cc:1135
 Map2D.cc:1136
 Map2D.cc:1137
 Map2D.cc:1138
 Map2D.cc:1139
 Map2D.cc:1140
 Map2D.cc:1141
 Map2D.cc:1142
 Map2D.cc:1143
 Map2D.cc:1144
 Map2D.cc:1145
 Map2D.cc:1146
 Map2D.cc:1147
 Map2D.cc:1148
 Map2D.cc:1149
 Map2D.cc:1150
 Map2D.cc:1151
 Map2D.cc:1152
 Map2D.cc:1153
 Map2D.cc:1154
 Map2D.cc:1155
 Map2D.cc:1156
 Map2D.cc:1157
 Map2D.cc:1158
 Map2D.cc:1159
 Map2D.cc:1160
 Map2D.cc:1161
 Map2D.cc:1162
 Map2D.cc:1163
 Map2D.cc:1164
 Map2D.cc:1165
 Map2D.cc:1166
 Map2D.cc:1167
 Map2D.cc:1168
 Map2D.cc:1169
 Map2D.cc:1170
 Map2D.cc:1171
 Map2D.cc:1172
 Map2D.cc:1173
 Map2D.cc:1174
 Map2D.cc:1175
 Map2D.cc:1176
 Map2D.cc:1177
 Map2D.cc:1178
 Map2D.cc:1179
 Map2D.cc:1180
 Map2D.cc:1181
 Map2D.cc:1182
 Map2D.cc:1183
 Map2D.cc:1184
 Map2D.cc:1185
 Map2D.cc:1186
 Map2D.cc:1187
 Map2D.cc:1188
 Map2D.cc:1189
 Map2D.cc:1190
 Map2D.cc:1191
 Map2D.cc:1192
 Map2D.cc:1193
 Map2D.cc:1194
 Map2D.cc:1195
 Map2D.cc:1196
 Map2D.cc:1197
 Map2D.cc:1198
 Map2D.cc:1199
 Map2D.cc:1200
 Map2D.cc:1201
 Map2D.cc:1202
 Map2D.cc:1203
 Map2D.cc:1204
 Map2D.cc:1205
 Map2D.cc:1206
 Map2D.cc:1207
 Map2D.cc:1208
 Map2D.cc:1209
 Map2D.cc:1210
 Map2D.cc:1211
 Map2D.cc:1212
 Map2D.cc:1213
 Map2D.cc:1214
 Map2D.cc:1215
 Map2D.cc:1216
 Map2D.cc:1217
 Map2D.cc:1218
 Map2D.cc:1219
 Map2D.cc:1220
 Map2D.cc:1221
 Map2D.cc:1222
 Map2D.cc:1223
 Map2D.cc:1224
 Map2D.cc:1225
 Map2D.cc:1226
 Map2D.cc:1227
 Map2D.cc:1228
 Map2D.cc:1229
 Map2D.cc:1230
 Map2D.cc:1231
 Map2D.cc:1232
 Map2D.cc:1233
 Map2D.cc:1234
 Map2D.cc:1235
 Map2D.cc:1236
 Map2D.cc:1237
 Map2D.cc:1238
 Map2D.cc:1239
 Map2D.cc:1240
 Map2D.cc:1241
 Map2D.cc:1242
 Map2D.cc:1243
 Map2D.cc:1244
 Map2D.cc:1245
 Map2D.cc:1246
 Map2D.cc:1247
 Map2D.cc:1248
 Map2D.cc:1249
 Map2D.cc:1250
 Map2D.cc:1251
 Map2D.cc:1252
 Map2D.cc:1253
 Map2D.cc:1254
 Map2D.cc:1255
 Map2D.cc:1256
 Map2D.cc:1257
 Map2D.cc:1258
 Map2D.cc:1259
 Map2D.cc:1260
 Map2D.cc:1261
 Map2D.cc:1262
 Map2D.cc:1263
 Map2D.cc:1264
 Map2D.cc:1265
 Map2D.cc:1266
 Map2D.cc:1267
 Map2D.cc:1268
 Map2D.cc:1269
 Map2D.cc:1270
 Map2D.cc:1271
 Map2D.cc:1272
 Map2D.cc:1273
 Map2D.cc:1274
 Map2D.cc:1275
 Map2D.cc:1276
 Map2D.cc:1277
 Map2D.cc:1278
 Map2D.cc:1279
 Map2D.cc:1280
 Map2D.cc:1281
 Map2D.cc:1282
 Map2D.cc:1283
 Map2D.cc:1284
 Map2D.cc:1285
 Map2D.cc:1286
 Map2D.cc:1287
 Map2D.cc:1288
 Map2D.cc:1289
 Map2D.cc:1290
 Map2D.cc:1291
 Map2D.cc:1292
 Map2D.cc:1293
 Map2D.cc:1294
 Map2D.cc:1295
 Map2D.cc:1296
 Map2D.cc:1297
 Map2D.cc:1298
 Map2D.cc:1299
 Map2D.cc:1300
 Map2D.cc:1301
 Map2D.cc:1302
 Map2D.cc:1303
 Map2D.cc:1304
 Map2D.cc:1305
 Map2D.cc:1306
 Map2D.cc:1307
 Map2D.cc:1308
 Map2D.cc:1309
 Map2D.cc:1310
 Map2D.cc:1311
 Map2D.cc:1312
 Map2D.cc:1313
 Map2D.cc:1314
 Map2D.cc:1315
 Map2D.cc:1316
 Map2D.cc:1317
 Map2D.cc:1318
 Map2D.cc:1319
 Map2D.cc:1320
 Map2D.cc:1321
 Map2D.cc:1322
 Map2D.cc:1323
 Map2D.cc:1324
 Map2D.cc:1325
 Map2D.cc:1326
 Map2D.cc:1327
 Map2D.cc:1328
 Map2D.cc:1329
 Map2D.cc:1330
 Map2D.cc:1331
 Map2D.cc:1332
 Map2D.cc:1333
 Map2D.cc:1334
 Map2D.cc:1335
 Map2D.cc:1336
 Map2D.cc:1337
 Map2D.cc:1338
 Map2D.cc:1339
 Map2D.cc:1340
 Map2D.cc:1341
 Map2D.cc:1342
 Map2D.cc:1343
 Map2D.cc:1344
 Map2D.cc:1345
 Map2D.cc:1346
 Map2D.cc:1347
 Map2D.cc:1348
 Map2D.cc:1349
 Map2D.cc:1350
 Map2D.cc:1351
 Map2D.cc:1352
 Map2D.cc:1353
 Map2D.cc:1354
 Map2D.cc:1355
 Map2D.cc:1356
 Map2D.cc:1357
 Map2D.cc:1358
 Map2D.cc:1359
 Map2D.cc:1360
 Map2D.cc:1361
 Map2D.cc:1362
 Map2D.cc:1363
 Map2D.cc:1364
 Map2D.cc:1365
 Map2D.cc:1366
 Map2D.cc:1367
 Map2D.cc:1368
 Map2D.cc:1369
 Map2D.cc:1370
 Map2D.cc:1371
 Map2D.cc:1372
 Map2D.cc:1373
 Map2D.cc:1374
 Map2D.cc:1375
 Map2D.cc:1376
 Map2D.cc:1377
 Map2D.cc:1378
 Map2D.cc:1379
 Map2D.cc:1380
 Map2D.cc:1381
 Map2D.cc:1382
 Map2D.cc:1383
 Map2D.cc:1384
 Map2D.cc:1385
 Map2D.cc:1386
 Map2D.cc:1387
 Map2D.cc:1388
 Map2D.cc:1389
 Map2D.cc:1390
 Map2D.cc:1391
 Map2D.cc:1392
 Map2D.cc:1393
 Map2D.cc:1394
 Map2D.cc:1395
 Map2D.cc:1396
 Map2D.cc:1397
 Map2D.cc:1398
 Map2D.cc:1399
 Map2D.cc:1400
 Map2D.cc:1401
 Map2D.cc:1402
 Map2D.cc:1403
 Map2D.cc:1404
 Map2D.cc:1405
 Map2D.cc:1406
 Map2D.cc:1407
 Map2D.cc:1408
 Map2D.cc:1409
 Map2D.cc:1410
 Map2D.cc:1411
 Map2D.cc:1412
 Map2D.cc:1413
 Map2D.cc:1414
 Map2D.cc:1415
 Map2D.cc:1416
 Map2D.cc:1417
 Map2D.cc:1418
 Map2D.cc:1419
 Map2D.cc:1420
 Map2D.cc:1421
 Map2D.cc:1422
 Map2D.cc:1423
 Map2D.cc:1424
 Map2D.cc:1425
 Map2D.cc:1426
 Map2D.cc:1427
 Map2D.cc:1428
 Map2D.cc:1429
 Map2D.cc:1430
 Map2D.cc:1431
 Map2D.cc:1432
 Map2D.cc:1433
 Map2D.cc:1434
 Map2D.cc:1435
 Map2D.cc:1436
 Map2D.cc:1437
 Map2D.cc:1438
 Map2D.cc:1439
 Map2D.cc:1440
 Map2D.cc:1441
 Map2D.cc:1442
 Map2D.cc:1443
 Map2D.cc:1444
 Map2D.cc:1445
 Map2D.cc:1446
 Map2D.cc:1447
 Map2D.cc:1448
 Map2D.cc:1449
 Map2D.cc:1450
 Map2D.cc:1451
 Map2D.cc:1452
 Map2D.cc:1453
 Map2D.cc:1454
 Map2D.cc:1455
 Map2D.cc:1456
 Map2D.cc:1457
 Map2D.cc:1458
 Map2D.cc:1459
 Map2D.cc:1460
 Map2D.cc:1461
 Map2D.cc:1462
 Map2D.cc:1463
 Map2D.cc:1464
 Map2D.cc:1465
 Map2D.cc:1466
 Map2D.cc:1467
 Map2D.cc:1468
 Map2D.cc:1469
 Map2D.cc:1470
 Map2D.cc:1471
 Map2D.cc:1472
 Map2D.cc:1473
 Map2D.cc:1474
 Map2D.cc:1475
 Map2D.cc:1476
 Map2D.cc:1477
 Map2D.cc:1478
 Map2D.cc:1479
 Map2D.cc:1480
 Map2D.cc:1481
 Map2D.cc:1482
 Map2D.cc:1483
 Map2D.cc:1484
 Map2D.cc:1485
 Map2D.cc:1486
 Map2D.cc:1487
 Map2D.cc:1488
 Map2D.cc:1489
 Map2D.cc:1490
 Map2D.cc:1491
 Map2D.cc:1492
 Map2D.cc:1493
 Map2D.cc:1494
 Map2D.cc:1495
 Map2D.cc:1496
 Map2D.cc:1497
 Map2D.cc:1498
 Map2D.cc:1499
 Map2D.cc:1500
 Map2D.cc:1501
 Map2D.cc:1502
 Map2D.cc:1503
 Map2D.cc:1504
 Map2D.cc:1505
 Map2D.cc:1506
 Map2D.cc:1507
 Map2D.cc:1508
 Map2D.cc:1509
 Map2D.cc:1510
 Map2D.cc:1511
 Map2D.cc:1512
 Map2D.cc:1513
 Map2D.cc:1514
 Map2D.cc:1515
 Map2D.cc:1516
 Map2D.cc:1517
 Map2D.cc:1518
 Map2D.cc:1519
 Map2D.cc:1520
 Map2D.cc:1521
 Map2D.cc:1522
 Map2D.cc:1523
 Map2D.cc:1524
 Map2D.cc:1525
 Map2D.cc:1526
 Map2D.cc:1527
 Map2D.cc:1528
 Map2D.cc:1529
 Map2D.cc:1530
 Map2D.cc:1531
 Map2D.cc:1532
 Map2D.cc:1533
 Map2D.cc:1534
 Map2D.cc:1535
 Map2D.cc:1536
 Map2D.cc:1537
 Map2D.cc:1538
 Map2D.cc:1539
 Map2D.cc:1540
 Map2D.cc:1541
 Map2D.cc:1542
 Map2D.cc:1543
 Map2D.cc:1544
 Map2D.cc:1545
 Map2D.cc:1546
 Map2D.cc:1547
 Map2D.cc:1548
 Map2D.cc:1549
 Map2D.cc:1550
 Map2D.cc:1551
 Map2D.cc:1552
 Map2D.cc:1553
 Map2D.cc:1554
 Map2D.cc:1555
 Map2D.cc:1556
 Map2D.cc:1557
 Map2D.cc:1558
 Map2D.cc:1559
 Map2D.cc:1560
 Map2D.cc:1561
 Map2D.cc:1562
 Map2D.cc:1563
 Map2D.cc:1564
 Map2D.cc:1565
 Map2D.cc:1566
 Map2D.cc:1567
 Map2D.cc:1568
 Map2D.cc:1569
 Map2D.cc:1570
 Map2D.cc:1571
 Map2D.cc:1572
 Map2D.cc:1573
 Map2D.cc:1574
 Map2D.cc:1575
 Map2D.cc:1576
 Map2D.cc:1577
 Map2D.cc:1578
 Map2D.cc:1579
 Map2D.cc:1580
 Map2D.cc:1581
 Map2D.cc:1582
 Map2D.cc:1583
 Map2D.cc:1584
 Map2D.cc:1585
 Map2D.cc:1586
 Map2D.cc:1587
 Map2D.cc:1588
 Map2D.cc:1589
 Map2D.cc:1590
 Map2D.cc:1591
 Map2D.cc:1592
 Map2D.cc:1593
 Map2D.cc:1594
 Map2D.cc:1595
 Map2D.cc:1596
 Map2D.cc:1597
 Map2D.cc:1598
 Map2D.cc:1599
 Map2D.cc:1600
 Map2D.cc:1601
 Map2D.cc:1602
 Map2D.cc:1603
 Map2D.cc:1604
 Map2D.cc:1605
 Map2D.cc:1606
 Map2D.cc:1607
 Map2D.cc:1608
 Map2D.cc:1609
 Map2D.cc:1610
 Map2D.cc:1611
 Map2D.cc:1612
 Map2D.cc:1613
 Map2D.cc:1614
 Map2D.cc:1615
 Map2D.cc:1616
 Map2D.cc:1617
 Map2D.cc:1618
 Map2D.cc:1619
 Map2D.cc:1620
 Map2D.cc:1621
 Map2D.cc:1622
 Map2D.cc:1623
 Map2D.cc:1624
 Map2D.cc:1625
 Map2D.cc:1626
 Map2D.cc:1627
 Map2D.cc:1628
 Map2D.cc:1629
 Map2D.cc:1630
 Map2D.cc:1631
 Map2D.cc:1632
 Map2D.cc:1633
 Map2D.cc:1634
 Map2D.cc:1635
 Map2D.cc:1636
 Map2D.cc:1637
 Map2D.cc:1638
 Map2D.cc:1639
 Map2D.cc:1640
 Map2D.cc:1641
 Map2D.cc:1642
 Map2D.cc:1643
 Map2D.cc:1644
 Map2D.cc:1645
 Map2D.cc:1646
 Map2D.cc:1647
 Map2D.cc:1648
 Map2D.cc:1649
 Map2D.cc:1650
 Map2D.cc:1651
 Map2D.cc:1652
 Map2D.cc:1653
 Map2D.cc:1654
 Map2D.cc:1655
 Map2D.cc:1656
 Map2D.cc:1657
 Map2D.cc:1658
 Map2D.cc:1659
 Map2D.cc:1660
 Map2D.cc:1661
 Map2D.cc:1662
 Map2D.cc:1663
 Map2D.cc:1664
 Map2D.cc:1665
 Map2D.cc:1666
 Map2D.cc:1667
 Map2D.cc:1668
 Map2D.cc:1669
 Map2D.cc:1670
 Map2D.cc:1671
 Map2D.cc:1672
 Map2D.cc:1673
 Map2D.cc:1674
 Map2D.cc:1675
 Map2D.cc:1676
 Map2D.cc:1677
 Map2D.cc:1678
 Map2D.cc:1679
 Map2D.cc:1680
 Map2D.cc:1681
 Map2D.cc:1682
 Map2D.cc:1683
 Map2D.cc:1684
 Map2D.cc:1685
 Map2D.cc:1686
 Map2D.cc:1687
 Map2D.cc:1688
 Map2D.cc:1689
 Map2D.cc:1690
 Map2D.cc:1691
 Map2D.cc:1692
 Map2D.cc:1693
 Map2D.cc:1694
 Map2D.cc:1695
 Map2D.cc:1696
 Map2D.cc:1697
 Map2D.cc:1698
 Map2D.cc:1699
 Map2D.cc:1700
 Map2D.cc:1701
 Map2D.cc:1702
 Map2D.cc:1703
 Map2D.cc:1704
 Map2D.cc:1705
 Map2D.cc:1706
 Map2D.cc:1707
 Map2D.cc:1708
 Map2D.cc:1709
 Map2D.cc:1710
 Map2D.cc:1711
 Map2D.cc:1712
 Map2D.cc:1713
 Map2D.cc:1714
 Map2D.cc:1715
 Map2D.cc:1716
 Map2D.cc:1717
 Map2D.cc:1718
 Map2D.cc:1719
 Map2D.cc:1720
 Map2D.cc:1721
 Map2D.cc:1722
 Map2D.cc:1723
 Map2D.cc:1724
 Map2D.cc:1725
 Map2D.cc:1726
 Map2D.cc:1727
 Map2D.cc:1728
 Map2D.cc:1729
 Map2D.cc:1730
 Map2D.cc:1731
 Map2D.cc:1732
 Map2D.cc:1733
 Map2D.cc:1734
 Map2D.cc:1735
 Map2D.cc:1736
 Map2D.cc:1737
 Map2D.cc:1738
 Map2D.cc:1739
 Map2D.cc:1740
 Map2D.cc:1741
 Map2D.cc:1742
 Map2D.cc:1743
 Map2D.cc:1744
 Map2D.cc:1745
 Map2D.cc:1746
 Map2D.cc:1747
 Map2D.cc:1748
 Map2D.cc:1749
 Map2D.cc:1750
 Map2D.cc:1751
 Map2D.cc:1752
 Map2D.cc:1753
 Map2D.cc:1754
 Map2D.cc:1755
 Map2D.cc:1756
 Map2D.cc:1757
 Map2D.cc:1758
 Map2D.cc:1759
 Map2D.cc:1760
 Map2D.cc:1761
 Map2D.cc:1762
 Map2D.cc:1763
 Map2D.cc:1764
 Map2D.cc:1765
 Map2D.cc:1766
 Map2D.cc:1767
 Map2D.cc:1768
 Map2D.cc:1769
 Map2D.cc:1770
 Map2D.cc:1771
 Map2D.cc:1772
 Map2D.cc:1773
 Map2D.cc:1774
 Map2D.cc:1775
 Map2D.cc:1776
 Map2D.cc:1777
 Map2D.cc:1778
 Map2D.cc:1779
 Map2D.cc:1780
 Map2D.cc:1781
 Map2D.cc:1782
 Map2D.cc:1783
 Map2D.cc:1784
 Map2D.cc:1785
 Map2D.cc:1786
 Map2D.cc:1787
 Map2D.cc:1788
 Map2D.cc:1789
 Map2D.cc:1790
 Map2D.cc:1791
 Map2D.cc:1792
 Map2D.cc:1793
 Map2D.cc:1794
 Map2D.cc:1795
 Map2D.cc:1796
 Map2D.cc:1797
 Map2D.cc:1798
 Map2D.cc:1799
 Map2D.cc:1800
 Map2D.cc:1801
 Map2D.cc:1802
 Map2D.cc:1803
 Map2D.cc:1804
 Map2D.cc:1805
 Map2D.cc:1806
 Map2D.cc:1807
 Map2D.cc:1808
 Map2D.cc:1809
 Map2D.cc:1810
 Map2D.cc:1811
 Map2D.cc:1812
 Map2D.cc:1813
 Map2D.cc:1814
 Map2D.cc:1815
 Map2D.cc:1816
 Map2D.cc:1817
 Map2D.cc:1818
 Map2D.cc:1819
 Map2D.cc:1820
 Map2D.cc:1821
 Map2D.cc:1822
 Map2D.cc:1823
 Map2D.cc:1824
 Map2D.cc:1825
 Map2D.cc:1826
 Map2D.cc:1827
 Map2D.cc:1828
 Map2D.cc:1829
 Map2D.cc:1830
 Map2D.cc:1831
 Map2D.cc:1832
 Map2D.cc:1833
 Map2D.cc:1834
 Map2D.cc:1835
 Map2D.cc:1836
 Map2D.cc:1837
 Map2D.cc:1838
 Map2D.cc:1839
 Map2D.cc:1840
 Map2D.cc:1841
 Map2D.cc:1842
 Map2D.cc:1843
 Map2D.cc:1844
 Map2D.cc:1845
 Map2D.cc:1846
 Map2D.cc:1847
 Map2D.cc:1848
 Map2D.cc:1849
 Map2D.cc:1850
 Map2D.cc:1851
 Map2D.cc:1852
 Map2D.cc:1853
 Map2D.cc:1854
 Map2D.cc:1855
 Map2D.cc:1856
 Map2D.cc:1857
 Map2D.cc:1858
 Map2D.cc:1859
 Map2D.cc:1860
 Map2D.cc:1861
 Map2D.cc:1862
 Map2D.cc:1863
 Map2D.cc:1864
 Map2D.cc:1865
 Map2D.cc:1866
 Map2D.cc:1867
 Map2D.cc:1868
 Map2D.cc:1869
 Map2D.cc:1870
 Map2D.cc:1871
 Map2D.cc:1872
 Map2D.cc:1873
 Map2D.cc:1874
 Map2D.cc:1875
 Map2D.cc:1876
 Map2D.cc:1877
 Map2D.cc:1878
 Map2D.cc:1879
 Map2D.cc:1880
 Map2D.cc:1881
 Map2D.cc:1882
 Map2D.cc:1883
 Map2D.cc:1884
 Map2D.cc:1885
 Map2D.cc:1886
 Map2D.cc:1887
 Map2D.cc:1888
 Map2D.cc:1889
 Map2D.cc:1890
 Map2D.cc:1891
 Map2D.cc:1892
 Map2D.cc:1893
 Map2D.cc:1894
 Map2D.cc:1895
 Map2D.cc:1896
 Map2D.cc:1897
 Map2D.cc:1898
 Map2D.cc:1899
 Map2D.cc:1900
 Map2D.cc:1901
 Map2D.cc:1902
 Map2D.cc:1903
 Map2D.cc:1904
 Map2D.cc:1905
 Map2D.cc:1906
 Map2D.cc:1907
 Map2D.cc:1908
 Map2D.cc:1909
 Map2D.cc:1910
 Map2D.cc:1911
 Map2D.cc:1912
 Map2D.cc:1913
 Map2D.cc:1914
 Map2D.cc:1915
 Map2D.cc:1916
 Map2D.cc:1917
 Map2D.cc:1918
 Map2D.cc:1919
 Map2D.cc:1920
 Map2D.cc:1921
 Map2D.cc:1922
 Map2D.cc:1923
 Map2D.cc:1924
 Map2D.cc:1925
 Map2D.cc:1926
 Map2D.cc:1927
 Map2D.cc:1928
 Map2D.cc:1929
 Map2D.cc:1930
 Map2D.cc:1931
 Map2D.cc:1932
 Map2D.cc:1933
 Map2D.cc:1934
 Map2D.cc:1935
 Map2D.cc:1936
 Map2D.cc:1937
 Map2D.cc:1938
 Map2D.cc:1939
 Map2D.cc:1940
 Map2D.cc:1941
 Map2D.cc:1942
 Map2D.cc:1943
 Map2D.cc:1944
 Map2D.cc:1945
 Map2D.cc:1946
 Map2D.cc:1947
 Map2D.cc:1948
 Map2D.cc:1949
 Map2D.cc:1950
 Map2D.cc:1951
 Map2D.cc:1952
 Map2D.cc:1953
 Map2D.cc:1954
 Map2D.cc:1955
 Map2D.cc:1956
 Map2D.cc:1957
 Map2D.cc:1958
 Map2D.cc:1959
 Map2D.cc:1960
 Map2D.cc:1961
 Map2D.cc:1962
 Map2D.cc:1963
 Map2D.cc:1964
 Map2D.cc:1965
 Map2D.cc:1966
 Map2D.cc:1967
 Map2D.cc:1968
 Map2D.cc:1969
 Map2D.cc:1970
 Map2D.cc:1971
 Map2D.cc:1972
 Map2D.cc:1973
 Map2D.cc:1974
 Map2D.cc:1975
 Map2D.cc:1976
 Map2D.cc:1977
 Map2D.cc:1978
 Map2D.cc:1979
 Map2D.cc:1980
 Map2D.cc:1981
 Map2D.cc:1982
 Map2D.cc:1983
 Map2D.cc:1984
 Map2D.cc:1985
 Map2D.cc:1986
 Map2D.cc:1987
 Map2D.cc:1988
 Map2D.cc:1989
 Map2D.cc:1990
 Map2D.cc:1991
 Map2D.cc:1992
 Map2D.cc:1993
 Map2D.cc:1994
 Map2D.cc:1995
 Map2D.cc:1996
 Map2D.cc:1997
 Map2D.cc:1998
 Map2D.cc:1999
 Map2D.cc:2000
 Map2D.cc:2001
 Map2D.cc:2002
 Map2D.cc:2003
 Map2D.cc:2004
 Map2D.cc:2005
 Map2D.cc:2006
 Map2D.cc:2007
 Map2D.cc:2008
 Map2D.cc:2009
 Map2D.cc:2010
 Map2D.cc:2011
 Map2D.cc:2012
 Map2D.cc:2013
 Map2D.cc:2014
 Map2D.cc:2015
 Map2D.cc:2016
 Map2D.cc:2017
 Map2D.cc:2018
 Map2D.cc:2019
 Map2D.cc:2020
 Map2D.cc:2021
 Map2D.cc:2022
 Map2D.cc:2023
 Map2D.cc:2024
 Map2D.cc:2025
 Map2D.cc:2026
 Map2D.cc:2027
 Map2D.cc:2028
 Map2D.cc:2029
 Map2D.cc:2030
 Map2D.cc:2031
 Map2D.cc:2032
 Map2D.cc:2033
 Map2D.cc:2034
 Map2D.cc:2035
 Map2D.cc:2036
 Map2D.cc:2037
 Map2D.cc:2038
 Map2D.cc:2039
 Map2D.cc:2040
 Map2D.cc:2041
 Map2D.cc:2042
 Map2D.cc:2043
 Map2D.cc:2044
 Map2D.cc:2045
 Map2D.cc:2046
 Map2D.cc:2047
 Map2D.cc:2048
 Map2D.cc:2049
 Map2D.cc:2050
 Map2D.cc:2051
 Map2D.cc:2052
 Map2D.cc:2053
 Map2D.cc:2054
 Map2D.cc:2055
 Map2D.cc:2056
 Map2D.cc:2057
 Map2D.cc:2058
 Map2D.cc:2059
 Map2D.cc:2060
 Map2D.cc:2061
 Map2D.cc:2062
 Map2D.cc:2063
 Map2D.cc:2064
 Map2D.cc:2065
 Map2D.cc:2066
 Map2D.cc:2067
 Map2D.cc:2068
 Map2D.cc:2069
 Map2D.cc:2070
 Map2D.cc:2071
 Map2D.cc:2072
 Map2D.cc:2073
 Map2D.cc:2074
 Map2D.cc:2075
 Map2D.cc:2076
 Map2D.cc:2077
 Map2D.cc:2078
 Map2D.cc:2079
 Map2D.cc:2080
 Map2D.cc:2081
 Map2D.cc:2082
 Map2D.cc:2083
 Map2D.cc:2084
 Map2D.cc:2085
 Map2D.cc:2086
 Map2D.cc:2087
 Map2D.cc:2088
 Map2D.cc:2089
 Map2D.cc:2090
 Map2D.cc:2091
 Map2D.cc:2092
 Map2D.cc:2093
 Map2D.cc:2094
 Map2D.cc:2095
 Map2D.cc:2096
 Map2D.cc:2097
 Map2D.cc:2098
 Map2D.cc:2099
 Map2D.cc:2100
 Map2D.cc:2101
 Map2D.cc:2102
 Map2D.cc:2103
 Map2D.cc:2104
 Map2D.cc:2105
 Map2D.cc:2106
 Map2D.cc:2107
 Map2D.cc:2108
 Map2D.cc:2109
 Map2D.cc:2110
 Map2D.cc:2111
 Map2D.cc:2112
 Map2D.cc:2113
 Map2D.cc:2114
 Map2D.cc:2115
 Map2D.cc:2116
 Map2D.cc:2117
 Map2D.cc:2118
 Map2D.cc:2119
 Map2D.cc:2120
 Map2D.cc:2121
 Map2D.cc:2122
 Map2D.cc:2123
 Map2D.cc:2124
 Map2D.cc:2125
 Map2D.cc:2126
 Map2D.cc:2127
 Map2D.cc:2128
 Map2D.cc:2129
 Map2D.cc:2130
 Map2D.cc:2131
 Map2D.cc:2132
 Map2D.cc:2133
 Map2D.cc:2134
 Map2D.cc:2135
 Map2D.cc:2136
 Map2D.cc:2137
 Map2D.cc:2138
 Map2D.cc:2139
 Map2D.cc:2140
 Map2D.cc:2141
 Map2D.cc:2142
 Map2D.cc:2143
 Map2D.cc:2144
 Map2D.cc:2145
 Map2D.cc:2146
 Map2D.cc:2147
 Map2D.cc:2148
 Map2D.cc:2149
 Map2D.cc:2150
 Map2D.cc:2151
 Map2D.cc:2152
 Map2D.cc:2153
 Map2D.cc:2154
 Map2D.cc:2155
 Map2D.cc:2156
 Map2D.cc:2157
 Map2D.cc:2158
 Map2D.cc:2159
 Map2D.cc:2160
 Map2D.cc:2161
 Map2D.cc:2162
 Map2D.cc:2163
 Map2D.cc:2164
 Map2D.cc:2165
 Map2D.cc:2166
 Map2D.cc:2167
 Map2D.cc:2168
 Map2D.cc:2169
 Map2D.cc:2170
 Map2D.cc:2171
 Map2D.cc:2172
 Map2D.cc:2173
 Map2D.cc:2174
 Map2D.cc:2175
 Map2D.cc:2176
 Map2D.cc:2177
 Map2D.cc:2178
 Map2D.cc:2179
 Map2D.cc:2180
 Map2D.cc:2181
 Map2D.cc:2182
 Map2D.cc:2183
 Map2D.cc:2184
 Map2D.cc:2185
 Map2D.cc:2186
 Map2D.cc:2187
 Map2D.cc:2188
 Map2D.cc:2189
 Map2D.cc:2190
 Map2D.cc:2191
 Map2D.cc:2192
 Map2D.cc:2193
 Map2D.cc:2194
 Map2D.cc:2195
 Map2D.cc:2196
 Map2D.cc:2197
 Map2D.cc:2198
 Map2D.cc:2199
 Map2D.cc:2200
 Map2D.cc:2201
 Map2D.cc:2202
 Map2D.cc:2203
 Map2D.cc:2204
 Map2D.cc:2205
 Map2D.cc:2206
 Map2D.cc:2207
 Map2D.cc:2208
 Map2D.cc:2209
 Map2D.cc:2210
 Map2D.cc:2211
 Map2D.cc:2212
 Map2D.cc:2213
 Map2D.cc:2214
 Map2D.cc:2215
 Map2D.cc:2216
 Map2D.cc:2217
 Map2D.cc:2218
 Map2D.cc:2219
 Map2D.cc:2220
 Map2D.cc:2221
 Map2D.cc:2222
 Map2D.cc:2223
 Map2D.cc:2224
 Map2D.cc:2225
 Map2D.cc:2226
 Map2D.cc:2227
 Map2D.cc:2228
 Map2D.cc:2229
 Map2D.cc:2230
 Map2D.cc:2231
 Map2D.cc:2232
 Map2D.cc:2233
 Map2D.cc:2234
 Map2D.cc:2235
 Map2D.cc:2236
 Map2D.cc:2237
 Map2D.cc:2238
 Map2D.cc:2239
 Map2D.cc:2240
 Map2D.cc:2241
 Map2D.cc:2242
 Map2D.cc:2243
 Map2D.cc:2244
 Map2D.cc:2245
 Map2D.cc:2246
 Map2D.cc:2247