       \param reinit_path Force path to be recalculated (may take some time).
       \return Path containing the \c convert binary.
    **/
    inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
      static CImg<char> s_path;
      cimg::mutex(7);
      if (reinit_path) s_path.assign();
      if (user_path) {
        if (!s_path) s_path.assign(1024);
        std::strncpy(s_path,user_path,1023);
      } else if (!s_path) {
        s_path.assign(1024);
        bool path_found = false;
        std::FILE *file = 0;
#if cimg_OS==2
        if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true;
        const char *const pf_path = win_programfiles_path();
        for (int l = 0; l<2 && !path_found; ++l) {
          const char *const s_exe = l?"convert":"magick";
          cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
          if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          for (int k = 32; k>=10 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 9; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=10 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 9; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=10 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 9; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=10 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 9; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=10 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 9; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=10 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 9; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          for (int k = 32; k>=0 && !path_found; --k) {
            cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
            if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
          }
          if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
        }
#else
        std::strcpy(s_path,"./magick");
        if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
        if (!path_found) {
          std::strcpy(s_path,"./convert");
          if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
        }
        if (!path_found) std::strcpy(s_path,"convert");
#endif
        winformat_string(s_path);
      }


    const CImg<T> *data(const unsigned int l) const {
      return const_cast<CImgList<T>*>(this)->data(l);
    }


    const T& at(const int offset) const {
      if (is_empty())
        throw CImgInstanceException(_cimg_instance
                                    "at(): Empty instance.",
                                    cimg_instance);
      return _at(offset);
    }


    }

    //! Load a sublist list from a (non compressed) .cimg file \newinstance.
    static CImgList<T> get_load_cimg(const char *const filename,
                                     const unsigned int n0, const unsigned int n1,
                                     const unsigned int x0, const unsigned int y0,
                                     const unsigned int z0, const unsigned int c0,
                                     const unsigned int x1, const unsigned int y1,


    const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
                           const bool exit_on_anykey=false) const {
      CImgDisplay disp;
      return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
    }


      \param pos0 Starting index of the sublist.
      \param pos1 Ending index of the sublist.
    **/
    CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
      if (pos0>pos1 || pos1>=_width)
        throw CImgArgumentException(_cimglist_instance
                                    "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
                                    cimglist_instance,
                                    pos0,pos1);
      CImgList<T> res(pos1 - pos0 + 1);


      static double nan() {
#ifdef NAN
        return (double)NAN;
#else
        const double val_nan = -std::sqrt(-1.); return val_nan;
#endif
      }


    Tfloat _cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
      const float
        nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
        nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f),
        nfz = cimg::type<float>::is_nan(fz)?0:cimg::mod(fz,_depth - 0.5f);
      const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
      const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
      const int
        px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
        py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()),
        pz = cimg::mod(z - 1,depth()), nz = cimg::mod(z + 1,depth()), az = cimg::mod(z + 2,depth());
      const Tfloat
        Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
        Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
        Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
                           dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
        Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
        Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
        Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
                           dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
        Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
        Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
        Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
                           dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
        Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
        Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
        Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
                           dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
        Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
                         dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
        Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
        Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
        Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
                           dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
        Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
        Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
        Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
                           dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
        Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
        Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
        Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
                           dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
        Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
        Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
        Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
                           dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
        Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
                         dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
        Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
        Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
        Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
                           dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
        Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
        Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
        Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
                           dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
        Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
        Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
        Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
                           dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
        Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
        Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
        Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
                           dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
        In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
                         dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
        Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
        Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
        Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
                           dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
        Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
        Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
        Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
                           dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
        Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
        Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
        Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
                           dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
        Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
        Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
        Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
                           dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
        Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
                         dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
      return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia));
    }


    CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
                              const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
      const ulongT
        beg = (ulongT)offset(x0,y0,z0,c0),
        end = (ulongT)offset(x1,y0,z0,c0);
      if (beg>end || beg>=size() || end>=size())
        throw CImgArgumentException(_cimg_instance
                                    "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
                                    cimg_instance,
                                    x0,x1,y0,z0,c0);
      return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
    }


    CImg<ucharT> get_serialize(const bool is_compressed=false, const unsigned int header_size=0) const {
      return CImgList<T>(*this,true).get_serialize(is_compressed,header_size);
    }


    CImgList<t>& move_to(CImgList<t>& list) {
      list.assign(_width);
      bool is_one_shared_element = false;
      cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
      if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
      else cimglist_for(*this,l) _data[l].move_to(list[l]);
      assign();
      return list;
    }


    CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
                  const T& val6, const T& val7) {
      if (is_empty()) return *this;
      T *ptrd, *ptre = end() - 7;
      for (ptrd = _data; ptrd<ptre; ) {
        *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
        *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
      }
      ptre+=7;
      switch (ptre - ptrd) {
      case 7 : *(--ptre) = val6; // fallthrough
      case 6 : *(--ptre) = val5; // fallthrough
      case 5 : *(--ptre) = val4; // fallthrough
      case 4 : *(--ptre) = val3; // fallthrough
      case 3 : *(--ptre) = val2; // fallthrough
      case 2 : *(--ptre) = val1; // fallthrough
      case 1 : *(--ptre) = val0; // fallthrough
      }
      return *this;
    }


    }

    //! Display a simple dialog box, and wait for the user's response \specialization.
    inline int dialog(const char *const title, const char *const msg,
                      const char *const button1_label, const char *const button2_label,
                      const char *const button3_label, const char *const button4_label,
                      const char *const button5_label, const char *const button6_label,
                      const bool is_centered) {


    T at(const int offset, const T& out_value) const {
      return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
    }


    CImg<T>& crop(const int x0, const int y0,
                  const int x1, const int y1,
                  const unsigned int boundary_conditions=0) {
      return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
    }


    static DWORD WINAPI _events_thread(void* arg) {
      CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
      const char *const title = (const char*)(((void**)arg)[1]);
      MSG msg;
      delete[] (void**)arg;
      disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      disp->_bmi.bmiHeader.biWidth = disp->width();
      disp->_bmi.bmiHeader.biHeight = -disp->height();
      disp->_bmi.bmiHeader.biPlanes = 1;
      disp->_bmi.bmiHeader.biBitCount = 32;
      disp->_bmi.bmiHeader.biCompression = BI_RGB;
      disp->_bmi.bmiHeader.biSizeImage = 0;
      disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
      disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
      disp->_bmi.bmiHeader.biClrUsed = 0;
      disp->_bmi.bmiHeader.biClrImportant = 0;
      disp->_data = new unsigned int[(size_t)disp->_width*disp->_height];
      if (!disp->_is_fullscreen) { // Normal window
        RECT rect;
        rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1;
        AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
        const int
          border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2),
          border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1),
          ww = disp->width() + 2*border1,
          wh = disp->height() + border1 + border2,
          sw = CImgDisplay::screen_width(),
          sh = CImgDisplay::screen_height();
        int
          wx = (int)cimg::round(cimg::rand(0,sw - ww -1)),
          wy = (int)cimg::round(cimg::rand(64,sh - wh - 65));
        if (wx + ww>=sw) wx = sw - ww;
        if (wy + wh>=sh) wy = sh - wh;
        if (wx<0) wx = 0;
        if (wy<0) wy = 0;
        disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
                                      (DWORD)(WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE)),
                                      wx,wy,ww,wh,0,0,0,&(disp->_ccs));
        if (!disp->_is_closed) {
          GetWindowRect(disp->_window,&rect);
          disp->_window_x = rect.left;
          disp->_window_y = rect.top;
        } else disp->_window_x = disp->_window_y = cimg::type<int>::min();
      } else { // Fullscreen window
        const unsigned int
          sx = (unsigned int)screen_width(),
          sy = (unsigned int)screen_height();
        disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
                                      (DWORD)(WS_POPUP | (disp->_is_closed?0:WS_VISIBLE)),
                                      (int)(sx - disp->_width)/2,
                                      (int)(sy - disp->_height)/2,
                                      disp->width(),disp->height(),0,0,0,&(disp->_ccs));
        disp->_window_x = disp->_window_y = 0;
      }
      SetForegroundWindow(disp->_window);
      disp->_hdc = GetDC(disp->_window);
      disp->_window_width = disp->_width;
      disp->_window_height = disp->_height;
      disp->flush();
#ifdef _WIN64
      SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
      SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
#else
      SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
      SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
#endif
      SetEvent(disp->_is_created);
      while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
      return 0;
    }


    CImg<T> get_ror(const CImg<t>& img) const {
      return (+*this).ror(img);
    }


    static CImg<T> get_load_pdf_external(const char *const filename, const unsigned int resolution=400) {
      return CImg<T>().load_pdf_external(filename,resolution);
    }


    CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
      if (is_empty()) return +img;
      if (!img) return +*this;
      return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
    }


    CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
                       const unsigned int dimw, const unsigned int dimh) {
      if (!file && !filename)
        throw CImgArgumentException(_cimg_instance
                                    "load_rgb(): Specified filename is (null).",
                                    cimg_instance);

      if (!dimw || !dimh) return assign();
      const longT cimg_iobuffer = (longT)24*1024*1024;
      std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
      CImg<ucharT> raw;
      assign(dimw,dimh,1,3);
      T
        *ptr_r = data(0,0,0,0),
        *ptr_g = data(0,0,0,1),
        *ptr_b = data(0,0,0,2);
      for (longT to_read = (longT)size(); to_read>0; ) {
        raw.assign(std::min(to_read,cimg_iobuffer));
        cimg::fread(raw._data,raw._width,nfile);
        to_read-=raw._width;
        const unsigned char *ptrs = raw._data;
        for (ulongT off = raw._width/3UL; off; --off) {
          *(ptr_r++) = (T)*(ptrs++);
          *(ptr_g++) = (T)*(ptrs++);
          *(ptr_b++) = (T)*(ptrs++);
        }
      }
      if (!file) cimg::fclose(nfile);
      return *this;
    }


      static double mp_lte(_cimg_math_parser& mp) {
        return (double)(_mp_arg(2)<=_mp_arg(3));
      }


    const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
                        const ulongT wh, const ulongT whd) const {
      return _data[x + y*_width + z*wh + c*whd];
    }


    const T& min() const {
      if (is_empty())
        throw CImgInstanceException(_cimg_instance
                                    "min(): Empty instance.",
                                    cimg_instance);
      const T *ptr_min = _data;
      T min_value = *ptr_min;
      cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
      return *ptr_min;
    }


    inline double _fibonacci(int exp) {
      double
        base = (1 + std::sqrt(5.))/2,
        result = 1/std::sqrt(5.);
      while (exp) {
        if (exp&1) result*=base;
        exp>>=1;
        base*=base;
      }
      return result;
    }


    CImg<T>& channel(const int c0) {
      return channels(c0,c0);
    }


    static CImg<T> get_load_dcraw_external(const char *const filename) {
      return CImg<T>().load_dcraw_external(filename);
    }


    CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
#define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
      _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
      _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
      _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
      _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
      _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
      _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
      _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
      _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
      _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
      _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
      _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
      _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
      _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
      _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
      _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
      _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
      _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
      _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
      _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
      _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
      _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
      _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
      _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
      _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
      _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
      if (is_pressed) {
        if (*_keys)
          std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
        *_keys = keycode;
        if (*_released_keys) {
          std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
          *_released_keys = 0;
        }
      } else {
        if (*_keys) {
          std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
          *_keys = 0;
        }
        if (*_released_keys)
          std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
        *_released_keys = keycode;
      }
      _is_event = keycode?true:false;
      if (keycode) {
#if cimg_display==1
        pthread_cond_broadcast(&cimg::X11_attr().wait_event);
#elif cimg_display==2
        SetEvent(cimg::Win32_attr().wait_event);
#endif
      }
      return *this;
    }


    T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
      if (is_empty())
        throw CImgInstanceException(_cimglist_instance
                                    "atNXYZ(): Empty instance.",
                                    cimglist_instance);

      return _atNXYZ(pos,x,y,z,c);
    }


    ~CImgDisplay() {
      assign();
      delete[] _keys;
      delete[] _released_keys;
    }


_functor2d_int(const CImg<T>& pref):ref(pref) {}


    static CImg<floatT> torus3d(CImgList<tf>& primitives,
                                const float radius1=100, const float radius2=30,
                                const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
      primitives.assign();
      if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
      CImgList<floatT> vertices;
      for (unsigned int v = 0; v<subdivisions1; ++v) {
        const float
          beta = (float)(v*2*cimg::PI/subdivisions1),
          xc = radius1*(float)std::cos(beta),
          yc = radius1*(float)std::sin(beta);
        for (unsigned int u = 0; u<subdivisions2; ++u) {
          const float
            alpha = (float)(u*2*cimg::PI/subdivisions2),
            x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
            y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
            z = radius2*(float)std::sin(alpha);
          CImg<floatT>::vector(x,y,z).move_to(vertices);
        }
      }
      for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
        const unsigned int nv = (vv + 1)%subdivisions1;
        for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
          const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
          CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives);
        }
      }
      return vertices>'x';
    }


      float operator()(const float x, const float y, const float z) const {
        return (float)ref._linear_atXYZ(x,y,z);
      }


    bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
      const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
      const T *const ppixel = &pixel;
      if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
      ulongT off = (ulongT)(ppixel - _data);
      const ulongT nc = off/whd;
      off%=whd;
      const ulongT nz = off/wh;
      off%=wh;
      const ulongT ny = off/_width, nx = off%_width;
      x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
      return true;
    }


    CImg<T>& erode(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
                   const bool is_real=false) {
      if (is_empty() || !kernel) return *this;
      return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
    }


    CImg<T>& pow(const char *const expression) {
      return pow((+*this)._fill(expression,true,1,0,0,"pow",this));
    }


    static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
      return CImg<T>().load_analyze(file,voxel_size);
    }


    static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) {
      return (u*u - i*i + g[u] - g[i])/(2*(u - i));
    }


    CImg<T>& resize_doubleXY() {
      return get_resize_doubleXY().move_to(*this);
    }


static bool is_finite(const bool) { return true; }


    CImg<T> get_identity_matrix() const {
      return identity_matrix(std::max(_width,_height));
    }


      static double mp_linear_add(_cimg_math_parser& mp) {
        return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
      }


    CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
                        const unsigned int depth, const unsigned int spectrum, const T& val) {
      assign(n);
      cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
      return *this;
    }


    CImgDisplay& close() {
      return assign();
    }


    CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
                              const unsigned int boundary_conditions=1) const {
      return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
    }


       \param fps Number of desired frames per second.
       \param nb_loops Number of loops (\c 0 for infinite looping).
    **/
    const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
                                         const unsigned int nb_loops=0) {
      CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
      CImgList<charT> filenames;
      std::FILE *file = 0;

#ifdef cimg_use_png
#define _cimg_save_gif_extension "png"
#else
#define _cimg_save_gif_extension "ppm"
#endif

      do {
        cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
                      cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
        cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data);
        if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
      } while (file);
      cimglist_for(*this,l) {
        cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1);
        CImg<charT>::string(filename_tmp2).move_to(filenames);
        if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2);
        else _data[l].save(filename_tmp2);
      }
      cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u",
                    cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops);
      CImg<ucharT>::string(command).move_to(filenames,0);
      cimg_snprintf(command,command._width,"\"%s\"",
                    CImg<charT>::string(filename)._system_strescape().data());
      CImg<ucharT>::string(command).move_to(filenames);
      CImg<charT> _command = filenames>'x';
      cimg_for(_command,p,char) if (!*p) *p = ' ';
      _command.back() = 0;

      cimg::system(_command, cimg::imagemagick_path());
      file = cimg::std_fopen(filename,"rb");
      if (!file)
        throw CImgIOException(_cimglist_instance
                              "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
                              cimglist_instance,
                              filename);
      else cimg::fclose(file);


    CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
                                    const float sigma_s, const float sigma_r,
                                    const float sampling_s=0, const float sampling_r=0) const {
      return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
    }


    static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
                              const T& a4, const T& a5, const T& a6, const T& a7,
                              const T& a8, const T& a9, const T& a10, const T& a11,
                              const T& a12, const T& a13, const T& a14) {
      CImg<T> r(15,1);
      r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
      r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
      return r;
    }


      static double mp_atanh(_cimg_math_parser& mp) {
        return cimg::atanh(_mp_arg(2));
      }


      static double mp_acosh(_cimg_math_parser& mp) {
        return cimg::acosh(_mp_arg(2));
      }


    inline bool is_blank(const char c) {
      return c>=0 && (unsigned char)c<=' ';
    }


    CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
      return get_dijkstra(starting_node,ending_node).move_to(*this);
    }


    bool is_sameXZ(const CImg<t>& img) const {
      return is_sameXZ(img._width,img._depth);
    }


    CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
                    const unsigned int size_z=1, const unsigned int size_c=1) {
      const size_t siz = safe_size(size_x,size_y,size_z,size_c);
      if (!values || !siz) return assign();
      const size_t curr_siz = (size_t)size();
      if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
      if (_is_shared || values + siz<_data || values>=_data + size()) {
        assign(size_x,size_y,size_z,size_c);
        if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T));
        else std::memcpy((void*)_data,(void*)values,siz*sizeof(T));
      } else {
        T *new_data = 0;
        try { new_data = new T[siz]; } catch (...) {
          _width = _height = _depth = _spectrum = 0; _data = 0;
          throw CImgInstanceException(_cimg_instance
                                      "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
                                      cimg_instance,
                                      cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
                                      size_x,size_y,size_z,size_c);
        }
        std::memcpy((void*)new_data,(void*)values,siz*sizeof(T));
        delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
      }
      return *this;
    }


    CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
      if (!values) return +*this;
      CImg<T> res;
      if (is_empty()) return res;
      const ulongT vsiz = values.size();
      const char _axis = cimg::lowercase(axis);
      ulongT j = 0;
      unsigned int k = 0;
      int i0 = 0;
      res.assign(width(),height(),depth(),spectrum());
      switch (_axis) {
      case 'x' : {
        cimg_forX(*this,i) {
          if ((*this)(i)!=(T)values[j]) {
            if (j) --i;
            res.draw_image(k,get_columns(i0,i));
            k+=i - i0 + 1; i0 = i + 1; j = 0;
          } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
        }
        if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
        res.resize(k,-100,-100,-100,0);
      } break;
      case 'y' : {
        cimg_forY(*this,i) {
          if ((*this)(0,i)!=(T)values[j]) {
            if (j) --i;
            res.draw_image(0,k,get_rows(i0,i));
            k+=i - i0 + 1; i0 = i + 1; j = 0;
          } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
        }
        if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
        res.resize(-100,k,-100,-100,0);
      } break;
      case 'z' : {
        cimg_forZ(*this,i) {
          if ((*this)(0,0,i)!=(T)values[j]) {
            if (j) --i;
            res.draw_image(0,0,k,get_slices(i0,i));
            k+=i - i0 + 1; i0 = i + 1; j = 0;
          } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
        }
        if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
        res.resize(-100,-100,k,-100,0);
      } break;
      case 'c' : {
        cimg_forC(*this,i) {
          if ((*this)(0,0,0,i)!=(T)values[j]) {
            if (j) --i;
            res.draw_image(0,0,0,k,get_channels(i0,i));
            k+=i - i0 + 1; i0 = i + 1; j = 0;
          } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
        }
        if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
        res.resize(-100,-100,-100,k,0);
      } break;
      default : {
        const ulongT siz = size();
        res.unroll('y');
        if (vsiz==1) { // Optimized version for a single discard value
          const T val = (T)values[0];
          cimg_foroff(*this,i) {
            const T _val = (T)_data[i];
            if (_val!=val) res[k++] = _val;
          }
        } else { // Generic version
          cimg_foroff(*this,i) {
            if ((*this)[i]!=(T)values[j]) {
              if (j) --i;
              std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
              k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
            } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
          }
          if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
        }
        res.resize(1,k,1,1,0);
      }
      }
      return res;
    }


    CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
                                      const float time_step=0.5f) const {
      return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
    }


    }

    //! Load a multi-page TIFF file \newinstance.
    static CImgList<T> get_load_tiff(const char *const filename,
                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
                                     const unsigned int step_frame=1, unsigned int *const bits_per_value=0,


    CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
      if (is_empty()) return *this;
      if (strict_threshold) {
        if (soft_threshold)
          cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
          cimg_rofoff(*this,off) {
            const T v = _data[off];
            _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
          }
        else
          cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
          cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0;
      } else {
        if (soft_threshold)
          cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
          cimg_rofoff(*this,off) {
            const T v = _data[off];
            _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
          }
        else
          cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
          cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0;
      }
      return *this;
    }


    CImg<_cimg_Tt> operator*(const t value) const {
      return CImg<_cimg_Tt>(*this,false)*=value;
    }


static unsigned int format(const unsigned char val) { return (unsigned int)val; }


      static double mp_rot3d(_cimg_math_parser& mp) {
        double *ptrd = &_mp_arg(1) + 1;
        const float
          x = (float)_mp_arg(2),
          y = (float)_mp_arg(3),
          z = (float)_mp_arg(4),
          theta = (float)_mp_arg(5);
        CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta*180/cimg::PI);
        return cimg::type<double>::nan();
      }


      static double mp_div(_cimg_math_parser& mp) {
        return _mp_arg(2)/_mp_arg(3);
      }


      ~_cimg_math_parser() {
        cimg::srand(rng);
      }


    inline void fempty(std::FILE *const file, const char *const filename) {
      if (!file && !filename)
        throw CImgArgumentException("cimg::fempty(): Specified filename is (null).");
      std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
      if (!file) cimg::fclose(nfile);
    }


    explicit CImgDisplay(const CImgList<T>& list,
                         const char *const title=0, const unsigned int normalization=3,
                         const bool is_fullscreen=false, const bool is_closed=false):
      _width(0),_height(0),_normalization(0),
      _min(0),_max(0),
      _is_fullscreen(false),
      _title(0),
      _window_width(0),_window_height(0),_button(0),
      _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
      _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
      _mouse_x(-1),_mouse_y(-1),_wheel(0),
      _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
      assign(list,title,normalization,is_fullscreen,is_closed);
    }


    CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
                           const CImg<tp>& vertices, const CImgList<tf>& primitives,
                           const CImgList<tc>& colors, const CImgList<to>& opacities,
                           const unsigned int render_type,
                           const bool is_double_sided, const float focale,
                           const float lightx, const float lighty, const float lightz,
                           const float specular_lightness, const float specular_shininess,
                           const float g_opacity, CImg<tz>& zbuffer) {
      return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
                            render_type,is_double_sided,focale,lightx,lighty,lightz,
                            specular_lightness,specular_shininess,g_opacity,1);
    }


      static double mp_inrange(_cimg_math_parser& mp) {
        const unsigned int sizd = (unsigned int)mp.opcode[2];
        const bool
          include_m = (bool)_mp_arg(9),
          include_M = (bool)_mp_arg(10);
        if (!sizd) { // Scalar result
          const double val = _mp_arg(3);
          const double m = _mp_arg(5), M = _mp_arg(7);
          if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
          else return (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
        }

        // Vector result
        const unsigned int
          siz1 = (unsigned int)mp.opcode[4],
          siz2 = (unsigned int)mp.opcode[6],
          siz3 = (unsigned int)mp.opcode[8],
          off1 = siz1?1:0,
          off2 = siz2?1:0,
          off3 = siz3?1:0;
        double *ptrd = &_mp_arg(1) + 1;
        const double
          *ptr1 = &_mp_arg(3) + off1,
          *ptr2 = &_mp_arg(5) + off2,
          *ptr3 = &_mp_arg(7) + off3;
        for (unsigned int k = 0; k<sizd; ++k) {
          const double val = *ptr1;
          const double m = *ptr2, M = *ptr3;
          if (M>=m)
            ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
          else
            ptrd[k] = (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
          ptr1+=off1;
          ptr2+=off2;
          ptr3+=off3;
        }
        return cimg::type<double>::nan();
      }


    CImg<T> get_slices(const int z0, const int z1) const {
      return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
    }


      unsigned int scalar1(const mp_func op, const unsigned int arg1) {
        const unsigned int pos =
          arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:
          ((return_new_comp = true), scalar());
        CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
        return pos;
      }


    CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
      if (is_empty()) return *this;
      for (const char *s = axes; *s; ++s) {
        const char axis = cimg::lowercase(*s);
        const CImg<intT> coords = _autocrop(value,axis);
        if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels
        else switch (axis) {
        case 'x' : {
          const int x0 = coords[0], x1 = coords[1];
          if (x0>=0 && x1>=0) crop(x0,x1);
        } break;
        case 'y' : {
          const int y0 = coords[0], y1 = coords[1];
          if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
        } break;
        case 'z' : {
          const int z0 = coords[0], z1 = coords[1];
          if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
        } break;
        default : {
          const int c0 = coords[0], c1 = coords[1];
          if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
        }
        }
      }
      return *this;
    }


    //! Search path of an executable (Windows only).
#if cimg_OS==2
    inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) {
      char *ptr = 0;


    const CImg<T>& display_object3d(const char *const title,
                                    const CImg<tp>& vertices,
                                    const CImgList<tf>& primitives,
                                    const CImgList<tc>& colors,
                                    const bool centering=true,
                                    const int render_static=4, const int render_motion=1,
                                    const bool is_double_sided=true, const float focale=700,
                                    const float light_x=0, const float light_y=0, const float light_z=-5e8f,
                                    const float specular_lightness=0.2f, const float specular_shininess=0.1f,
                                    const bool display_axes=true, float *const pose_matrix=0,
                                    const bool exit_on_anykey=false) const {
      return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
                              render_static,render_motion,is_double_sided,focale,
                              light_x,light_y,light_z,specular_lightness,specular_shininess,
                              display_axes,pose_matrix,exit_on_anykey);
    }


    CImg<T>& load_bmp(std::FILE *const file) {
      return _load_bmp(file,0);
    }


    CImg<T> rotate(const float u, const float v, const float w, const float angle,
                   const float cx, const float cy, const float cz,
                   const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
      const float nangle = cimg::mod(angle,360.f);
      if (nangle==0.f) return *this;
      return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
    }


    const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
      return _save_raw(file,0,is_multiplexed);
    }


    static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
                            const unsigned int starting_node, const unsigned int ending_node=~0U) {
      CImg<uintT> foo;
      return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
    }


    bool is_nan() const {
      if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
      return false;
    }


    CImg<T>& matchpatch(const CImg<T>& patch_image,
                        const unsigned int patch_width,
                        const unsigned int patch_height,
                        const unsigned int patch_depth,
                        const unsigned int nb_iterations,
                        const unsigned int nb_randoms,
                        const float patch_penalization,
                        const CImg<t1> &guide,
                        CImg<t2> &matching_score) {
      return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
                            nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this);
    }


    CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
      return _load_off(primitives,colors,0,filename);
    }


    CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
      return get_histogram(nb_levels,min_value,max_value).move_to(*this);
    }


    inline void invert_endianness(T* const buffer, const cimg_ulong size) {
      if (size) switch (sizeof(T)) {
        case 1 : break;
        case 2 : {
          for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) {
            const unsigned short val = *(--ptr);
            *ptr = (unsigned short)((val>>8) | ((val<<8)));
          }
        } break;
        case 4 : {
          for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) {
            const unsigned int val = *(--ptr);
            *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24);
          }
        } break;
        case 8 : {
          const cimg_uint64
            m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24,
            m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56;
          for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) {
            const cimg_uint64 val = *(--ptr);
            *ptr =  (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) |
                     ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56));
          }
        } break;
        default : {
          for (T* ptr = buffer + size; ptr>buffer; ) {
            unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
            for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
          }
        }
        }
    }


    CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
      _width(0),_allocated_width(0),_data(0) {
      assign(2);
      _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
    }


    //! Remove first image.
    /**
    **/


      void check_list(char *const ss, char *const se, const char saved_char) {
        if (!imglist) {
          char *s0; _cimg_mp_strerr;
          throw CImgArgumentException("[" cimg_appname "_math_parser] "
                                      "CImg<%s>::%s: %s%s Invalid call with an empty image list, "
                                      "in expression '%s'.",
                                      pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0);
        }
      }


    CImg<T>& transpose() {
      if (_width==1) { _width = _height; _height = 1; return *this; }
      if (_height==1) { _height = _width; _width = 1; return *this; }
      if (_width==_height) {
        cimg_forYZC(*this,y,z,c) for (int x = y; x<width(); ++x) cimg::swap((*this)(x,y,z,c),(*this)(y,x,z,c));
        return *this;
      }
      return get_transpose().move_to(*this);
    }


    bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
      if (error_message) *error_message = 0;

      // Check instance dimension and header.
      if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
        if (error_message) cimg_sprintf(error_message,
                                        "CImg3d has invalid dimensions (%u,%u,%u,%u)",
                                        _width,_height,_depth,_spectrum);
        return false;
      }
      const T *ptrs = _data, *const ptre = end();
      if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
          !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
        if (error_message) cimg_sprintf(error_message,
                                        "CImg3d header not found");
        return false;
      }
      const unsigned int
        nb_points = cimg::float2uint((float)*(ptrs++)),
        nb_primitives = cimg::float2uint((float)*(ptrs++));

      // Check consistency of number of vertices / primitives.
      if (!full_check) {
        const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
        if (_data + minimal_size>ptre) {
          if (error_message) cimg_sprintf(error_message,
                                          "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected",
                                          nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size);
          return false;
        }
      }

      // Check consistency of vertex data.
      if (!nb_points) {
        if (nb_primitives) {
          if (error_message) cimg_sprintf(error_message,
                                          "CImg3d (%u,%u) defines no vertices but %u primitives",
                                          nb_points,nb_primitives,nb_primitives);
          return false;
        }
        if (ptrs!=ptre) {
          if (error_message) cimg_sprintf(error_message,
                                          "CImg3d (%u,%u) is an empty object but contains %u value%s "
                                          "more than expected",
                                          nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
          return false;
        }
        return true;
      }
      if (ptrs + 3*nb_points>ptre) {
        if (error_message) cimg_sprintf(error_message,
                                        "CImg3d (%u,%u) defines only %u vertices data",
                                        nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3);
        return false;
      }
      ptrs+=3*nb_points;

      // Check consistency of primitive data.
      if (ptrs==ptre) {
        if (error_message) cimg_sprintf(error_message,
                                        "CImg3d (%u,%u) defines %u vertices but no primitive",
                                        nb_points,nb_primitives,nb_points);
        return false;
      }

      if (!full_check) return true;

      for (unsigned int p = 0; p<nb_primitives; ++p) {
        const unsigned int nb_inds = (unsigned int)*(ptrs++);
        switch (nb_inds) {
        case 1 : { // Point
          const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
          if (i0>=nb_points) {
            if (error_message) cimg_sprintf(error_message,
                                            "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]",
                                            nb_points,nb_primitives,i0,p);
            return false;
          }
        } break;
        case 5 : { // Sphere
          const unsigned int
            i0 = cimg::float2uint((float)*(ptrs++)),
            i1 = cimg::float2uint((float)*(ptrs++));
          ptrs+=3;
          if (i0>=nb_points || i1>=nb_points) {
            if (error_message) cimg_sprintf(error_message,
                                            "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
                                            "sphere primitive [%u]",
                                            nb_points,nb_primitives,i0,i1,p);
            return false;
          }
        } break;
        case 2 : case 6 : { // Segment
          const unsigned int
            i0 = cimg::float2uint((float)*(ptrs++)),
            i1 = cimg::float2uint((float)*(ptrs++));
          if (nb_inds==6) ptrs+=4;
          if (i0>=nb_points || i1>=nb_points) {
            if (error_message) cimg_sprintf(error_message,
                                            "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
                                            "segment primitive [%u]",
                                            nb_points,nb_primitives,i0,i1,p);
            return false;
          }
        } break;
        case 3 : case 9 : { // Triangle
          const unsigned int
            i0 = cimg::float2uint((float)*(ptrs++)),
            i1 = cimg::float2uint((float)*(ptrs++)),
            i2 = cimg::float2uint((float)*(ptrs++));
          if (nb_inds==9) ptrs+=6;
          if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
            if (error_message) cimg_sprintf(error_message,
                                            "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
                                            "triangle primitive [%u]",
                                            nb_points,nb_primitives,i0,i1,i2,p);
            return false;
          }
        } break;
        case 4 : case 12 : { // Quadrangle
          const unsigned int
            i0 = cimg::float2uint((float)*(ptrs++)),
            i1 = cimg::float2uint((float)*(ptrs++)),
            i2 = cimg::float2uint((float)*(ptrs++)),
            i3 = cimg::float2uint((float)*(ptrs++));
          if (nb_inds==12) ptrs+=8;
          if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
            if (error_message) cimg_sprintf(error_message,
                                            "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
                                            "quadrangle primitive [%u]",
                                            nb_points,nb_primitives,i0,i1,i2,i3,p);
            return false;
          }
        } break;
        default :
          if (error_message) cimg_sprintf(error_message,
                                          "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
                                          nb_points,nb_primitives,p,nb_inds);
          return false;
        }
        if (ptrs>ptre) {
          if (error_message) cimg_sprintf(error_message,
                                          "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
                                          "%u values missing",
                                          nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre));
          return false;
        }
      }

      // Check consistency of color data.
      if (ptrs==ptre) {
        if (error_message) cimg_sprintf(error_message,
                                        "CImg3d (%u,%u) defines no color/texture data",
                                        nb_points,nb_primitives);
        return false;
      }
      for (unsigned int c = 0; c<nb_primitives; ++c) {
        if (*(ptrs++)!=(T)-128) ptrs+=2;
        else if ((ptrs+=3)<ptre) {
          const unsigned int
            w = (unsigned int)*(ptrs - 3),
            h = (unsigned int)*(ptrs - 2),
            s = (unsigned int)*(ptrs - 1);
          if (!h && !s) {
            if (w>=c) {
              if (error_message) cimg_sprintf(error_message,
                                              "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u "
                                              "for primitive [%u]",
                                              nb_points,nb_primitives,w,c);
              return false;
            }
          } else ptrs+=w*h*s;
        }
        if (ptrs>ptre) {
          if (error_message) cimg_sprintf(error_message,
                                          "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
                                          "%u values missing",
                                          nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre));
          return false;
        }
      }

      // Check consistency of opacity data.
      if (ptrs==ptre) {
        if (error_message) cimg_sprintf(error_message,
                                        "CImg3d (%u,%u) defines no opacity data",
                                        nb_points,nb_primitives);
        return false;
      }
      for (unsigned int o = 0; o<nb_primitives; ++o) {
        if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
          const unsigned int
            w = (unsigned int)*(ptrs - 3),
            h = (unsigned int)*(ptrs - 2),
            s = (unsigned int)*(ptrs - 1);
          if (!h && !s) {
            if (w>=o) {
              if (error_message) cimg_sprintf(error_message,
                                              "CImg3d (%u,%u) refers to invalid shared opacity index %u "
                                              "for primitive [%u]",
                                              nb_points,nb_primitives,w,o);
              return false;
            }
          } else ptrs+=w*h*s;
        }
        if (ptrs>ptre) {
          if (error_message) cimg_sprintf(error_message,
                                          "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
                                          nb_points,nb_primitives,o);
          return false;
        }
      }

      // Check end of data.
      if (ptrs<ptre) {
        if (error_message) cimg_sprintf(error_message,
                                        "CImg3d (%u,%u) contains %u value%s more than expected",
                                        nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
        return false;
      }
      return true;
    }


      static double mp_fill(_cimg_math_parser& mp) {
        unsigned int siz = (unsigned int)mp.opcode[2];
        double
          *ptrd = &_mp_arg(1),
          *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
          *const ptrs = &_mp_arg(4);
        if (siz) ++ptrd; else ++siz; // Fill vector-valued slot
        const CImg<ulongT>
          *const p_body = ++mp.p_code,
          *const p_end = p_body + mp.opcode[5];
        const unsigned int _break_type = mp.break_type;
        mp.break_type = 0;

        unsigned int it = 0;
        if (ptrc) { // Version with loop variable (3 arguments)
          while (it<siz) {
            *ptrc = (double)it;
            for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
              mp.opcode._data = mp.p_code->_data;
              const ulongT target = mp.opcode[1];
              mp.mem[target] = _cimg_mp_defunc(mp);
            }
            if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
            else ptrd[it] = *ptrs;
            ++it;
          }
          *ptrc = (double)it;
        } else // Version without loop variable (2 arguments)
          while (it<siz) {
            for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
              mp.opcode._data = mp.p_code->_data;
              const ulongT target = mp.opcode[1];
              mp.mem[target] = _cimg_mp_defunc(mp);
            }
            if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
            else ptrd[it] = *ptrs;
            ++it;
          }

        mp.break_type = _break_type;
        mp.p_code = p_end - 1;
        return *ptrd;
      }


      static double mp_pow4(_cimg_math_parser& mp) {
        const double val = _mp_arg(2);
        return val*val*val*val;
      }


      static double mp_list_Jxyz(_cimg_math_parser& mp) {
        double *ptrd = &_mp_arg(1) + 1;
        const unsigned int
          ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
          interpolation = (unsigned int)_mp_arg(6),
          boundary_conditions = (unsigned int)_mp_arg(7),
          vsiz = (unsigned int)mp.opcode[8];
        const CImg<T> &img = mp.imglist[ind];
        const double
          ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
          x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
        const ulongT whd = (ulongT)img._width*img._height*img._depth;
        const T *ptrs;
        switch (interpolation) {
        case 2 : // Cubic interpolation
          switch (boundary_conditions) {
          case 3 : { // Mirror
            const float
              w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
              mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
              cx = mx<img.width()?mx:w2 - mx - 1,
              cy = my<img.height()?my:h2 - my - 1,
              cz = mz<img.depth()?mz:d2 - mz - 1;
            cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
          } break;
          case 2 : // Periodic
            cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
            break;
          case 1 : // Neumann
            cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
            break;
          default : // Dirichlet
            cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
          } break;
        case 1 : // Linear interpolation
          switch (boundary_conditions) {
          case 3 : { // Mirror
            const float
              w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
              mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
              cx = mx<img.width()?mx:w2 - mx - 1,
              cy = my<img.height()?my:h2 - my - 1,
              cz = mz<img.depth()?mz:d2 - mz - 1;
            cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
          } break;
          case 2 : // Periodic
            cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
            break;
          case 1 : // Neumann
            cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
            break;
          default : // Dirichlet
            cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
          } break;
        case 0 : // Nearest neighbor interpolation
          switch (boundary_conditions) {
          case 3 : { // Mirror
            const int
              w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
              mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
              cx = mx<img.width()?mx:w2 - mx - 1,
              cy = my<img.height()?my:h2 - my - 1,
              cz = mz<img.depth()?mz:d2 - mz - 1;
            ptrs = &img(cx,cy,cz);
            cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
          } break;
          case 2 : { // Periodic
            const int
              cx = (int)cimg::mod(x,(double)img._width),
              cy = (int)cimg::mod(y,(double)img._height),
              cz = (int)cimg::mod(z,(double)img._depth);
            ptrs = &img(cx,cy,cz);
            cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
          } break;
          case 1 : { // Neumann
            ptrs = &img._atXYZ((int)x,(int)y,(int)z);
            cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
          } break;
          default : // Dirichlet
            if (img.containsXYZC((int)x,(int)y,(int)z)) {
              ptrs = &img((int)x,(int)y,(int)z);
              cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
            } else std::memset(ptrd,0,vsiz*sizeof(double));
          }
        }
        return cimg::type<double>::nan();
      }


    inline cimg_int64 abs(const cimg_uint64 a) {
      return (cimg_int64)a;
    }


      return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
    }

    CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
                            const unsigned int n0, const unsigned int n1,
                            const unsigned int x0, const unsigned int y0,
                            const unsigned int z0, const unsigned int c0,
                            const unsigned int x1, const unsigned int y1,
                            const unsigned int z1, const unsigned int c1) {
#define _cimg_load_cimg_case2(Ts,Tss) \
      if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
        for (unsigned int l = 0; l<=nn1; ++l) { \
          j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
          W = H = D = C = 0; \
          if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
            throw CImgIOException(_cimglist_instance \
                                  "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
                                  cimglist_instance, \
                                  W,H,D,C,l,filename?filename:"(FILE*)"); \
          if (W*H*D*C>0) { \
            if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
            else { \
              const unsigned int \
                _nx1 = nx1==~0U?W - 1:nx1, \
                _ny1 = ny1==~0U?H - 1:ny1, \
                _nz1 = nz1==~0U?D - 1:nz1, \
                _nc1 = nc1==~0U?C - 1:nc1; \
              if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
                throw CImgArgumentException(_cimglist_instance \
                                            "load_cimg(): Invalid specified coordinates " \
                                            "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
                                            "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
                                            cimglist_instance, \
                                            n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
              CImg<Tss> raw(1 + _nx1 - nx0); \
              CImg<T> &img = _data[l - nn0]; \
              img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
              T *ptrd = img._data; \
              ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
              if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
              for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
                const ulongT skipzb = nz0*W*H*sizeof(Tss); \
                if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
                for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
                  const ulongT skipyb = ny0*W*sizeof(Tss); \
                  if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
                  for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
                    const ulongT skipxb = nx0*sizeof(Tss); \
                    if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
                    cimg::fread(raw._data,raw._width,nfile); \
                    if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
                    const Tss *ptrs = raw._data; \
                    for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
                    const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
                    if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
                  } \
                  const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
                  if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
                } \
                const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
                if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
              } \
              const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
              if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
            } \
          } \
        } \
        loaded = true; \
      }

      if (!filename && !file)
        throw CImgArgumentException(_cimglist_instance
                                    "load_cimg(): Specified filename is (null).",
                                    cimglist_instance);
      unsigned int
        nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
        nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
        ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
        nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
        nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);

      std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
      bool loaded = false, endian = cimg::endianness();
      CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
      *tmp = *str_pixeltype = *str_endian = 0;
      unsigned int j, N, W, H, D, C;
      int i, err;
      j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
      err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
                        &N,str_pixeltype._data,str_endian._data);
      if (err<2) {
        if (!file) cimg::fclose(nfile);
        throw CImgIOException(_cimglist_instance
                              "load_cimg(): CImg header not found in file '%s'.",
                              cimglist_instance,
                              filename?filename:"(FILE*)");
      }
      if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
      else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
      nn1 = n1==~0U?N - 1:n1;
      if (nn1>=N)
        throw CImgArgumentException(_cimglist_instance
                                    "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
                                    "because file '%s' contains only %u images.",
                                    cimglist_instance,
                                    n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
      assign(1 + nn1 - n0);
      _cimg_load_cimg_case2("bool",bool);
      _cimg_load_cimg_case2("unsigned_char",unsigned char);
      _cimg_load_cimg_case2("uchar",unsigned char);
      _cimg_load_cimg_case2("char",char);
      _cimg_load_cimg_case2("unsigned_short",unsigned short);
      _cimg_load_cimg_case2("ushort",unsigned short);
      _cimg_load_cimg_case2("short",short);
      _cimg_load_cimg_case2("unsigned_int",unsigned int);
      _cimg_load_cimg_case2("uint",unsigned int);
      _cimg_load_cimg_case2("int",int);
      _cimg_load_cimg_case2("unsigned_long",ulongT);
      _cimg_load_cimg_case2("ulong",ulongT);
      _cimg_load_cimg_case2("long",longT);
      _cimg_load_cimg_case2("unsigned_int64",uint64T);
      _cimg_load_cimg_case2("uint64",uint64T);
      _cimg_load_cimg_case2("int64",int64T);
      _cimg_load_cimg_case2("float",float);
      _cimg_load_cimg_case2("double",double);
      if (!loaded) {
        if (!file) cimg::fclose(nfile);
        throw CImgIOException(_cimglist_instance
                              "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
                              cimglist_instance,
                              str_pixeltype._data,filename?filename:"(FILE*)");
      }


    static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
      disp1._is_event = disp2._is_event = false;
      while ((!disp1._is_closed || !disp2._is_closed) &&
             !disp1._is_event && !disp2._is_event) wait_all();
    }


      _functor4d_streamline_expr(const char *const expr):mp(0) {
        mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
      }


      static double mp_vector_resize(_cimg_math_parser& mp) {
        double *const ptrd = &_mp_arg(1) + 1;
        const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
        const int
          interpolation = (int)_mp_arg(5),
          boundary_conditions = (int)_mp_arg(6);
        if (p2) { // Resize vector
          const double *const ptrs = &_mp_arg(3) + 1;
          CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
            get_resize(p1,1,1,1,interpolation,boundary_conditions);
        } else { // Resize scalar
          const double value = _mp_arg(3);
          CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
                                                                                boundary_conditions);
        }
        return cimg::type<double>::nan();
      }


    CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
      return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
    }


    CImg<T>& crop(const int x0, const int y0, const int z0,
                  const int x1, const int y1, const int z1,
                  const unsigned int boundary_conditions=0) {
      return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
    }


#endif

    // [internal] Sorting function, used by cimg::files().
    inline int _sort_files(const void* a, const void* b) {


    CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
                        const CImg<T>& sprite, const float opacity=1) {
      if (is_empty() || !sprite) return *this;
      if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
      if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
        return assign(sprite,false);
      const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
      const int
        dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
        sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
        lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
        ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
        lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
        lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
      const ulongT slx = lx*sizeof(T);

      const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
      if (lx>0 && ly>0 && lz>0 && lc>0) {
        for (int c = 0; c<lc; ++c)
          for (int z = 0; z<lz; ++z)
            for (int y = 0; y<ly; ++y) {
              T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
              const T *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
              if (opacity>=1) std::memcpy(ptrd,ptrs,slx);
              else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
            }
      }
      return *this;
    }


    CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
                  const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
                  const T& val12, const T& val13) {
      if (is_empty()) return *this;
      T *ptrd, *ptre = end() - 13;
      for (ptrd = _data; ptrd<ptre; ) {
        *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
        *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
        *(ptrd++) = val12; *(ptrd++) = val13;
      }
      ptre+=13;
      switch (ptre - ptrd) {
      case 13 : *(--ptre) = val12; // fallthrough
      case 12 : *(--ptre) = val11; // fallthrough
      case 11 : *(--ptre) = val10; // fallthrough
      case 10 : *(--ptre) = val9; // fallthrough
      case 9 : *(--ptre) = val8; // fallthrough
      case 8 : *(--ptre) = val7; // fallthrough
      case 7 : *(--ptre) = val6; // fallthrough
      case 6 : *(--ptre) = val5; // fallthrough
      case 5 : *(--ptre) = val4; // fallthrough
      case 4 : *(--ptre) = val3; // fallthrough
      case 3 : *(--ptre) = val2; // fallthrough
      case 2 : *(--ptre) = val1; // fallthrough
      case 1 : *(--ptre) = val0; // fallthrough
      }
      return *this;
    }


    static const CImg<Tuchar>& flag_LUT256() {
      static CImg<Tuchar> colormap;
      cimg::mutex(8);
      if (!colormap) {
        colormap.assign(1,4,1,3,(T)0);
        colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
        colormap.resize(1,256,1,3,0,2);
      }
      cimg::mutex(8,0);
      return colormap;
    }


    }

    //! Insert n empty images img into the current image list, at position \p pos \newinstance.


      static double mp_list_whds(_cimg_math_parser& mp) {
        const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
        return (double)mp.imglist[ind]._width*mp.imglist[ind]._height*mp.imglist[ind]._depth*mp.imglist[ind]._spectrum;
      }


    const CImg<T>& save_pnk(const char *const filename) const {
      return _save_pnk(0,filename);
    }


Win32_static& Win32_attr() { static Win32_static val; return val; }


      static double mp_image_s(_cimg_math_parser& mp) {
        unsigned int ind = (unsigned int)mp.opcode[2];
        if (ind!=~0U) {
          if (!mp.imglist.width()) return cimg::type<double>::nan();
          ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
        }
        const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
        return (double)img.spectrum();
      }


inline void invert_endianness(char* const, const cimg_ulong) {}


    Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
                              const int x=0, const int y=0, const int z=0) const {
      const Tfloat M = (Tfloat)cimg::type<T>::max();
      T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M);
      Tfloat root = 0;
      if (_depth>1) { // 3D
        T
          T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M),
          T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M);
        if (T1>T2) cimg::swap(T1,T2);
        if (T2>T3) cimg::swap(T2,T3);
        if (T1>T2) cimg::swap(T1,T2);
        if (P<=0) return (Tfloat)T1;
        if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root))
          return std::max((Tfloat)T3,root);
        if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
          return std::max((Tfloat)T2,root);
        return P + T1;
      } else if (_height>1) { // 2D
        T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M);
        if (T1>T2) cimg::swap(T1,T2);
        if (P<=0) return (Tfloat)T1;
        if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
          return std::max((Tfloat)T2,root);
        return P + T1;
      } else { // 1D
        if (P<=0) return (Tfloat)T1;
        return P + T1;
      }
      return 0;
    }


    CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
      return get_shared_slices(z0,z0,c0);
    }


    CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
                     const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
                     const T& val12) const {
      return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
                                                           val11,val12);
    }


    CImg<T>& draw_triangle(int x0, int y0, const float z0,
                           int x1, int y1, const float z1,
                           int x2, int y2, const float z2,
                           const CImg<tc>& texture,
                           int tx0, int ty0,
                           int tx1, int ty1,
                           int tx2, int ty2,
                           float bs0,
                           float bs1,
                           float bs2,
                           const float opacity=1) {
      if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
      if (texture._depth>1 || texture._spectrum<_spectrum)
        throw CImgArgumentException(_cimg_instance
                                    "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
                                    cimg_instance,
                                    texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
      if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
                                                       bs0,bs1,bs2,opacity);

      float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
      if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
      if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
      if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
      if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;

      const int
        w1 = width() - 1, h1 = height() - 1,
        dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
        dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
        cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
        hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
      const float
        diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
        txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
        tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
        dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
        dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
        dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;

      const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
      cimg_init_scanline(opacity);

      for (int y = cy0; y<=cy2; ++y) {
        const int yy0 = y - y0, yy1 = y - y1;
        int
          xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
          xM = x0 + (dx02*yy0 + hdy02)/dy02;
        float
          izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
          izM = iz0 + diz02*yy0/dy02,
          txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
          txzM = txz0 + dtxz02*yy0/dy02,
          tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
          tyzM = tyz0 + dtyz02*yy0/dy02,
          bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
          bsM = bs0 + dbs02*yy0/dy02;
        if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
        if (xM>=0 && xm<=w1) {
          const int
            cxm = cimg::cut(xm,0,w1),
            cxM = cimg::cut(xM,0,w1);
          T *ptrd = data(cxm,y);
          const int dxmM = std::max(1,xM - xm);
          const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;

          for (int x = cxm; x<=cxM; ++x) {
            const int xxm = x - xm;
            const float
              iz = izm + dizmM*xxm/dxmM,
              txz = txzm + dtxzmM*xxm/dxmM,
              tyz = tyzm + dtyzmM*xxm/dxmM,
              cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
            const int
              tx = (int)cimg::round(txz/iz),
              ty = (int)cimg::round(tyz/iz);
            const tc *const color = &texture._atXY(tx,ty);
            cimg_forC(*this,c) {
              const tc col = color[c*twhd];
              const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
              ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
            }
            ++ptrd;
          }
        }
      }
      return *this;
    }


      unsigned int scalar6(const mp_func op,
                           const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
                           const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
        const unsigned int pos =
          arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
          arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
          arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
          arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
          arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
          arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
          ((return_new_comp = true), scalar());
        CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
        return pos;
      }


      static double mp_list_wh(_cimg_math_parser& mp) {
        const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
        return (double)mp.imglist[ind]._width*mp.imglist[ind]._height;
      }


    /**
       \param n Index of the checked image.
    **/
    bool containsN(const int n) const {


    CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
                        const unsigned int normalization_type=3,
                        const bool fullscreen_flag=false, const bool closed_flag=false) {
      if (!list) return assign();
      CImg<T> tmp;
      const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
                                                                                (img._height - 1)/2,
                                                                                (img._depth - 1)/2));
      _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
      if (_normalization==2) _min = (float)nimg.min_max(_max);
      return display(nimg);
    }


    inline t maxabs(const t& a, const t& b) {
      return cimg::abs(b)>cimg::abs(a)?b:a;
    }


      void begin_t() {
        if (!code_begin_t) return;
        mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
        p_code_end = code_begin_t.end();
        for (p_code = code_begin_t; p_code<p_code_end; ++p_code) {
          opcode._data = p_code->_data;
          const ulongT target = opcode[1];
          mem[target] = _cimg_mp_defunc(*this);
        }
        p_code_end = code.end();
      }


      unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
        const unsigned int
          siz = _cimg_mp_size(arg1),
          pos = is_comp_vector(arg1)?arg1:
          ((return_new_comp = true), vector(siz));
        if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
        else {
          code.insert(siz);
          for (unsigned int k = 1; k<=siz; ++k)
            CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
        }
        return pos;
      }


    CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
                         const unsigned int normalization_type=3,
                         const bool fullscreen_flag=false, const bool closed_flag=false) {

      // Allocate space for window title
      const char *const nptitle = ptitle?ptitle:"";
      const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
      char *const tmp_title = s?new char[s]:0;
      if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));

      // Destroy previous window if existing
      if (!is_empty()) assign();

      // Set display variables
      _width = std::min(dimw,(unsigned int)screen_width());
      _height = std::min(dimh,(unsigned int)screen_height());
      _normalization = normalization_type<4?normalization_type:3;
      _is_fullscreen = fullscreen_flag;
      _window_x = _window_y = cimg::type<int>::min();
      _is_closed = closed_flag;
      _is_cursor_visible = true;
      _is_mouse_tracked = false;
      _title = tmp_title;
      flush();
      if (_is_fullscreen) _init_fullscreen();

      // Create event thread
      void *const arg = (void*)(new void*[2]);
      ((void**)arg)[0] = (void*)this;
      ((void**)arg)[1] = (void*)_title;
      _mutex = CreateMutex(0,FALSE_WIN,0);
      _is_created = CreateEvent(0,FALSE_WIN,FALSE_WIN,0);
      _thread = CreateThread(0,0,_events_thread,arg,0,0);
      WaitForSingleObject(_is_created,INFINITE);
      return *this;
    }


    CImg<ulongT> get_label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
                           const bool is_L2_norm=true) const {
      if (is_empty()) return CImg<ulongT>();
      int nb = 0;
      cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
      CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
      nb = 0;
      cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
                                               connectivity_mask(x,y,z)) {
        dx[nb] = x; dy[nb] = y; dz[nb++] = z;
      }
      return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
    }


    const CImg<T> *data(const unsigned int l) const {
      return _data + l;
    }


      static double mp_int(_cimg_math_parser& mp) {
        return (double)(longT)_mp_arg(2);
      }


    void eval(CImg<t>& output, const char *const expression,
              const double x=0, const double y=0, const double z=0, const double c=0,
              CImgList<T> *const list_images=0) const {
      _eval(output,0,expression,x,y,z,c,list_images);
    }


    CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
      return _load_off(primitives,colors,file,0);
    }


    const CImg<T>& save_pnk(std::FILE *const file) const {
      return _save_pnk(file,0);
    }


    static CImg<T> _logo40x38() {
      CImg<T> res(40,38,1,3);
      const unsigned char *ptrs = cimg::logo40x38;
      T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
      for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
        const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
        for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
      }
      return res;
    }


      static double mp_image_d(_cimg_math_parser& mp) {
        unsigned int ind = (unsigned int)mp.opcode[2];
        if (ind!=~0U) {
          if (!mp.imglist.width()) return cimg::type<double>::nan();
          ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
        }
        const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
        return (double)img.depth();
      }


    bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
      return _height==size_y && _spectrum==size_c;
    }


    void _load_tiff_separate(TIFF *const tif, const cimg_uint16 samplesperpixel,
                             const cimg_uint32 nx, const cimg_uint32 ny) {
      t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
      if (buf) {
        cimg_uint32 row, rowsperstrip = (cimg_uint32)-1;
        TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
        for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
          for (row = 0; row<ny; row+= rowsperstrip) {
            cimg_uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
            tstrip_t strip = TIFFComputeStrip(tif, row, vv);
            if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
              _TIFFfree(buf); TIFFClose(tif);
              throw CImgIOException(_cimg_instance
                                    "load_tiff(): Invalid strip in file '%s'.",
                                    cimg_instance,
                                    TIFFFileName(tif));
            }
            const t *ptr = buf;
            for (unsigned int rr = 0;rr<nrow; ++rr)
              for (unsigned int cc = 0; cc<nx; ++cc)
                (*this)(cc,row + rr,vv) = (T)*(ptr++);
          }
        _TIFFfree(buf);
      }
    }


    T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
                  const unsigned int z=0, const unsigned int c=0) {
      return (*this)[pos](x,y,z,c);
    }


    CImg<T> get_fill(const T& val) const {
      return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
    }


cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; }


    bool is_moved() const {
      return _is_moved;
    }


inline void invert_endianness(bool* const, const cimg_ulong) {}


    CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
             const CImg<t5>& img5, const bool is_shared=false):
      _width(0),_allocated_width(0),_data(0) {
      assign(5);
      _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
      _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
    }