gnuastro-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnuastro-commits] master 4021652 078/113: Imported recent work in maste


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 4021652 078/113: Imported recent work in master, conflicts fixed
Date: Fri, 16 Apr 2021 10:33:52 -0400 (EDT)

branch: master
commit 40216529a74eb960ae97f12f861a857a6835a29f
Merge: 3882956 e91fe5e
Author: Mohammad Akhlaghi <mohammad@akhlaghi.org>
Commit: Mohammad Akhlaghi <mohammad@akhlaghi.org>

    Imported recent work in master, conflicts fixed
    
    A minor conflict came up in the `main.h' structure of `crop' which was just
    due to re-ordering. It has been fixed.
    
    Another minor (renaming) conflict came up due to the new upper-limit
    termination criteria in MakeCatalog.
---
 NEWS                                     |  27 +-
 THANKS                                   |   7 +
 bin/arithmetic/arithmetic.c              |  99 +++-
 bin/arithmetic/arithmetic.h              |   3 +
 bin/buildprog/Makefile.am                |  14 +-
 bin/crop/args.h                          |  11 +-
 bin/crop/crop.c                          |   4 +-
 bin/crop/main.h                          |   3 +-
 bin/crop/ui.c                            | 128 ++++-
 bin/match/match.c                        | 198 +++----
 bin/mkcatalog/main.h                     |   4 +-
 bin/mkcatalog/ui.c                       |   2 +-
 bin/mkcatalog/upperlimit.c               |  20 +-
 bootstrap                                |  16 +-
 configure.ac                             |  67 ++-
 developer-build                          |  27 +-
 doc/announce-acknowledge.txt             |   7 +
 doc/gnuastro.texi                        | 882 ++++++++++++++++++++++---------
 lib/arithmetic.c                         | 139 +++--
 lib/dimension.c                          | 438 ++++++++++++++-
 lib/fits.c                               | 168 +++---
 lib/gnuastro-internal/config.h.in        |  33 +-
 lib/gnuastro/dimension.h                 |  17 +
 lib/gnuastro/wcs.h                       |   3 +
 lib/txt.c                                |  22 +-
 lib/wcs.c                                | 170 ++++++
 tests/Makefile.am                        |  28 +-
 tests/arithmetic/connected-components.sh |   8 +-
 tests/arithmetic/onlynumbers.sh          |   6 +-
 tests/arithmetic/or.sh                   |   6 +-
 tests/arithmetic/snimage.sh              |   8 +-
 tests/arithmetic/where.sh                |   6 +-
 tests/buildprog/simpleio.sh              |   7 +-
 tests/convertt/blankch.sh                |   6 +-
 tests/convertt/fitstojpeg.sh             |   6 +-
 tests/convertt/fitstojpegcmyk.sh         |   6 +-
 tests/convertt/fitstopdf.sh              |   6 +-
 tests/convertt/fitstotxt.sh              |   6 +-
 tests/convertt/jpegtofits.sh             |   6 +-
 tests/convertt/jpegtotxt.sh              |   6 +-
 tests/convolve/frequency.sh              |   7 +-
 tests/convolve/spatial.sh                |   7 +-
 tests/cosmiccal/simpletest.sh            |   6 +-
 tests/crop/imgcat.sh                     |  11 +-
 tests/crop/imgcenter.sh                  |   8 +-
 tests/crop/imgcenternoblank.sh           |   9 +-
 tests/crop/imgoutpolygon.sh              |  10 +-
 tests/crop/imgpolygon.sh                 |   9 +-
 tests/crop/section.sh                    |   8 +-
 tests/crop/wcscat.sh                     |  11 +-
 tests/crop/wcscenter.sh                  |   9 +-
 tests/crop/wcspolygon.sh                 |   9 +-
 tests/during-dev.sh                      |  19 +-
 tests/fits/copyhdu.sh                    |   6 +-
 tests/fits/delete.sh                     |   6 +-
 tests/fits/print.sh                      |   6 +-
 tests/fits/update.sh                     |   8 +-
 tests/fits/write.sh                      |  10 +-
 tests/lib/multithread.sh                 |   6 +-
 tests/lib/versioncxx.sh                  |   6 +-
 tests/match/merged-cols.sh               |   9 +-
 tests/match/positions.sh                 |   7 +-
 tests/mkcatalog/aperturephot.sh          |  10 +-
 tests/mkcatalog/detections.sh            |   9 +-
 tests/mkcatalog/objects-clumps.sh        |   9 +-
 tests/mknoise/addnoise.sh                |   8 +-
 tests/mkprof/clearcanvas.sh              |   9 +-
 tests/mkprof/ellipticalmasks.sh          |   8 +-
 tests/mkprof/mosaic1.sh                  |   8 +-
 tests/mkprof/mosaic2.sh                  |   6 +-
 tests/mkprof/mosaic3.sh                  |   6 +-
 tests/mkprof/mosaic4.sh                  |   6 +-
 tests/mkprof/radeccat.sh                 |   7 +-
 tests/noisechisel/noisechisel.sh         |   9 +-
 tests/segment/segment.sh                 |   6 +-
 tests/statistics/basicstats.sh           |   6 +-
 tests/statistics/estimate_sky.sh         |   6 +-
 tests/table/fits-ascii-to-txt.sh         |   6 +-
 tests/table/fits-binary-to-txt.sh        |   6 +-
 tests/table/txt-to-fits-ascii.sh         |   7 +-
 tests/table/txt-to-fits-binary.sh        |   7 +-
 tests/warp/homographic.sh                |   8 +-
 tests/warp/warp_scale.sh                 |   6 +-
 83 files changed, 2302 insertions(+), 658 deletions(-)

diff --git a/NEWS b/NEWS
index 5a359f9..07b2347 100644
--- a/NEWS
+++ b/NEWS
@@ -1,17 +1,34 @@
 GNU Astronomy Utilities NEWS                          -*- outline -*-
 
-
 * Noteworthy changes in release X.X (library 5.0.0) (YYYY-MM-DD) [alpha]
 
 ** New features
 
+  Installation:
+    --enable-debug: enable debugging flags and disable optimization.
+    --enable-check-with-valgrind: Run `make check' tests within Valgrind.
+
+  Arithmetic:
+    - `collapse-sum': collapse/remove a dimension by summing over it.
+    - `collapse-mean': collapse/remove a dimension by averaging over it.
+    - `collapse-number': Number of elements included in the collapse.
+
   Table:
-   - `--colinfoinstdout': column information when writing to standard output.
+    --colinfoinstdout: column information when writing to standard output.
+
+  Library:
+    - gal_dimension_collapse_sum: collapse/remove a dimension by summing.
+    - gal_dimension_collapse_mean: collapse/remove a dimension by averaging.
+    - gal_dimension_collapse_number: collapse/remove a dimension by number.
+    - gal_wcs_remove_dimension: Remove a dimension in the given WCS structure.
 
 ** Removed features
 
 ** Changed features
 
+  Crop:
+    --checkcenter: the units of value depend on mode (image or WCS).
+
   MakeCatalog:
     - `--checkuplim': new name for `--checkupperlimit'.
     - `--brightnessnoriver': new name for `--noriverbrightness'.
@@ -26,6 +43,12 @@ GNU Astronomy Utilities NEWS                          -*- 
outline -*-
   bug #54063: Match tests in make check fail randomly.
   bug #54186: MakeCatalog's --checkupperlimit not keeping output's name.
   bug #54188: MakeCatalog's Upperlimit not being sigma-clipped properly.
+  bug #54284: Crop segrault when catalog contains no data.
+  bug #54285: make check fails if g++ not present.
+  bug #54286: BuildProgram's configuration file, not built by default.
+  bug #54297: No Match output when --notmatched called and no match.
+  bug #54298: Table not writing array when there are no rows.
+  bug #54312: Crash when CFITSIO doesn't have fits_is_reentrant function.
 
 
 
diff --git a/THANKS b/THANKS
index 4c9823d..69a9ebc 100644
--- a/THANKS
+++ b/THANKS
@@ -15,6 +15,7 @@ The following people provided valuable feedback (suggestions, 
ideas) to the
 authors of Gnuastro. We hereby gratefully acknowledge their help and
 support in Gnuastro. The list is ordered alphabetically (by family name).
 
+    Valentina Abril-melgarejo            valentina.abril@lam.fr
     Marjan Akbari                        mrjakbari@gmail.com
     Roland Bacon                         roland.bacon@univ-lyon1.fr
     Karl Berry                           karl@gnu.org
@@ -33,19 +34,25 @@ support in Gnuastro. The list is ordered alphabetically (by 
family name).
     Takashi Ichikawa                     ichikawa@astr.tohoku.ac.jp
     Raúl Infante Sainz                   infantesainz@gmail.com
     Brandon Invergo                      brandon@gnu.org
+    Oryna Ivashtenko                     arinaivashtenko@gmail.com
     Aurélien Jarno                       aurelien.jarno@univ-lyon1.fr
+    Geoffry Krouchi                      geoffrey.krouchi@etu.univ-lyon1.fr
     Lee Kelvin                           l.s.kelvin@ljmu.ac.uk
     Brandon Kelly                        b.k.kelly@2017.ljmu.ac.uk
     Mohammad-Reza Khellat                moha.khe@gmail.com
     Floriane Leclercq                    floriane.leclercq@univ-lyon1.fr
     Alan Lefor                           alefor@astr.tohoku.ac.jp
     Guillaume Mahler                     guillaume.mahler@univ-lyon1.fr
+    Juan Molina Tobar                    juan.a.molina.t@gmail.com
     Francesco Montanari                  francesco.montanari@openmailbox.org
+    Dmitrii Oparin                       doparin2@gmail.com
     Bertrand Pain                        bertrand.pain@inserm.fr
     William Pence                        william.pence@nasa.gov
     Bob Proulx                           bob@proulx.com
+    Teymoor Saifollahi                   teymur.saif@gmail.com
     Yahya Sefidbakht                     y.sefidbakht@gmail.com
     Alejandro Serrano Borlaff            asborlaff@ucm.es
+    Jenny Sorce                          jenny.sorce@univ-lyon1.fr
     Lee Spitler                          lee.spitler@mq.edu.au
     Richard Stallman                     rms@gnu.org
     Ole Streicher                        olebole@debian.org
diff --git a/bin/arithmetic/arithmetic.c b/bin/arithmetic/arithmetic.c
index 44ab2e5..f83e461 100644
--- a/bin/arithmetic/arithmetic.c
+++ b/bin/arithmetic/arithmetic.c
@@ -685,6 +685,91 @@ arithmetic_interpolate(struct arithmeticparams *p, char 
*token)
 
 
 
+static void
+arithmetic_collapse(struct arithmeticparams *p, char *token, int operator)
+{
+  long dim;
+  gal_data_t *collapsed=NULL;
+
+  /* First popped operand is the dimension. */
+  gal_data_t *dimension = operands_pop(p, token);
+
+  /* The second popped operand is the desired input dataset. */
+  gal_data_t *input = operands_pop(p, token);
+
+
+  /* Small sanity check. */
+  if( dimension->ndim!=1 || dimension->size!=1)
+    error(EXIT_FAILURE, 0, "First popped operand of `collapse-*' operators "
+          "(dimension to collapse) must be a single number (single-element, "
+          "one-dimensional dataset). But it has %zu dimension(s) and %zu "
+          "element(s).", dimension->ndim, dimension->size);
+  if(dimension->type==GAL_TYPE_FLOAT32 || dimension->type==GAL_TYPE_FLOAT64)
+    error(EXIT_FAILURE, 0, "First popped operand of `collapse-*' operators "
+          "(dimension to collapse) must have an integer type, but it has "
+          "a floating point type (`%s')", gal_type_name(dimension->type,1));
+  dimension=gal_data_copy_to_new_type_free(dimension, GAL_TYPE_LONG);
+  dim=((long *)(dimension->array))[0];
+  if(dim<0 || dim==0)
+    error(EXIT_FAILURE, 0, "First popped operand of `collapse-*' operators "
+          "(dimension to collapse) must be positive (larger than zero), it "
+          "is %ld", dim);
+
+
+  /* If a WCS structure has been read, we'll need to pass it to
+     `gal_dimension_collapse', so it modifies it respectively. */
+  input->wcs=p->refdata.wcs;
+
+
+  /* Run the relevant library function. */
+  switch(operator)
+    {
+    case ARITHMETIC_OP_COLLAPSE_SUM:
+      collapsed=gal_dimension_collapse_sum(input, input->ndim-dim, NULL);
+      break;
+
+    case ARITHMETIC_OP_COLLAPSE_MEAN:
+      collapsed=gal_dimension_collapse_mean(input, input->ndim-dim, NULL);
+      break;
+
+    case ARITHMETIC_OP_COLLAPSE_NUMBER:
+      collapsed=gal_dimension_collapse_number(input, input->ndim-dim);
+      break;
+
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+            "problem. The operator code %d is not recognized", __func__,
+            PACKAGE_BUGREPORT, operator);
+    }
+
+
+  /* If a WCS structure existed, a modified WCS is now present in
+     `collapsed->wcs'. So we'll let the freeing of `input' free the old
+     `p->refdata.wcs' structure and we'll put the new one there, then we'll
+     set `collapsed->wcs' to `NULL', so the new one isn't freed. */
+  p->refdata.wcs = collapsed->wcs;
+  collapsed->wcs = NULL;
+
+
+  /* We'll also need to correct the size of the reference dataset. We'll
+     use `memcpy' to write the new `dsize' values into the old ones. The
+     dimensions have decreased, so we won't be writing outside of allocated
+     space that `p->refdata.dsize' points to. */
+  p->refdata.ndim -= 1;
+  memcpy( p->refdata.dsize, collapsed->dsize,
+           p->refdata.ndim * (sizeof *p->refdata.dsize) );
+
+
+  /* Clean up and add the collapsed dataset to the top of the operands. */
+  gal_data_free(input);
+  gal_data_free(dimension);
+  operands_add(p, NULL, collapsed);
+}
+
+
+
+
+
 
 
 
@@ -854,7 +939,7 @@ reversepolish(struct arithmeticparams *p)
           else if (!strcmp(token->v, "float64"))
             { op=GAL_ARITHMETIC_OP_TO_FLOAT64;        nop=1;  }
 
-          /* Filters. */
+          /* Library wrappers. */
           else if (!strcmp(token->v, "filter-mean"))
             { op=ARITHMETIC_OP_FILTER_MEAN;           nop=0;  }
           else if (!strcmp(token->v, "filter-median"))
@@ -873,6 +958,12 @@ reversepolish(struct arithmeticparams *p)
             { op=ARITHMETIC_OP_INVERT;                nop=0;  }
           else if (!strcmp(token->v, "interpolate-medianngb"))
             { op=ARITHMETIC_OP_INTERPOLATE_MEDIANNGB; nop=0;  }
+          else if (!strcmp(token->v, "collapse-sum"))
+            { op=ARITHMETIC_OP_COLLAPSE_SUM;          nop=0; }
+          else if (!strcmp(token->v, "collapse-mean"))
+            { op=ARITHMETIC_OP_COLLAPSE_MEAN;         nop=0; }
+          else if (!strcmp(token->v, "collapse-number"))
+            { op=ARITHMETIC_OP_COLLAPSE_NUMBER;       nop=0; }
 
 
           /* Finished checks with known operators */
@@ -964,6 +1055,12 @@ reversepolish(struct arithmeticparams *p)
                   arithmetic_interpolate(p, token->v);
                   break;
 
+                case ARITHMETIC_OP_COLLAPSE_SUM:
+                case ARITHMETIC_OP_COLLAPSE_MEAN:
+                case ARITHMETIC_OP_COLLAPSE_NUMBER:
+                  arithmetic_collapse(p, token->v, op);
+                  break;
+
                 default:
                   error(EXIT_FAILURE, 0, "%s: a bug! please contact us at "
                         "%s to fix the problem. The code %d is not "
diff --git a/bin/arithmetic/arithmetic.h b/bin/arithmetic/arithmetic.h
index e5eca69..4144006 100644
--- a/bin/arithmetic/arithmetic.h
+++ b/bin/arithmetic/arithmetic.h
@@ -40,6 +40,9 @@ enum arithmetic_prog_operators
   ARITHMETIC_OP_CONNECTED_COMPONENTS,
   ARITHMETIC_OP_INVERT,
   ARITHMETIC_OP_INTERPOLATE_MEDIANNGB,
+  ARITHMETIC_OP_COLLAPSE_SUM,
+  ARITHMETIC_OP_COLLAPSE_MEAN,
+  ARITHMETIC_OP_COLLAPSE_NUMBER,
 };
 
 
diff --git a/bin/buildprog/Makefile.am b/bin/buildprog/Makefile.am
index 7555f03..446186e 100644
--- a/bin/buildprog/Makefile.am
+++ b/bin/buildprog/Makefile.am
@@ -37,8 +37,10 @@ bin_PROGRAMS = astbuildprog
 ## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
 astbuildprog_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro
 
-astbuildprog_SOURCES = main.c ui.c buildprog.c astbuildprog.conf
+# Basic program sources.
+astbuildprog_SOURCES = main.c ui.c buildprog.c
 
+# Extra files that must be distributed in the tarball.
 EXTRA_DIST = main.h authors-cite.h args.h ui.h buildprog.h astbuildprog.conf.in
 
 
@@ -73,7 +75,13 @@ astbuildprog.conf: 
$(top_srcdir)/bin/buildprog/astbuildprog.conf.in
 
 
 
-## The configuration file (clean, distribute and install).
+## The configuration file in BuildProgram must be built on the users's
+## system (see above). The built configuration file is needed during `make
+## check'. But the (built) configuration file should NOT be distributed in a
+## tarball. Since all built files are distributed, the only way to avoid
+## the configuration file being distributed it so use a `nodist_' prefix.
+##
 ## NOTE: the man page is created in doc/Makefile.am
+nodist_sysconf_DATA = astbuildprog.conf
+check_DATA = astbuildprog.conf
 CLEANFILES = astbuildprog.conf
-dist_sysconf_DATA = astbuildprog.conf
diff --git a/bin/crop/args.h b/bin/crop/args.h
index 714b8d7..3b8a6a7 100644
--- a/bin/crop/args.h
+++ b/bin/crop/args.h
@@ -128,15 +128,16 @@ struct argp_option program_options[] =
     {
       "checkcenter",
       UI_KEY_CHECKCENTER,
-      "INT",
+      "FLT/INT",
       0,
       "Width (in pixels) of box at center to check.",
       UI_GROUP_CENTER_GENERAL,
-      &p->checkcenter,
-      GAL_TYPE_SIZE_T,
-      GAL_OPTIONS_RANGE_0_OR_ODD,
+      &p->incheckcenter,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
-      GAL_OPTIONS_NOT_SET
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_float64
     },
     {
       "width",
diff --git a/bin/crop/crop.c b/bin/crop/crop.c
index bccd980..3f82835 100644
--- a/bin/crop/crop.c
+++ b/bin/crop/crop.c
@@ -135,7 +135,7 @@ crop_verbose_final(struct cropparams *p)
       /* Only if the user wanted to check the center. */
       if(p->checkcenter)
         {
-          if( asprintf(&msg, "%zu filled in the center.", numcfilled)<0 )
+          if( asprintf(&msg,"%zu pixels filled in the center.",numcfilled)<0 )
             error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
           gal_timing_report(NULL, msg, 1);
           free(msg);
@@ -469,7 +469,7 @@ crop(struct cropparams *p)
     {
       if(p->checkcenter)
         {
-          if( asprintf(&tmp, "Width of central check box: %zu",
+          if( asprintf(&tmp, "Width of central check box (in pixels): %zu",
                        p->checkcenter)<0 )
             error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
           gal_list_str_add(&comments, tmp, 0);
diff --git a/bin/crop/main.h b/bin/crop/main.h
index d389c48..dd4e4fd 100644
--- a/bin/crop/main.h
+++ b/bin/crop/main.h
@@ -87,7 +87,7 @@ struct cropparams
   uint8_t       zeroisnotblank;  /* ==1: In float or double, keep 0.0.    */
   uint8_t              noblank;  /* ==1: no blank (out of image) pixels.  */
   char                 *suffix;  /* Ending of output file name.           */
-  size_t           checkcenter;  /* width of a box to check for zeros     */
+  gal_data_t    *incheckcenter;  /* Value given to `--checkcenter'.       */
   gal_data_t           *center;  /* Center position of crop.              */
   gal_data_t            *width;  /* Width of crop when defined by center. */
   char                *catname;  /* Name of input catalog.                */
@@ -102,6 +102,7 @@ struct cropparams
   size_t                 numin;  /* Number of input images.               */
   size_t                numout;  /* Number of output images.              */
   double        **centercoords;  /* A 1D array for the center position.   */
+  size_t           checkcenter;  /* width of a box to check for zeros     */
   char                  **name;  /* filename of crop in row.              */
   double             *wpolygon;  /* Array of WCS polygon vertices.        */
   double             *ipolygon;  /* Array of image polygon vertices.      */
diff --git a/bin/crop/ui.c b/bin/crop/ui.c
index 13f2428..6cee112 100644
--- a/bin/crop/ui.c
+++ b/bin/crop/ui.c
@@ -307,7 +307,24 @@ ui_read_check_only_options(struct cropparams *p)
     }
 
 
-  /* Section is currentlyl only defined in Image mode. */
+  /* Checkcenter sanity check. */
+  if(p->incheckcenter)
+    {
+      /* We only want a single number. */
+      if(p->incheckcenter->size>1)
+        error(EXIT_FAILURE, 0, "%zu values given to `--checkcenter'. This "
+              "option only takes one value currently",
+              p->incheckcenter->size);
+
+      darray=p->incheckcenter->array;
+      if(*darray<0.0f)
+        error(EXIT_FAILURE, 0, "negative value (%f) given to "
+              "`--checkcenter'. This option only takes positive values",
+              *darray);
+    }
+
+
+  /* Section is currently only defined in Image mode. */
   if(p->section && p->mode!=IMGCROP_MODE_IMG)
     error(EXIT_FAILURE, 0, "The `--section' option is only available in "
           "image coordinate mode, currently it doesn't work with WCS mode. "
@@ -404,16 +421,33 @@ ui_check_options_and_arguments(struct cropparams *p)
   if(p->catname)
     {
       /* When multiple threads need to access a file, CFITSIO needs to be
-         configured with the `--enable-reentrant` option. */
+         configured with the `--enable-reentrant` option and we can only
+         know from the `fits_is_reentrant' function that came from CFITSIO
+         version 3.30. */
+#if GAL_CONFIG_HAVE_FITS_IS_REENTRANT == 1
       if(p->cp.numthreads>1 && fits_is_reentrant()==0)
-        error(EXIT_FAILURE, 0, "CFITSIO was not configured with the "
-              "`--enable-reentrant` option but you have asked to crop "
-              "on %zu threads.\n\nPlease configure, make and install CFITSIO "
-              "again with this flag. Alternatively, to avoid this error "
-              "you can set the number of threads to 1 by adding the "
-              "`--numthreads=1` or `-N1` options. Please run the following "
-              "command to learn more about configuring CFITSIO:\n\n"
-              "    $ info gnuastro CFITSIO", p->cp.numthreads);
+        {
+          fprintf(stderr, "WARNING: CFITSIO was not configured with the "
+                  "`--enable-reentrant' option but you have asked to crop "
+                  "on %zu threads. Therefore only one thread will be used.\n\n"
+                  "Please run the following command to learn more about "
+                  "configuring CFITSIO:\n\n"
+                  "    $ info gnuastro CFITSIO", p->cp.numthreads);
+          p->cp.numthreads=1;
+        }
+#else
+      if(p->cp.numthreads>1)
+        {
+          fprintf(stderr, "WARNING: the installed CFITSIO version doesn't "
+                  "have `fits_is_reentrant' function (it is older than "
+                  "version 3.30). But you have asked to crop on %zu threads."
+                  "Therefore only one thread will be used.\n\n"
+                  "To avoid this warning, you can set the number of threads "
+                  "to one with `-N1' or update your installation of CFITSIO.",
+                  p->cp.numthreads);
+          p->cp.numthreads=1;
+        }
+#endif
 
       /* Make sure the given output is a directory. */
       gal_checkset_check_dir_write_add_slash(&p->cp.output);
@@ -452,12 +486,11 @@ ui_check_options_and_arguments(struct cropparams *p)
    must be in actual number of pixels (an integer). But the user's values
    can be in WCS mode or even in image mode, they may be non-integers. */
 static void
-ui_set_iwidth(struct cropparams *p)
+ui_set_img_sizes(struct cropparams *p)
 {
   gal_data_t *newwidth;
-  double pwidth, *warray;
   size_t i, ndim=p->imgs->ndim;
-
+  double pwidth, pcheckcenter, *warray;
 
   /* Make sure a width value is actually given. */
   if(p->width==NULL)
@@ -492,19 +525,25 @@ ui_set_iwidth(struct cropparams *p)
     }
   else warray=p->width->array;
 
-  /* Fill in `p->iwidth' depending on the mode. */
-  for(i=0;i<ndim;++i)
+  /* WCS mode. */
+  if(p->mode==IMGCROP_MODE_WCS)
     {
-      /* Set iwidth. */
-      if(p->mode==IMGCROP_MODE_WCS)
+      /* Fill in the widths depending on the mode. */
+      for(i=0;i<ndim;++i)
         {
           /* Convert the width in units of the input's WCS into pixels. */
           pwidth = warray[i]/p->pixscale[i];
           if(pwidth<3 || pwidth>50000)
-            error(EXIT_FAILURE, 0, "%g (width along dimension %zu) "
-                  "translates to %.0f pixels. This is probably not what "
-                  "you wanted. Note that the resolution in this dimension "
-                  "is %g", warray[i], i+1, pwidth, p->pixscale[i]);
+            error(EXIT_FAILURE, 0, "value %g (requested width along "
+                  "dimension %zu) translates to %.0f pixels on this "
+                  "dataset. This is probably not what you wanted. Note "
+                  "that the dataset's resolution in this dimension is "
+                  "%g.\n\n"
+                  "You can do the conversion to the dataset's WCS units "
+                  "prior to calling Crop. Alternatively, you can specify "
+                  "all the coordinates/sizes in image (not WCS) units and "
+                  "use the `--mode=img' option", warray[i], i+1, pwidth,
+                  p->pixscale[i]);
 
           /* Write the single valued width in WCS and the image width for
              this dimension. */
@@ -515,11 +554,44 @@ ui_set_iwidth(struct cropparams *p)
               warray[i]    += p->pixscale[i];
             }
         }
-      else
+
+      /* Checkcenter: */
+      if(p->incheckcenter)
+        pcheckcenter=((double *)(p->incheckcenter->array))[0]/p->pixscale[0];
+    }
+  /* Image mode. */
+  else
+    {
+      /* The width (along each dimension). */
+      for(i=0;i<ndim;++i)
         {
           p->iwidth[i]=GAL_DIMENSION_FLT_TO_INT(warray[i]);
           if(p->iwidth[i]%2==0) p->iwidth[i] += 1;
         }
+
+      /* Checkcenter: */
+      if(p->incheckcenter)
+        {
+          /* Write the double value into the temporary variable */
+          pcheckcenter=((double *)(p->incheckcenter->array))[0];
+
+          /* In image-mode it has to be an integer. */
+          if( ceilf(pcheckcenter)!=pcheckcenter )
+            error(EXIT_FAILURE, 0, "%g is not an integer. When cropping in "
+                  "image-mode, the number of pixels to check in the "
+                  "center must be an integer", pcheckcenter);
+        }
+    }
+
+  /* Finalize the number of central pixels to check. */
+  if(p->incheckcenter)
+    {
+      /* Convert the floating point value to an integer. */
+      p->checkcenter=GAL_DIMENSION_FLT_TO_INT(pcheckcenter);
+
+      /* If `checkcenter' isn't zero, but is even, convert it to an odd
+         number (so the actual center can be checked). */
+      if(p->checkcenter && p->checkcenter%2==0) p->checkcenter += 1;
     }
 
   /* For a check:
@@ -601,6 +673,10 @@ ui_read_cols(struct cropparams *p)
   /* Read the desired columns from the file. */
   cols=gal_table_read(p->catname, p->cathdu, colstrs, p->cp.searchin,
                       p->cp.ignorecase, p->cp.minmapsize, NULL);
+  if(cols==NULL)
+    error(EXIT_FAILURE, 0, "%s: is empty! No usable information "
+          "(un-commented lines) could be read from this file",
+          gal_fits_name_save_as_string(p->catname, p->cathdu));
 
 
   /* Set the number of objects (rows in each column). */
@@ -691,7 +767,7 @@ ui_prepare_center(struct cropparams *p)
 
 
   /* Set the integer widths of the crop(s) when defined by center. */
-  ui_set_iwidth(p);
+  ui_set_img_sizes(p);
 
   /* For a catalog, we have a separate function, but for a single center
      value, put the center values into an array. This will essentially
@@ -961,6 +1037,12 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
cropparams *p)
   if(!p->cp.quiet)
     {
       printf(PROGRAM_NAME" started on %s", ctime(&p->rawtime));
+      if(p->cp.numthreads>1)
+        printf("  - Using %zu CPU thread%s\n", p->cp.numthreads,
+               p->cp.numthreads==1 ? "." : "s.");
+      if(p->checkcenter)
+        printf("  - Number of central pixels to check for blank: %zu\n",
+               p->checkcenter);
       if( asprintf(&msg, "Read metadata of %zu dataset%s.", p->numin,
                    p->numin>1 ? "s" : "")<0 )
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
diff --git a/bin/match/match.c b/bin/match/match.c
index bd659c3..44c685c 100644
--- a/bin/match/match.c
+++ b/bin/match/match.c
@@ -72,31 +72,46 @@ match_catalog_read_write_all(struct matchparams *p, size_t 
*permutation,
 
 
   /* Go over each column and permute its contents. */
-  for(tmp=cat; tmp!=NULL; tmp=tmp->next)
-    {
-      /* Do the permutation. */
-      gal_permutation_apply(tmp, permutation);
-
-      /* Correct the size of the array so only the matching columns are
-         saved as output. This is only Gnuastro's convention, it has no
-         effect on later freeing of the array in the memory. */
-      if(p->notmatched)
-        {
-          /* Add the original array pointer to a list (we need to reset it
-             later). */
-          gal_list_void_add(&arrays, tmp->array);
-
-          /* Reset the data structure's array element to start where the
-             non-matched elements start. */
-          tmp->array=gal_pointer_increment(tmp->array, nummatched, tmp->type);
-
-          /* Correct the size of the tile. */
-          tmp->size = tmp->dsize[0] = tmp->size - nummatched;
-        }
-      else
-        tmp->size=tmp->dsize[0]=nummatched;
-    }
+  if(permutation)
+    for(tmp=cat; tmp!=NULL; tmp=tmp->next)
+      {
+        /* Do the permutation. */
+        gal_permutation_apply(tmp, permutation);
+
+        /* Correct the size of the array so only the matching columns are
+           saved as output. This is only Gnuastro's convention, it has no
+           effect on later freeing of the array in the memory. */
+        if(p->notmatched)
+          {
+            /* Add the original array pointer to a list (we need to reset it
+               later). */
+            gal_list_void_add(&arrays, tmp->array);
+
+            /* Reset the data structure's array element to start where the
+               non-matched elements start. */
+            tmp->array=gal_pointer_increment(tmp->array, nummatched,
+                                             tmp->type);
+
+            /* Correct the size of the tile. */
+            tmp->size = tmp->dsize[0] = tmp->size - nummatched;
+          }
+        else
+          tmp->size=tmp->dsize[0]=nummatched;
+      }
 
+  /* If no match was found (`permutation==NULL'), and the matched columns
+     are requested, empty all the columns that are to be written (only
+     keeping the meta-data). */
+  else
+    if(p->notmatched==0)
+      {
+        for(tmp=cat; tmp!=NULL; tmp=tmp->next)
+          {
+            tmp->size=0;
+            free(tmp->dsize); tmp->dsize=NULL;
+            free(tmp->array); tmp->array=NULL;
+          }
+      }
 
   /* Write the catalog to the output. */
   if(p->outcols)
@@ -189,81 +204,80 @@ match_catalog(struct matchparams *p)
   mcols=gal_match_coordinates(p->cols1, p->cols2, p->aperture->array, 0, 1,
                               p->cp.minmapsize, &nummatched);
 
-  /* If a match was found, then make the output files. */
-  if(mcols)
+  /* If the output is to be taken from the input columns (it isn't just the
+     log), then do the job. */
+  if(p->logasoutput==0)
     {
-      /* Read all the first catalog columns. */
-      if(p->logasoutput==0)
+      /* Read (and possibly write) the outputs. Note that we only need to
+         read the table when it is necessary for the output (the user might
+         have asked for `--outcols', only with columns of one of the two
+         inputs). */
+      if(p->outcols==NULL || p->acols)
+        a=match_catalog_read_write_all(p, mcols?mcols->array:NULL,
+                                       nummatched, 1, &acolmatch);
+      if(p->outcols==NULL || p->bcols)
+        b=match_catalog_read_write_all(p, mcols?mcols->next->array:NULL,
+                                       nummatched, 2, &bcolmatch);
+
+      /* If one catalog (with specific columns from either of the two
+         inputs) was requested, then write it out. */
+      if(p->outcols)
         {
-          /* Read (and possibly write) the outputs. Note that we only need
-             to read the table when it is necessary for the output (the
-             user user might have asked for `--outcols', only with columns
-             of one of the two inputs. */
-          if(p->outcols==NULL || p->acols)
-            a=match_catalog_read_write_all(p, mcols->array, nummatched,
-                                           1, &acolmatch);
-          if(p->outcols==NULL || p->bcols)
-            b=match_catalog_read_write_all(p, mcols->next->array, nummatched,
-                                           2, &bcolmatch);
-
-          /* If one catalog (with specific columns from either of the two
-             inputs) was requested, then write it out. */
-          if(p->outcols)
-            {
-              /* Arrange the columns and write the output. */
-              match_catalog_write_one(p, a, b, acolmatch, bcolmatch);
+          /* Arrange the columns and write the output. */
+          match_catalog_write_one(p, a, b, acolmatch, bcolmatch);
 
-              /* Clean up. */
-              if(acolmatch) free(acolmatch);
-              if(bcolmatch) free(bcolmatch);
-            }
+          /* Clean up. */
+          if(acolmatch) free(acolmatch);
+          if(bcolmatch) free(bcolmatch);
         }
+    }
 
-      /* Write the raw information in a log file if necessary.  */
-      if(p->logname)
-        {
-          /* Note that unsigned 64-bit integers are not recognized in FITS
-             tables. So if the log file is a FITS table, covert the two
-             index columns to uint32. */
-          tmp=gal_data_copy_to_new_type(mcols, GAL_TYPE_UINT32);
-          tmp->next=mcols->next;
-          tmp->size=nummatched;
-          gal_data_free(mcols);
-          mcols=tmp;
-
-          /* We also want everything to be incremented by one. In a C
-             program, counting starts with zero, so `gal_match_coordinates'
-             will return indexs starting from zero. But outside a C
-             program, on the command-line people expect counting to start
-             from 1 (for example with AWK). */
-          uf = (u=mcols->array) + tmp->size; do (*u)++; while(++u<uf);
-
-          /* Same for the second set of indexs. */
-          tmp=gal_data_copy_to_new_type(mcols->next, GAL_TYPE_UINT32);
-          uf = (u=tmp->array) + tmp->size; do (*u)++; while(++u<uf);
-          tmp->next=mcols->next->next;
-          gal_data_free(mcols->next);
-          tmp->size=nummatched;
-          mcols->next=tmp;
-
-          /* Correct the comments. */
-          free(mcols->comment);
-          mcols->comment="Row index in first catalog (counting from 1).";
-          free(mcols->next->comment);
-          mcols->next->comment="Row index in second catalog (counting "
-            "from 1).";
-
-          /* Write them into the table. */
-          gal_table_write(mcols, NULL, p->cp.tableformat, p->logname,
-                          "LOG_INFO", 0);
-
-          /* Set the comment pointer to NULL: they weren't allocated. */
-          mcols->comment=NULL;
-          mcols->next->comment=NULL;
-        }
-      gal_list_data_free(mcols);
+  /* Write the raw information in a log file if necessary.  */
+  if(p->logname && mcols)
+    {
+      /* Note that unsigned 64-bit integers are not recognized in FITS
+         tables. So if the log file is a FITS table, covert the two
+         index columns to uint32. */
+      tmp=gal_data_copy_to_new_type(mcols, GAL_TYPE_UINT32);
+      tmp->next=mcols->next;
+      tmp->size=nummatched;
+      gal_data_free(mcols);
+      mcols=tmp;
+
+      /* We also want everything to be incremented by one. In a C
+         program, counting starts with zero, so `gal_match_coordinates'
+         will return indexs starting from zero. But outside a C
+         program, on the command-line people expect counting to start
+         from 1 (for example with AWK). */
+      uf = (u=mcols->array) + tmp->size; do (*u)++; while(++u<uf);
+
+      /* Same for the second set of indexs. */
+      tmp=gal_data_copy_to_new_type(mcols->next, GAL_TYPE_UINT32);
+      uf = (u=tmp->array) + tmp->size; do (*u)++; while(++u<uf);
+      tmp->next=mcols->next->next;
+      gal_data_free(mcols->next);
+      tmp->size=nummatched;
+      mcols->next=tmp;
+
+      /* Correct the comments. */
+      free(mcols->comment);
+      mcols->comment="Row index in first catalog (counting from 1).";
+      free(mcols->next->comment);
+      mcols->next->comment="Row index in second catalog (counting "
+        "from 1).";
+
+      /* Write them into the table. */
+      gal_table_write(mcols, NULL, p->cp.tableformat, p->logname,
+                      "LOG_INFO", 0);
+
+      /* Set the comment pointer to NULL: they weren't allocated. */
+      mcols->comment=NULL;
+      mcols->next->comment=NULL;
     }
 
+  /* Clean up. */
+  gal_list_data_free(mcols);
+
   /* Print the number of matches if not in quiet mode. */
   if(!p->cp.quiet)
     fprintf(stdout, "%zu\n", nummatched);
diff --git a/bin/mkcatalog/main.h b/bin/mkcatalog/main.h
index 4519191..9376d7d 100644
--- a/bin/mkcatalog/main.h
+++ b/bin/mkcatalog/main.h
@@ -37,8 +37,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 /* Multiple of given number to stop searching for upper-limit magnitude. */
-#define MKCATALOG_UPPERLIMIT_STOP_MULTIP 50
-#define MKCATALOG_UPPERLIMIT_MINIMUM_NUM 20
+#define MKCATALOG_UPPERLIMIT_MINIMUM_NUM     20
+#define MKCATALOG_UPPERLIMIT_MAXFAILS_MULTIP 10
 
 
 /* Unit string to use if values dataset doesn't have any. */
diff --git a/bin/mkcatalog/ui.c b/bin/mkcatalog/ui.c
index 5554ef8..e418f68 100644
--- a/bin/mkcatalog/ui.c
+++ b/bin/mkcatalog/ui.c
@@ -1213,7 +1213,7 @@ ui_preparations_outnames(struct mkcatalogparams *p)
     }
 
   /* If an upperlimit check image is requsted, then set its filename. */
-  if(p->checkuplim)
+  if(p->checkuplim[0]!=GAL_BLANK_INT32)
     {
       /* See if the directory should be respected. */
       p->cp.keepinputdir = p->cp.output ? 1 : p->cp.keepinputdir;
diff --git a/bin/mkcatalog/upperlimit.c b/bin/mkcatalog/upperlimit.c
index 32dc016..c28be22 100644
--- a/bin/mkcatalog/upperlimit.c
+++ b/bin/mkcatalog/upperlimit.c
@@ -577,11 +577,11 @@ upperlimit_one_tile(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
   uint8_t *M=NULL, *st_m=NULL;
   int continueparse, writecheck=0;
   struct gal_list_f32_t *check_s=NULL;
+  size_t d, counter=0, se_inc[2], nfailed=0;
   float *V, *st_v, *uparr=pp->up_vals->array;
-  size_t d, tcounter=0, counter=0, se_inc[2];
   size_t min[3], max[3], increment, num_increment;
   int32_t *O, *OO, *oO, *st_o, *st_oo, *st_oc, *oC=NULL;
-  size_t maxcount = p->upnum * MKCATALOG_UPPERLIMIT_STOP_MULTIP;
+  size_t maxfails = p->upnum * MKCATALOG_UPPERLIMIT_MAXFAILS_MULTIP;
   struct gal_list_sizet_t *check_x=NULL, *check_y=NULL, *check_z=NULL;
   size_t *rcoord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
                                       "rcoord");
@@ -620,7 +620,7 @@ upperlimit_one_tile(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
 
 
   /* Continue measuring randomly until we get the desired total number. */
-  while(tcounter<maxcount && counter<p->upnum)
+  while(nfailed<maxfails && counter<p->upnum)
     {
       /* Get the random coordinates. */
       for(d=0;d<ndim;++d)
@@ -687,9 +687,16 @@ upperlimit_one_tile(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
           else break;
         }
 
+
       /* Further processing is only necessary if this random tile was fully
-         parsed. */
-      if(continueparse) uparr[ counter++ ] = sum;
+         parsed. If it was, we must reset `nfailed' to zero again. */
+      if(continueparse)
+        {
+          nfailed=0;
+          uparr[ counter++ ] = sum;
+        }
+      else ++nfailed;
+
 
       /* If a check is necessary, write in the values (in FITS
          coordinates). */
@@ -715,9 +722,6 @@ upperlimit_one_tile(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
             }
           gal_list_f32_add(&check_s, continueparse ? sum : NAN);
         }
-
-      /* Increment the total-counter. */
-      ++tcounter;
     }
 
   /* If a check is necessary, then write the values. */
diff --git a/bootstrap b/bootstrap
index 002edf6..ed3b0a4 100755
--- a/bootstrap
+++ b/bootstrap
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Print a version string.
-scriptversion=2018-05-27.20; # UTC
+scriptversion=2018-07-01.02; # UTC
 
 # Bootstrap this package from checked-out sources.
 
@@ -942,13 +942,13 @@ fi
 if $use_gnulib; then
   gnulib_tool_options="\
    --no-changelog\
-   --aux-dir $build_aux\
-   --doc-base $doc_base\
-   --lib $gnulib_name\
-   --m4-base $m4_base/\
-   --source-base $source_base/\
-   --tests-base $tests_base\
-   --local-dir $local_gl_dir\
+   --aux-dir=$build_aux\
+   --doc-base=$doc_base\
+   --lib=$gnulib_name\
+   --m4-base=$m4_base/\
+   --source-base=$source_base/\
+   --tests-base=$tests_base\
+   --local-dir=$local_gl_dir\
    $gnulib_tool_option_extras\
   "
   if test $use_libtool = 1; then
diff --git a/configure.ac b/configure.ac
index dcb470f..95f6d0e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -87,9 +87,56 @@ AC_DEFINE([IN_GNUASTRO_BUILD], [1], [In building, not usage])
 
 
 
-# Add warnings and high optimization flags for all builds.
-CFLAGS="-Wall -O3 $CFLAGS"
-CXXFLAGS="-Wall -O3 $CXXFLAGS"
+# See if `make check' should be made with Valgrind. This should be done
+# before checking the compilation flags because it can re-write
+# `enable-debug').
+AC_ARG_ENABLE(check-with-valgrind,
+              [AS_HELP_STRING([--enable-check-with-valgrind],
+                              [Run `make check' programs within Valgrind])],
+              [AS_IF([test "x$enable_check_with_valgrind" != xno],
+                     [enable_check_with_valgrind=yes])],
+              [enable_check_with_valgrind=no])
+AS_IF([test "x$enable_check_with_valgrind" = "xyes"],
+      [
+        enable_debug=yes;
+        AC_CHECK_PROG(has_valgrind, valgrind, [yes], [no])
+        AS_IF([test "x$has_valgrind" = "xno"],
+              [AC_MSG_ERROR([Valgrind not found. Please install it or don't 
use `--enable-check-with-valgrind'])])
+      ])
+AM_CONDITIONAL([COND_CHECK_WITH_VALGRIND], [test 
"x$enable_check_with_valgrind" = "xyes"])
+
+
+
+
+
+# Set the compilation flags.
+AC_ARG_ENABLE(debug,
+              [AS_HELP_STRING([--enable-debug],
+                              [No optimization, build with debug flags.])],
+              [AS_IF([test "x$enable_debug" != xno], [enable_debug=yes])],
+              [enable_debug=no])
+AS_IF([test "x$enable_debug" = "xyes"],
+      [cflags_add="-g -O0"],
+      [cflags_add="-O3"])
+CFLAGS="-Wall $cflags_add $CFLAGS"
+CXXFLAGS="-Wall $cflags_add $CXXFLAGS"
+
+
+
+
+
+# See if the C++ compiler was present. `CXX' has already been set by
+# `AC_PROG_CXX' (above). According to the Autoconf manual: "if none of the
+# [AC_PROG_CXX] checks succeed, then as a last resort [it will] set `CXX'
+# to `g++'". Therefore, we can't rely on it to see if the compiler
+# executable is actually usable. Therefore tests that rely on it will
+# fail. Unfortunately some OSs (like Fedora), don't install `g++' with
+# `gcc', so it may not always be present. To fix this, here we are just
+# using `AC_CHECK_PROG' to see if the `CXX' executable is reachable in the
+# search path and if it isn't, we'll disable the C++ check during `make
+# check' with an Automake conditional.
+AC_CHECK_PROG(has_cxx, $CXX, "yes", "no")
+AM_CONDITIONAL([COND_HASCXX], [test "x$has_cxx" = "xyes"])
 
 
 
@@ -218,6 +265,7 @@ AC_CHECK_DECLS(gsl_interp_steffen,
                [ gsl_version_old=yes; anywarnings=yes ],
                [[#include <gsl/gsl_interp.h>]])
 
+
 # Since version 0.42, if `libcurl' is installed, CFITSIO will link with it
 # and thus it will be necessary to explicitly link with libcurl also. If it
 # doesn't exist on the system, then CFITSIO won't link with it and there is
@@ -229,10 +277,21 @@ AC_SEARCH_LIBS([ffopen], [cfitsio], [],
 AC_SEARCH_LIBS([wcspih], [wcs], [],
     [AC_MSG_ERROR([WCSLIB not found, cannot continue.])])
 
+
 # These are secondary tests for more fine-grained control in libraries that
 # have already been checked. We don't need to add them to the LIBS
 # variable, so we are using AC_CHECK_LIB for these tests.
 
+# If the CFITSIO library has the `fits_is_reentrant' function (it was added
+# since version 3.30 of April 2012).
+AC_CHECK_LIB([cfitsio], [fits_is_reentrant], [has_fits_is_reentrant=1],
+             [has_fits_is_reentrant=0], [-lm])
+AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_FITS_IS_REENTRANT],
+                   [$has_fits_is_reentrant],
+                   [CFITSIO has the fits_is_reentrant function])
+AC_SUBST(HAVE_FITS_IS_REENTRANT, [$has_fits_is_reentrant])
+
+
 # If the WCS library has the `wcslib_version' function.
 AC_CHECK_LIB([wcs], [wcslib_version], [has_wcslib_version=1],
              [has_wcslib_version=0], [-lcfitsio -lm])
@@ -240,6 +299,7 @@ AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_WCSLIB_VERSION], 
[$has_wcslib_version],
                    [WCSLIB comes with wcslib_version])
 AC_SUBST(HAVE_WCSLIB_VERSION, [$has_wcslib_version])
 
+
 # If the pthreads library has `pthread_barrier_wait'.
 AC_CHECK_LIB([pthread], [pthread_barrier_wait], [has_pthread_barrier=1],
              [has_pthread_barrier=0])
@@ -258,6 +318,7 @@ gl_INIT
 
 
 
+
 # Gnulib checks for the proper name for the C99 equivalent `restrict'
 # keyword and puts it in the `ac_cv_c_restrict' variable. If none exists,
 # it will put a `no' inside of this variable. As described in the output
diff --git a/developer-build b/developer-build
index 22b8ae2..e188c4d 100755
--- a/developer-build
+++ b/developer-build
@@ -44,6 +44,7 @@ check=0
 reconf=0
 upload=0
 install=0
+valgrind=0
 top_build_dir=/dev/shm
 if [ -f .version ]; then
     version=$(cat .version)
@@ -73,6 +74,13 @@ help_print() {
         debug_status="ENABLED"
     fi
 
+    # See if valgrind is enabled or not.
+    if [ $valgrind = "0" ]; then
+        valgrind_status="DISABLED"
+    else
+        valgrind_status="ENABLED"
+    fi
+
     # See if clean is enabled or not.
     if [ $clean = "0" ]; then
         clean_status="DISABLED"
@@ -146,6 +154,9 @@ Options:
                           libraries.
                           Current status: $debug_status
 
+ -v, --valgrind           Build with `--enable-check-with-valgrind'.
+                          Current status: $valgrind_status
+
  -j, --jobs INT           Number of threads to use in 'make'.
                           Current value: $jobs
 
@@ -218,6 +229,10 @@ do
             debug=1
             shift # past argument
             ;;
+        -v|--valgrind)
+            valgrind=1
+            shift # past argument
+            ;;
         -j|--jobs)
             jobs="$2"
             if [ x"jobs" = x ]; then
@@ -397,11 +412,17 @@ cd $build_dir
 # (in 'configure.ac'), we have set optimization flags which have to be
 # cancelled in debugging.
 if [ ! -f Makefile ]; then
+
+    # Set the proper flags.
     if [ x$debug = x1 ]; then
-        $srcdir/configure --srcdir=$srcdir CFLAGS="-g -O0" --disable-shared
-    else
-        $srcdir/configure --srcdir=$srcdir
+        confopts="--enable-debug --disable-shared"
     fi
+    if [ x$valgrind = x1 ]; then
+        confopts="--enable-check-with-valgrind --disable-shared"
+    fi
+
+    # Run the configure script.
+    $srcdir/configure --srcdir=$srcdir $confopts
 fi
 
 
diff --git a/doc/announce-acknowledge.txt b/doc/announce-acknowledge.txt
index 1f82afb..49ab4ba 100644
--- a/doc/announce-acknowledge.txt
+++ b/doc/announce-acknowledge.txt
@@ -1,6 +1,13 @@
 Alphabetically ordered list to acknowledge in the next release.
 
+Valentina Abril-melgarejo
 Leindert Boogaard
 Nushkia Chamba
 Takashi Ichikawa
+Oryna Ivashtenko
+Geoffry Krouchi
 Alan Lefor
+Juan Molina Tobar
+Dmitrii Oparin
+Teymoor Saifollahi
+Jenny Sorce
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index 95ac3b4..8fbbcf8 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -2309,18 +2309,20 @@ Gnuastro.
 In this tutorial, we'll use the HST
 @url{https://archive.stsci.edu/prepds/xdf, eXtreme Deep Field}
 dataset. Like almost all astronomical surveys, this dataset is free for
-download and usable by the public. This tutorial was first prepared for the
-``Exploring the Ultra-Low Surface Brightness Universe'' workshop (November
-2017) at the International Space Science Institute (ISSI) in Bern,
-Switzerland. We would like to thank them and the attendees for a very
-fruitful week.
-
-You will need the following tools in this tutorial: Gnuastro, SAO DS9
-@footnote{See @ref{SAO ds9}, available at
-@url{http://ds9.si.edu/site/Home.html}.}, GNU
-Wget@footnote{@url{https://www.gnu.org/software/wget}.}, and AWK (most
+download and usable by the public. You will need the following tools in
+this tutorial: Gnuastro, SAO DS9 @footnote{See @ref{SAO ds9}, available at
+@url{http://ds9.si.edu/site/Home.html}}, GNU
+Wget@footnote{@url{https://www.gnu.org/software/wget}}, and AWK (most
 common implementation is GNU
-AWK@footnote{@url{https://www.gnu.org/software/gawk}.}).
+AWK@footnote{@url{https://www.gnu.org/software/gawk}}).
+
+This tutorial was first prepared for the ``Exploring the Ultra-Low Surface
+Brightness Universe'' workshop (November 2017) at the ISSI in Bern,
+Switzerland. It was further extended in the ``4th Indo-French Astronomy
+School'' (July 2018) organized by LIO, CRAL CNRS UMR5574, UCBL, and IUCAA
+in Lyon, France. We are very greatful to the organizers of these workshops
+and the attendees for the very fruitful discussions and suggestions that
+made this tutorial possible.
 
 @cartouche
 @noindent
@@ -2605,7 +2607,7 @@ $ echo $n $r
 ## (9)  Use the number of pixels (first number passed to AWK) and
 ##      length of each pixel's edge (second number passed to AWK)
 ##      to estimate the area of the field in arc-minutes squared.
-$ area=(echo $n $r | awk '@{print $1 * ($2^2) * 3600@}')
+$ area=$(echo $n $r | awk '@{print $1 * ($2^2) * 3600@}')
 @end example
 
 The area of this field is 4.03817 (or 4.04) arc-minutes squared. Just for
@@ -2839,7 +2841,7 @@ format, a white-space character is also enough.
 
 @example
 $ astcosmiccal -H70    -z2
-$ astcosmiccal --H0 70 -z2 --arcsectandisk
+$ astcosmiccal --H0 70 -z2 --arcsectandist
 @end example
 
 Let's assume that in one project, you want to only use rounded cosmological
@@ -3201,21 +3203,21 @@ program:
 
 @example
 $ rm *.fits
-$ mkdir segment
-$ astsegment nc/xdf-f160w.fits -osegment/xdf-f160w.fits
+$ mkdir seg
+$ astsegment nc/xdf-f160w.fits -oseg/xdf-f160w.fits
 @end example
 
 Segment's operation is very much like NoiseChisel (in fact, prior to
 version 0.6, it was part of NoiseChisel), for example the output is a
-multi-extension FITS file, it has check images and uses the signal-to-noise
-ratio of undetected regions as a reference. Please have a look at Segment's
-multi-extension output with @command{ds9} to get a good feeling of what it
-has done. Like NoiseChisel, the first extension is the input. The
-@code{CLUMPS} extension shows the true ``clumps'' with values that are
-@mymath{\ge1}, and the diffuse regions labeled as @mymath{-1}. In the
-@code{OBJECTS} extension, we see that the large detections of NoiseChisel
-(that may have contained many galaxies) are now broken up into separate
-labels. see @ref{Segment} for more.
+multi-extension FITS file, it has check images and uses the undetected
+regions as a reference. Please have a look at Segment's multi-extension
+output with @command{ds9} to get a good feeling of what it has done. Like
+NoiseChisel, the first extension is the input. The @code{CLUMPS} extension
+shows the true ``clumps'' with values that are @mymath{\ge1}, and the
+diffuse regions labeled as @mymath{-1}. In the @code{OBJECTS} extension, we
+see that the large detections of NoiseChisel (that may have contained many
+galaxies) are now broken up into separate labels. see @ref{Segment} for
+more.
 
 Having localized the regions of interest in the dataset, we are ready to do
 measurements on them with @ref{MakeCatalog}. Besides the IDs, we want to
@@ -3341,35 +3343,124 @@ robust for calculating the colors (compared to 
objects). Therefore from
 this step onward, we'll continue with clumps.
 
 We can finally calculate the colors of the objects from these two
-datasets. We'll merge them into one table using the @command{paste} program
-on the command-line. But, we only want the magnitude from the F105W
-dataset, so we'll only pull out the @code{MAGNITUDE} and @code{SN}
-column. The output of @command{paste} will have each line of both catalogs
-merged into a single line.
+datasets. If you inspect the contents of the two catalogs, you'll notice
+that because they were both derived from the same segmentation maps, the
+rows are ordered identically (they correspond to the same object/clump in
+both filters). But to be generic (usable even when the rows aren't ordered
+similarly) and display another useful program in Gnuastro, we'll use
+@ref{Match}.
+
+As the name suggests, Gnuastro's Match program will match rows based on
+distance (or aperture in 2D) in one (or two) columns. In the command below,
+the options relating to each catalog are placed under it for easy
+understanding. You give Match two catalogs (from the two different filters
+we derived above) as argument, and the HDUs containing them (if they are
+FITS files) with the @option{--hdu} and @option{--hdu2} options. The
+@option{--ccol1} and @option{--ccol2} options specify which columns should
+be matched with which in the two catalogs. With @option{--aperture} you
+specify the acceptable error (radius in 2D), in the same units as the
+columns (see below for why we have requested an aperture of 0.35
+arcseconds, or less than 6 HST pixels).
+
+The @option{--outcols} is a very convenient feature in Match: you can use
+it to specify which columns from the two catalogs you want in the
+output. If the first character is an `@key{a}', the respective matched
+column (number or name, similar to Table above) in the first catalog will
+be written in the output table. When the first character is a `@key{b}',
+the respective column from the second catalog will be written in the
+output. You can use this to mix the desired matched columns from both
+catalogs in the output.
+
+@example
+$ astmatch cat/xdf-f160w.fits           cat/xdf-f105w.fits         \
+           --hdu=CLUMPS                 --hdu2=CLUMPS              \
+           --ccol1=RA,DEC               --ccol2=RA,DEC             \
+           --aperture=0.35/3600                                    \
+           --outcols=a1,a2,aRA,aDEC,aMAGNITUDE,aSN,bMAGNITUDE,bSN  \
+           --log --output=cat/xdf-f160w-f105w.fits
+@end example
+
+By default (when @option{--quiet} isn't called), the Match program will
+just print the number of matched rows in the standard output. If you have a
+look at your input catalogs, this should be the same as the number of rows
+in them. Let's have a look at the columns in the matched catalog:
+
+@example
+$ asttable cat/xdf-f160w-f105w.fits -i
+@end example
+
+Indeed, its exactly the columns we wanted. There is just one confusion
+however: there are two @code{MAGNITUDE} and @code{SN} columns. Right now,
+you know that the first one was from the F160W filter, and the second was
+for F105W. But in one hour, you'll start doubting your self: going through
+your command history, trying to answer this question: ``which magnitude
+corresponds to which filter?''. You should never torture your future-self
+(or colleagues) like this! So, let's rename these confusing columns in the
+matched catalog. The FITS standard for tables stores the column names in
+the @code{TTYPE} header keywords, so let's have a look:
 
 @example
-$ asttable cat/xdf-f160w.fits -h2                > xdf-f160w.txt
-$ asttable cat/xdf-f105w.fits -h2 -cMAGNITUDE,SN > xdf-f105w.txt
-$ paste xdf-f160w.txt xdf-f105w.txt              > xdf-f160w-f105w.txt
+$ astfits cat/xdf-f160w-f105w.fits -h1 | grep TTYPE
 @end example
 
-Open @file{xdf-f160w-f105w.txt} to see how @command{paste} has operated. We
-can now use AWK to find the colors. We'll ask AWK to only use lines that
-don't have a NaN magnitude in the 7th column (F105W
-magnitude@footnote{Recall that the objects and clumps labels were made on
-the F160W image. On the F105W image, there might not be enough signal, so
-random scatter may give a negative total brightness and thus a NaN
-magnitude.}). We will also ignore columns which don't have reliable F105W
-measurement (with a S/N less than 7@footnote{The value of 7 is taken from
-the clump S/N threshold in F160W (where the clumps were defined).}). For
-the other lines, AWK will print the IDs, positional columns and the
-difference between the respective magnitude columns.
+Changing/updating the column names is as easy as updating the values to
+these options with the first command below, and with the second, confirm
+this change:
 
 @example
-$ awk '$7!="nan" && $8>7  @{print $1, $2, $3, $4, $7-$5@}' \
-      xdf-f160w-f105w.txt > cat/xdf-f105w-f160w_c.txt
+$ astfits cat/xdf-f160w-f105w.fits -h1                          \
+          --update=TTYPE5,MAG_F160W   --update=TTYPE6,SN_F160W  \
+          --update=TTYPE7,MAG_F105W   --update=TTYPE8,SN_F105W
+$ asttable cat/xdf-f160w-f105w.fits -i
 @end example
 
+
+If you noticed, when running Match, the previous command, we also asked for
+@option{--log}. Many Gnuastro programs have this option to provide some
+detailed information on their operation in case you are curious. Here, we
+are using it to justify the value we gave to @option{--aperture}. Even
+though you asked for the output to be written in the @file{cat} directory,
+a listing of the contents of your current directory will show you an extra
+@file{astmatch.fits} file. Let's have a look at what columns it contains.
+
+@example
+$ ls
+$ asttable astmatch.log -i
+@end example
+
+@c********************************
+@c We'll merge them into one table using the @command{paste} program
+@c on the command-line. But, we only want the magnitude from the F105W
+@c dataset, so we'll only pull out the @code{MAGNITUDE} and @code{SN}
+@c column. The output of @command{paste} will have each line of both catalogs
+@c merged into a single line.
+
+@c @example
+@c $ asttable cat/xdf-f160w.fits -h2                > xdf-f160w.txt
+@c $ asttable cat/xdf-f105w.fits -h2 -cMAGNITUDE,SN > xdf-f105w.txt
+@c $ paste xdf-f160w.txt xdf-f105w.txt              > xdf-f160w-f105w.txt
+@c @end example
+
+@c Open @file{xdf-f160w-f105w.txt} to see how @command{paste} has operated.
+@c ********************************
+
+@cindex Flux-weighted
+@cindex SED, Spectral Energy Distribution
+@cindex Spectral Energy Distribution, SED
+The @file{MATCH_DIST} column contains the distance of the matched rows,
+let's have a look at the distribution of values in this column. You might
+be asking yourself ``why should the positions of the two filters differ
+when I gave MakeCatalog the same segmentation map?'' The reason is that the
+central positions are @emph{flux-weighted}. Therefore the
+@option{--valuesfile} dataset you give to MakeCatalog will also affect the
+center measurements@footnote{To only measure the center based on the
+labeled pixels (and ignore the pixel values), you can ask for the columns
+that contain @option{geo} (for geometric) in them. For example
+@option{--geow1} or @option{--geow2} for the RA and Declination (first and
+second world-coordinates).}. Recall that the Spectral Energy Distribution
+(SED) of galaxies is not flat and they have substructure, therefore, they
+can have different shapes/morphologies in different filters.
+
 Gnuastro has a simple program for basic statistical analysis. The command
 below will print some basic information about the distribution (minimum,
 maximum, median and etc), along with a cute little ASCII histogram to
@@ -3381,7 +3472,36 @@ working on a server (where you may not have graphic user 
interface), and
 finally, its fast.
 
 @example
-$ aststatistics cat/xdf-f105w-f160w_c.txt -c5
+$ aststatistics astmatch.fits -cMATCH_DIST
+@end example
+
+The units of this column are the same as the columns you gave to Match: in
+degrees. You see that while almost all the objects matched very nicely, the
+maximum distance is roughly 0.31 arcseconds. This is why we asked for an
+aperture of 0.35 arcseconds when doing the match.
+
+We can now use AWK to find the colors. We'll ask AWK to only use rows that
+don't have a NaN magnitude in either filter@footnote{This can happen even
+on the reference image. It is because of the current way clumps are defined
+in Segment when they are placed on strong gradients. It is because of high
+``river'' values on such gradients. See @ref{Segment changes after
+publication}. To avoid this problem, you can currently ask for the
+@option{--brighntessnoriver} output column.}. We will also ignore columns
+which don't have reliable F105W measurement (with a S/N less than
+7@footnote{The value of 7 is taken from the clump S/N threshold in F160W
+(where the clumps were defined).}).
+
+@example
+$ asttable cat/xdf-f160w-f105w.fits -cMAG_F160W,MAG_F105W,SN_F105W  \
+           | awk '$1!="nan" && $2!="nan" && $3>7 @{print $2-$1@}'     \
+           > f105w-f160w.txt
+@end example
+
+You can inspect the distribution of colors with the Statistics program
+again:
+
+@example
+$ aststatistics f105w-f160w.txt -c1
 @end example
 
 You can later use Gnuastro's Statistics program with the
@@ -3389,10 +3509,10 @@ You can later use Gnuastro's Statistics program with the
 a table to feed into your favorite plotting program for a much more
 accurate/appealing plot (for example with PGFPlots in @LaTeX{}). If you
 just want a specific measure, for example the mean, median and standard
-deviation, you can ask for them specifically:
+deviation, you can ask for them specifically with this command:
 
 @example
-$ aststatistics catalog/xdf-f105w-f160w_c.txt -c5 --mean --median --std
+$ aststatistics f105w-f160w.txt -c1 --mean --median --std
 @end example
 
 Some researchers prefer to have colors in a fixed aperture for all the
@@ -3405,13 +3525,13 @@ detection image.
 
 @cindex GNU AWK
 To generate the apertures catalog, we'll first read the positions from
-F160W catalog we generated before for the positions and set the other
-parameters of each profile to be a fixed circle of radius 5 pixels (we want
-all apertures to be identical after all).
+F160W catalog and set the other parameters of each profile to be a fixed
+circle of radius 5 pixels (we want all apertures to be identical in this
+scenario).
 
 @example
-$ rm *.txt
-$ asttable cat/xdf-f160w.fits -h2 -cRA,DEC                       \
+$ rm *.fits *.txt
+$ asttable cat/xdf-f160w.fits -hCLUMPS -cRA,DEC                    \
            | awk '!/^#/@{print NR, $1, $2, 5, 5, 0, 0, 1, NR, 1@}' \
            > apertures.txt
 @end example
@@ -3434,7 +3554,7 @@ $ astmkprof apertures.txt 
--background=flat-ir/xdf-f160w.fits     \
 The first thing you might notice in the printed information is that the
 profiles are not built in order. This is because MakeProfiles works in
 parallel, and parallel CPU operations are asynchronous. You can try running
-MakeProfiles with one thread (using @option{--numthreads=1} to see how
+MakeProfiles with one thread (using @option{--numthreads=1}) to see how
 order is respected in that case.
 
 Open the output @file{apertures.fits} file and see the result. Where the
@@ -3445,11 +3565,11 @@ are interested, please join us in completing Gnuastro 
with added
 improvements like this (see task 14750
 @footnote{@url{https://savannah.gnu.org/task/index.php?14750}}).
 
-We can now feed the labeled @file{apertures.fits} labeled image into
-MakeCatalog instead of Segment's output as shown below. In comparison with
-the previous MakeCatalog call, you will notice that there is no more
+We can now feed the @file{apertures.fits} labeled image into MakeCatalog
+instead of Segment's output as shown below. In comparison with the previous
+MakeCatalog call, you will notice that there is no more
 @option{--clumpscat} option, since each aperture is treated as a separate
-object.
+``object'' here.
 
 @example
 $ astmkcatalog apertures.fits -h1 --zeropoint=26.27        \
@@ -3465,26 +3585,34 @@ name and zeropoint magnitudes and run this command 
again to have the fixed
 aperture magnitude in the F160W filter and measure colors on apertures.
 
 @cindex GNU AWK
-Let's find some of the objects with the strongest color difference and make
-a cutout to inspect them visually: let's see what the objects with a color
-more than two magnitudes look like. We'll use the
-@file{cat/xdf-f105w-f160w_c.txt} file that we made above. With the command
-below, all lines with a color more than 1.5 will be put in @file{reddest.txt}
+As a final step, let's go back to the original clumps-based catalogs we
+generated before. We'll find the objects with the strongest color and make
+a cutout to inspect them visually and finally, we'll see how they are
+located on the image.
+
+First, let's see what the objects with a color more than two magnitudes
+look like. As you see, this is very much like the command above for
+selecting the colors, only instead of printing the color, we'll print the
+RA and Dec. With the command below, the positions of all lines with a color
+more than 1.5 will be put in @file{reddest.txt}
 
 @example
-$ awk '$5>1.5' cat/xdf-f105w-f160w_c.txt > red.txt
+$ asttable cat/xdf-f160w-f105w.fits                                \
+           -cMAG_F160W,MAG_F105W,SN_F105W,RA,DEC                   \
+           | awk '$1!="nan" && $2!="nan" && $2-$1>1.5 && $3>7      \
+                  @{print $4,$5@}' > reddest.txt
 @end example
 
-We can now feed @file{red.txt} into Gnuastro's crop to see what these
+We can now feed @file{reddest.txt} into Gnuastro's crop to see what these
 objects look like. To keep things clean, we'll make a directory called
 @file{crop-red} and ask Crop to save the crops in this directory. We'll
 also add a @file{-f160w.fits} suffix to the crops (to remind us which image
-they came from).
+they came from). The width of the crops will be 15 arcseconds.
 
 @example
 $ mkdir crop-red
 $ astcrop --mode=wcs --coordcol=3 --coordcol=4 flat-ir/xdf-f160w.fits \
-          --catalog=red.txt --width=15/3600,15/3600                   \
+          --catalog=reddest.txt --width=15/3600,15/3600               \
           --suffix=-f160w.fits --output=crop-red
 @end example
 
@@ -3506,45 +3634,50 @@ $ for f in *.fits; do                                   
               \
 $ cd ..
 @end example
 
-You can now easily use your general graphic user interface image viewer to
-flip through the images more easily. On GNOME, you can use the ``Eye of
-GNOME'' image viewer (with executable name of @file{eog}). Run the command
-below and by pressing the @key{<SPACE>} key, you can flip through the
-images and compare them visually more easily. Of course, the flux ranges
-have been chosen generically here for seeing the fainter parts. Therefore,
-brighter objects will be fully black.
+You can now use your general graphic user interface image viewer to flip
+through the images more easily. On GNOME, you can use the ``Eye of GNOME''
+image viewer (with executable name of @file{eog}). Run the command below to
+open the first one (if you aren't using GNOME, use the command of your
+image viewer instead of @code{eog}):
 
 @example
 $ eog 1-f160w.jpg
 @end example
 
+In Eye of GNOME, you can flip through the images and compare them visually
+more easily by pressing the @key{<SPACE>} key. Of course, the flux ranges
+have been chosen generically here for seeing the fainter parts. Therefore,
+brighter objects will be fully black.
+
 @cindex GNU Parallel
 The @code{for} loop above to convert the images will do the job in series:
-each file is converted only after the previous ones are complete. If you
-have @url{https://www.gnu.org/software/parallel, GNU Parallel}, you can
-greatly speed up this conversion. GNU Parallel will run the separate
-commands simultaneously on different CPU threads in parallel. For more
-information on efficiently using your threads, see @ref{Multi-threaded
+each file is converted only after the previous one is complete. If you have
+@url{https://www.gnu.org/s/parallel, GNU Parallel}, you can greatly speed
+up this conversion. GNU Parallel will run the separate commands
+simultaneously on different CPU threads in parallel. For more information
+on efficiently using your threads, see @ref{Multi-threaded
 operations}. Here is a replacement for the shell @code{for} loop above
 using GNU Parallel.
 
 @example
 $ cd crop-red
-$ parallel astconvertt --fluxlow=-0.001 --fluxhigh=0.005 --invert      \
+$ parallel astconvertt --fluxlow=-0.001 --fluxhigh=0.005 --invert   \
            -ojpg ::: *.fits
 $ cd ..
 @end example
 
-Another thing that is commonly needed is to visually mark these objects on
-the image. DS9 has the ``Region''s concept for this purpose. You just have
-to convert your catalog into a ``region file'' to feed into DS9. To do
-that, you can use AWK again as shown below.
+@cindex DS9
+@cindex SAO DS9
+As the final action, let's see how these objects are positioned over the
+dataset. DS9 has the ``Region''s concept for this purpose. You just have to
+convert your catalog into a ``region file'' to feed into DS9. To do that,
+you can use AWK again as shown below.
 
 @example
 $ awk 'BEGIN@{print "# Region file format: DS9 version 4.1";     \
              print "global color=green width=2";                \
              print "fk5";@}                                      \
-       @{printf "circle(%s,%s,1\")\n", $3, $4;@}' reddest.txt     \
+       @{printf "circle(%s,%s,1\")\n", $1, $2;@}' reddest.txt     \
        > reddest.reg
 @end example
 
@@ -3558,12 +3691,14 @@ $ ds9 -mecube seg/xdf-f160w.fits -zscale -zoom to fit   
 \
       -regions load all reddest.reg
 @end example
 
-Finally, if this book or any of the programs in Gnuastro have been useful
-for your research, please cite the respective papers and share your
-thoughts and suggestions with us (it can be very encouraging). All Gnuastro
-programs have a @option{--cite} option to help you cite the authors' work
-more easily. Just note that it may be necessary to cite additional papers
-for different programs, so please try it out for any program you use.
+In conclusion, we hope this extended tutorial has been a good starting
+point to help in your exciting research. If this book or any of the
+programs in Gnuastro have been useful for your research, please cite the
+respective papers and share your thoughts and suggestions with us (it can
+be very encouraging). All Gnuastro programs have a @option{--cite} option
+to help you cite the authors' work more easily. Just note that it may be
+necessary to cite additional papers for different programs, so please try
+it out for any program you used.
 
 @example
 $ astmkcatalog --cite
@@ -4998,42 +5133,75 @@ we add it. Gnuastro itself if also already packaged in 
some package
 managers (for example Debian or Homebrew).
 
 As discussed above, we recommend installing the @emph{mandatory}
-dependencies manually from source. Therefore, in each command below, first
-the optional dependencies are given. The mandatory dependencies are
-included after an empty line. If you would also like to install the
-mandatory dependencies with your package manager, just ignore the empty
-line.
+dependencies manually from source (see @ref{Mandatory
+dependencies}). Therefore, in each command below, first the optional
+dependencies are given. The mandatory dependencies are included after an
+empty line. If you would also like to install the mandatory dependencies
+with your package manager, just ignore the empty line.
+
+For better archivability and compression ratios, Gnuastro's recommended
+tarball compression format is with the
+@url{http://lzip.nongnu.org/lzip.html, Lzip} program, see @ref{Release
+tarball}. Therefore, the package manager commands below also contain Lzip.
 
 @table @asis
-@item @command{apt-get} (Debian-based OSs: Ubuntu, Linux Mint and etc)
+@item @command{apt-get} (Debian-based OSs: Debian, Ubuntu, Linux Mint, and etc)
 @cindex Debian
 @cindex Ubuntu
+@cindex Linux Mint
 @cindex @command{apt-get}
 @cindex Advanced Packaging Tool (APT, Debian)
-Debian-based GNU/Linux
-distributions@footnote{@url{https://en.wikipedia.org/wiki/List_of_Linux_distributions#Debian-based}}
-(for example Ubuntu or its derivatives) are arguably the largest, and most
-used, class of GNU/Linux distributions. All such GNU/Linux distributions
-use Debian's Advanced Packaging Tool (APT, for example @command{apt-get})
-for managing packages.
-@example
-$ sudo apt-get install ghostscript libtool-bin  libjpeg-dev \
-                       libtiff-dev libgit2-dev              \
+@url{https://en.wikipedia.org/wiki/Debian,Debian} is one of the oldest
+GNU/Linux
+distributions@footnote{@url{https://en.wikipedia.org/wiki/List_of_Linux_distributions#Debian-based}}.
 It
+thus has a very extended user community and a robust internal structure and
+standards. All of it is free software and based on the work of volunteers
+around the world. Many distributions are thus derived from it, for example
+Ubuntu and Linux Mint. This arguably makes Debian-based OSs the largest,
+and most used, class of GNU/Linux distributions. All of them use Debian's
+Advanced Packaging Tool (APT, for example @command{apt-get}) for managing
+packages.
+@example
+$ sudo apt-get install ghostscript libtool-bin libjpeg-dev  \
+                       libtiff-dev libgit2-dev lzip         \
                                                             \
                        libgsl0-dev libcfitsio-dev wcslib-dev
 @end example
 
+@item @command{dnf}
+@itemx @command{yum} (Red Hat-based OSs: Red Hat, Fedora, CentOS, Scientific 
Linux, and etc)
+@cindex RHEL
+@cindex Fedora
+@cindex CentOS
+@cindex Red Hat
+@cindex @command{dnf}
+@cindex @command{yum}
+@cindex Scientific Linux
+@url{https://en.wikipedia.org/wiki/Red_Hat,Red Hat Enterprise Linux} (RHEL)
+is released by Red Hat Inc. RHEL requires paid subscriptions for use of its
+binaries and support. But since it is free software, many other teams use
+its code to spin-off their own distributions based on RHEL. Red Hat-based
+GNU/Linux distributions initially used the ``Yellowdog Updated, Modifier''
+(YUM) package manager, which has been replaced by ``Dandified yum''
+(DNF). If the latter isn't available on your system, you can use
+@command{yum} instead of @command{dnf} in the command below.
+@example
+$ sudo dnf install ghostscript libtool libjpeg-devel        \
+                   libtiff-devel libgit2-devel lzip         \
+                                                            \
+                   gsl-devel cfitsio-devel wcslib-devel
+@end example
+
 @item @command{brew} (macOS)
 @cindex macOS
 @cindex Homebrew
 @cindex MacPorts
 @cindex @command{brew}
-macOS is the operating system used on Apple devices
-(@url{https://en.wikipedia.org/wiki/MacOS}). macOS does not come with a
-package manager pre-installed, but several widely used, third-party package
-managers exist, such as Homebrew or MacPorts. Both are free
-software. Currently we have only tested Gnuastro's installation with
-Homebrew as described below.
+@url{https://en.wikipedia.org/wiki/MacOS,macOS} is the operating system
+used on Apple devices. macOS does not come with a package manager
+pre-installed, but several widely used, third-party package managers exist,
+such as Homebrew or MacPorts. Both are free software. Currently we have
+only tested Gnuastro's installation with Homebrew as described below.
 
 If not already installed, first obtain Homebrew by following the
 instructions at @url{https://brew.sh}. Homebrew manages packages in
@@ -5042,8 +5210,9 @@ dependencies}) via Homebrew you will need to 
@command{tap} into
 @command{brewsci/science} first (the tap may change in the future, but can
 be found by calling @command{brew search wcslib}).
 @example
-$ brew install ghostscript libtool libjpeg libtiff libgit2 \
-                                                           \
+$ brew install ghostscript libtool libjpeg libtiff          \
+               libgit2 lzip                                 \
+                                                            \
                gsl cfitsio
 $ brew tap brewsci/science
 $ brew install wcslib
@@ -5052,20 +5221,62 @@ $ brew install wcslib
 @item @command{pacman} (Arch Linux)
 @cindex Arch Linux
 @cindex @command{pacman}
-Arch Linux is a smaller GNU/Linux distribution. As discussed in
-@url{https://en.wikipedia.org/wiki/Arch_Linux, Wikipedia}, it follows ``the
-KISS principle ("keep it simple, stupid") as the general guideline, and
-focuses on elegance, code correctness, minimalism and simplicity, and
-expects the user to be willing to make some effort to understand the
-system's operation''. Arch Linux uses ``Package manager'' (Pacman) to
-manage its packages/components.
-@example
-$ sudo pacman -S ghostscript libtool libjpeg libtiff libgit2 \
-                                                             \
+@url{https://en.wikipedia.org/wiki/Arch_Linux,Arch Linux} is a smaller
+GNU/Linux distribution, which follows the KISS principle (``keep it simple,
+stupid'') as a general guideline. It ``focuses on elegance, code
+correctness, minimalism and simplicity, and expects the user to be willing
+to make some effort to understand the system's operation''. Arch Linux uses
+``Package manager'' (Pacman) to manage its packages/components.
+@example
+$ sudo pacman -S ghostscript libtool libjpeg libtiff        \
+                 libgit2 lzip                               \
+                                                            \
                  gsl cfitsio wcslib
 @end example
 @end table
 
+Usually, when libraries are installed by operating system package managers,
+there should be no problems when configuring and building other programs
+from source (that depend on the libraries: Gnuastro in this case). However,
+in some special conditions, problems may pop-up during the configuration,
+building, or checking/running any of Gnuastro's programs. The most common
+of such problems and their solution are discussed below.
+
+@cartouche
+@noindent
+@strong{Not finding library during configuration:} If a library is
+installed, but during Gnuastro's @command{configre} step the library isn't
+found, then configure Gnuastro like the command below (correcting
+@file{/path/to/lib}). For more, see @ref{Known issues} and
+@ref{Installation directory}.
+@example
+$ ./configure LDFLAGS="-I/path/to/lib"
+@end example
+@end cartouche
+
+@cartouche
+@noindent
+@strong{Not finding header (.h) files while building:} If a library is
+installed, but during Gnuastro's @command{make} step, the library's header
+(file with a @file{.h} suffix) isn't found, then configure Gnuastro like
+the command below (correcting @file{/path/to/include}). For more, see
+@ref{Known issues} and @ref{Installation directory}.
+@example
+$ ./configure CPPFLAGS="-I/path/to/include"
+@end example
+@end cartouche
+
+@cartouche
+@noindent
+@strong{Gnuastro's programs don't run during check or after install:} If a
+library is installed, but the programs don't run due to linking problems,
+set the @code{LD_LIBRARY_PATH} variable like below (assuming Gnuastro is
+installed in @file{/path/to/installed}). For more, see @ref{Known issues}
+and @ref{Installation directory}.
+@example
+$ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/path/to/installed/lib"
+@end example
+@end cartouche
 
 
 
@@ -5543,6 +5754,35 @@ program using the GNU build system (through the 
configure script).
 
 @vtable @option
 
+@item --enable-debug
+@cindex Valgrind
+@cindex Debugging
+@cindex GNU Debugger
+Compile/build Gnuastro with debugging information and no optimization. In
+order to allow more efficient programs when using Gnuastro (after the
+installation), by default Gnuastro is built with a 3rd level (a very high
+level) optimization and no debugging information. But when there are
+crashes or un-expected behavior, debugging flags and disabling optimization
+can greatly help in localizing the problem. This configuration option is
+identical to manually calling the configuration script with
+@code{CFLAGS="-g -O0"}.
+
+@item --enable-check-with-valgrind
+@cindex Valgrind
+Do the @command{make check} tests through Valgrind. Therefore, if any
+crashes or memory-related issues (segmentation faults in particular) occur
+in the tests, the output of Valgrind will also be put in the
+@file{tests/test-suite.log} file without having to manually modify the
+check scripts. This option will also build Gnuastro with no optimization
+and enable debugging flags (as if you also call @option{--enable-debug}).
+
+Valgrind is free software. It is a program for easy checking of
+memory-related issues in programs. It runs a program within its own
+controlled environment and can thus identify the exact line-number in the
+program's source where a memory-related issue occurs. However, it can
+significantly slow-down the tests. So this option is only useful when a
+segmentation fault is found during @command{make check}.
+
 @item --enable-progname
 Only build and install @file{progname} along with any other program that is
 enabled in this fashion. @file{progname} is the name of the executable
@@ -6220,7 +6460,7 @@ as GDB, or Valgrind), disable optimization and also the 
building of shared
 libraries. Similar to running the configure script of below
 
 @example
-$ ./configure CFLAGS="-g -O0" --disable-shared
+$ ./configure --enable-debug --disable-shared
 @end example
 
 Besides all the debugging advantages of building with this option, it will
@@ -6228,6 +6468,13 @@ also be significantly speed up the build (at the cost of 
slower built
 programs). So when you are testing something small or working on the build
 system itself, it will be much faster to test your work with this option.
 
+@item -v
+@itemx --valgrind
+@cindex Valgrind
+Build all @command{make check} tests within Valgrind. For more, see the
+description of @option{--enable-check-with-valgrind} in @ref{Gnuastro
+configure options}.
+
 @item -j INT
 @itemx --jobs INT
 The maximum number of threads/jobs for Make to build at any moment. As the
@@ -6420,13 +6667,18 @@ from source, the headers are also installed.
 @cindex @command{LDFLAGS}
 From source. Then your linker is not looking where you installed the
 library. If you followed the instructions in this chapter, all the
-libraries will be installed in @file{/usr/local/lib}. So you have to
-tell your linker to look in this directory. To do so, add
-@command{LDFLAGS=-L/usr/local/lib} to the Gnuastro configure
-script. If you want to use the libraries for your other programming
-projects, then export this environment variable similar to the case
-for @file{LD_LIBRARY_PATH} explained below.
+libraries will be installed in @file{/usr/local/lib}. So you have to tell
+your linker to look in this directory. To do so, configure Gnuastro like
+this:
 
+@example
+$ ./configure LDFLAGS="-I/usr/local/lib"
+@end example
+
+If you want to use the libraries for your other programming projects, then
+export this environment variable in a start-up script similar to the case
+for @file{LD_LIBRARY_PATH} explained below, also see @ref{Installation
+directory}.
 @end itemize
 
 @item
@@ -6449,11 +6701,19 @@ available in Gnulib, it can be fixed immediately.
 
 @item
 @cindex @command{CPPFLAGS}
-@command{$ make}: @emph{Can't find the headers (.h files) of libraries
-installed from source.} Similar to the case for @file{LDFLAGS}
-(above), your compiler is not looking in the right place, add
-@command{CPPFLAGS=-I/usr/local/include} to @command{./configure} to
-re-configure Gnuastro, then re-run make.
+@command{$ make}: @emph{Can't find the headers (.h files) of installed
+libraries.} Your C pre-processor (CPP) isn't looking in the right place. To
+fix this, configure Gnuastro with an additional @code{CPPFLAGS} like below
+(assuming the library is installed in @file{/usr/local/include}:
+
+@example
+$ ./configure CPPFLAGS="-I/usr/local/include"
+@end example
+
+If you want to use the libraries for your other programming projects, then
+export this environment variable in a start-up script similar to the case
+for @file{LD_LIBRARY_PATH} explained below, also see @ref{Installation
+directory}.
 
 @cindex Tests, only one passes
 @cindex @file{LD_LIBRARY_PATH}
@@ -10761,15 +11021,22 @@ instead.
 Output options:
 @table @option
 
-@item -c INT
-@itemx --checkcenter=INT
+@item -c FLT/INT
+@itemx --checkcenter=FLT/INT
 @cindex Check center of crop
-Box width (odd number of pixels) of region in the center of the image to
-check for blank values. If the value to this option is zero, no checking is
-done. This option is only relevant when the cropped region(s) are defined
-by their center (not by the vertices, see @ref{Crop modes}). If any of the
-pixels in this central region of a crop (defined by its center) are blank,
-then it will not be created.
+Square box width of region in the center of the image to check for blank
+values. If any of the pixels in this central region of a crop (defined by
+its center) are blank, then it will not be stored in an output file. If the
+value to this option is zero, no checking is done. This check is only
+applied when the cropped region(s) are defined by their center (not by the
+vertices, see @ref{Crop modes}).
+
+The units of the value are interpretted based on the @option{--mode} value
+(in WCS or pixel units). The ultimate checked region size (in pixels) will
+be an odd integer around the center (converted from WCS, or when an even
+number of pixels are given to this option). In WCS mode, the value can be
+given as fractions, for example if the WCS untis are in degrees,
+@code{0.1/3600} will correspond to a check size of 0.1 arcseconds.
 
 Because survey regions don't often have a clean square or rectangle shape,
 some of the pixels on the sides of the survey FITS image don't commonly
@@ -10777,13 +11044,13 @@ have any data and are blank (see @ref{Blank pixels}). 
So when the catalog
 was not generated from the input image, it often happens that the image
 does not have data over some of the points.
 
-When the given center of a crop falls in such regions and this option has a
-non-zero, odd value, no crop will be created. Therefore with this option,
-you can specify a width of a small box (3 pixels is often good enough)
-around the central pixel of the cropped image. You can check which crops
-were created and which weren't from the command-line (if @option{--quiet}
-was not called, see @ref{Operating mode options}), or in Crop's log file
-(see @ref{Crop output}).
+When the given center of a crop falls in such regions or outside the
+dataset, and this option has a non-zero value, no crop will be
+created. Therefore with this option, you can specify a width of a small box
+(3 pixels is often good enough) around the central pixel of the cropped
+image. You can check which crops were created and which weren't from the
+command-line (if @option{--quiet} was not called, see @ref{Operating mode
+options}), or in Crop's log file (see @ref{Crop output}).
 
 @item -p STR
 @itemx --suffix=STR
@@ -11228,6 +11495,55 @@ non-blank neighbors used to calculate the median is 
given by the first
 popped operand. Note that the distance of the nearest non-blank neighbors
 is irrelevant in this interpolation.
 
+@item collapse-sum
+Collapse the given dataset (second popped operand), by summing all elements
+along the first popped operand (a dimension in FITS standard: counting from
+one, from fastest dimension). The returned dataset has one dimension less
+compared to the input.
+
+The output will have a double-precision floating point type irrespective of
+the input dataset's type. Doing the operation in double-precision (64-bit)
+floating point will help the collapse (summation) be affected less by
+floating point errors. But afterwards, single-precision floating points are
+usually enough in real (noisy) datasets. So depending on the type of the
+input and its nature, it is recommended to use one of the type conversion
+operators on the returned dataset.
+
+@cindex World Coordinate System (WCS)
+If any WCS is present, the returned dataset will also lack the respective
+dimension in its WCS matrix. Therefore, when the WCS is important for later
+processing, be sure that the input is aligned with the respective axises: all 
non-diagonal elements in the WCS matrix are zero.
+
+@cindex IFU
+@cindex Data cubes
+@cindex 3D data-cubes
+@cindex Cubes (3D data)
+@cindex Narrow-band image
+@cindex Integral field unit (IFU)
+One common application of this operator is the creation of pseudo
+broad-band or narrow-band 2D images from 3D data cubes. For example
+integral field unit (IFU) data products that have two spatial dimensions
+(first two FITS dimensions) and one spectral dimension (third FITS
+dimension). The command below will collapse the whole third dimension into
+a 2D array the size of the first two dimensions, and then convert the
+output to single-precision floating point (as discussed above).
+
+@example
+$ astarithmetic cube.fits 3 collapse-sum float32
+@end example
+
+@item collapse-mean
+Similar to @option{collapse-sum}, but the returned dataset will be the mean
+value along the collapsed dimension, not the sum.
+
+@item collapse-number
+Similar to @option{collapse-sum}, but the returned dataset will be the
+number of non-blank values along the collapsed dimension. The output will
+have a 32-bit signed integer type. If the input dataset doesn't have blank
+values, all the elements in the returned dataset will have a single value
+(the length of the collapsed dimension). Therefore this is mostly relevant
+when there are blank values in the dataset.
+
 @item erode
 @cindex Erosion
 Erode the foreground pixels (with value @code{1}) of the input dataset
@@ -15294,28 +15610,37 @@ un-predictable. So please use this option with care 
and in a highly
 controlled environment, for example in the scenario discussed below.
 
 In almost all situations, as the input gets larger, the single most CPU
-(and time) consuming step in NoiseChisel is convolution (the first step in
-its processing). To test NoiseChisel for the best parameters in a given
-analysis, you have to run NoiseChisel multiple times and see the effect of
-each change. Therefore, once the kernel is finalized, re-convolving the
-input on every change will greatly hinder, or discourage, testing of
-higher-level parameters. With this option, you can convolve the input image
-with your chosen kernel once before running NoiseChisel, then feed it to
-NoiseChisel on each test run and thus save valuable time for better/more
-tests.
+(and time) consuming step in NoiseChisel (and other programs that need a
+convolved image) is convolution. Therefore minimizing the number of
+convolutions can save a significant amount of time in some scenarios. One
+such scenario is when you want to segment NoiseChisel's detections using
+the same kernel (with @ref{Segment}, which also supports this
+@option{--convolved} option). This scenario would require two convolutions
+of the same dataset: once by NoiseChisel and once by Segment. Using this
+option in both programs, only one convolution (prior to running
+NoiseChisel) is enough.
+
+Another common scenario where this option can be convenient is when you are
+testing NoiseChisel (or Segment) for the best parameters. You have to run
+NoiseChisel multiple times and see the effect of each change. However, once
+you are happy with the kernel, re-convolving the input on every change of
+higher-level parameters will greatly hinder, or discourage, further
+testing. With this option, you can convolve the input image with your
+chosen kernel once before running NoiseChisel, then feed it to NoiseChisel
+on each test run and thus save valuable time for better/more tests.
 
 To build your desired convolution kernel, you can use
 @ref{MakeProfiles}. To convolve the image with a given kernel you can use
-@ref{Convolve}. Spatial domain convolution is mandatory. In the frequency
+@ref{Convolve}. Spatial domain convolution is mandatory: in the frequency
 domain, blank pixels (if present) will cover the whole image and gradients
 will appear on the edges, see @ref{Spatial vs. Frequency domain}.
 
-Below you can see an example of such a scenario: you want to see how
+Below you can see an example of the second scenario: you want to see how
 variation of the growth level (through the @option{--detgrowquant} option)
 will affect the final result. Recall that you can ignore all the extra
 spaces, new lines, and backslash's (`@code{\}') if you are typing in the
-terminal (in a shell script, remove the @code{$} signs at the start of the
-lines).
+terminal. In a shell script, remove the @code{$} signs at the start of the
+lines.
 
 @example
 ## Make the kernel to convolve with.
@@ -16327,20 +16652,22 @@ acceptable values, please see the description of 
@option{--hdu} in
 
 @item --convolved
 The convolved image to avoid internal convolution by Segment. The usage of
-this option is identical to NoiseChisel's @option{--convolved} option
-(@ref{NoiseChisel input}). Please see the descriptions there for more.
+this option is identical to NoiseChisel's @option{--convolved}
+option. Please see @ref{NoiseChisel input} for a thorough discussion of the
+usefulness and best practices of using this option.
 
 If you want to use the same convolution kernel for detection (with
-@ref{NoiseChisel}) and segmentation, you can use the same convolved image
-with this option (that is also available in NoiseChisel). However, just be
-careful to use the input to NoiseChisel as the input to Segment also, then
-use the @option{--sky} and @option{--std} to specify the Sky and its
-standard deviation (from NoiseChisel's output). Recall that when
-NoiseChisel is not called with @option{--rawoutput}, the first extension of
-NoiseChisel's output is the @emph{Sky-subtracted} input (see
-@ref{NoiseChisel output}. So if you use the same convolved image that you
-fed to NoiseChisel, but use NoiseChisel's output with Segment's
-@option{--convolved}, then the convolved image won't be Sky subtracted.
+@ref{NoiseChisel}) and segmentation, with this option, you can use the same
+convolved image (that is also available in NoiseChisel) and avoid two
+convolutions. However, just be careful to use the input to NoiseChisel as
+the input to Segment also, then use the @option{--sky} and @option{--std}
+to specify the Sky and its standard deviation (from NoiseChisel's
+output). Recall that when NoiseChisel is not called with
+@option{--rawoutput}, the first extension of NoiseChisel's output is the
+@emph{Sky-subtracted} input (see @ref{NoiseChisel output}). So if you use
+the same convolved image that you fed to NoiseChisel, but use NoiseChisel's
+output with Segment's @option{--convolved}, then the convolved image won't
+be Sky subtracted.
 
 @item --chdu
 The HDU/extension containing the convolved image (given to
@@ -17003,9 +17330,7 @@ Due to the noisy nature of data, it is possible to get 
arbitrarily low
 values for a faint object's brightness (or arbitrarily high
 @emph{magnitudes}). Given the scatter caused by the dataset's noise, values
 fainter than a certain level are meaningless: another similar depth
-observation will give a radically different value. This problem is usually
-becomes relevant when the detection and measurement images are not the same
-(for example when you are estimating colors, see @ref{NoiseChisel output}).
+observation will give a radically different value.
 
 For example, while the depth of the image is 32 magnitudes/pixel, a
 measurement that gives a magnitude of 36 for a @mymath{\sim100} pixel
@@ -17014,22 +17339,25 @@ measure a magnitude of 30 for it, and yet another 
might give
 33. Furthermore, due to the noise scatter so close to the depth of the
 data-set, the total brightness might actually get measured as a negative
 value, so no magnitude can be defined (recall that a magnitude is a base-10
-logarithm).
+logarithm). This problem usually becomes relevant when the detection labels
+were not derived from the values being measured (for example when you are
+estimating colors, see @ref{MakeCatalog}).
 
 @cindex Upper limit magnitude
 @cindex Magnitude, upper limit
 Using such unreliable measurements will directly affect our analysis, so we
-must not use the raw measurements. However, all is not lost! Given our
-limited depth, there is one thing we can deduce about the object's
-magnitude: we can say that if something actually exists here (possibly
-buried deep under the noise), it must have a magnitude that is fainter than
-an @emph{upper limit magnitude}. To find this upper limit magnitude, we
-place the object's footprint (segmentation map) over random parts of the
-image where there are no detections, so we only have pure (possibly
-correlated) noise and undetected objects. Doing this a large number of
-times will give us a distribution of brightness values. The standard
-deviation (@mymath{\sigma}) of that distribution can be used to quantify
-the upper limit magnitude.
+must not use the raw measurements. But how can we know how reliable a
+measurement on a given dataset is?
+
+When we confront such unreasonably faint magnitudes, there is one thing we
+can deduce: that if something actually exists here (possibly buried deep
+under the noise), it's inherent magnitude is fainter than an @emph{upper
+limit magnitude}. To find this upper limit magnitude, we place the object's
+footprint (segmentation map) over random parts of the image where there are
+no detections, so we only have pure (possibly correlated) noise, along with
+undetected objects. Doing this a large number of times will give us a
+distribution of brightness values. The standard deviation (@mymath{\sigma})
+of that distribution can be used to quantify the upper limit magnitude.
 
 @cindex Correlated noise
 Traditionally, faint/small object photometry was done using fixed circular
@@ -17042,13 +17370,20 @@ patters, so the shape of the object can also affect 
the final result
 result. Fortunately, with the much more advanced hardware and software of
 today, we can make customized segmentation maps for each object.
 
-
-If requested, MakeCatalog will estimate the the upper limit magnitude is
-found for each object in the image separately, the procedure is fully
-configurable with the options in @ref{Upper-limit settings}. If one value
-for the whole image is required, you can either use the surface brightness
-limit above or make a circular aperture and feed it into MakeCatalog to
-request an upper-limit magnitude for it.
+When requested, MakeCatalog will randomly place each target's footprint
+over the dataset as described above and estimate the resulting
+distribution's properties (like the upper limit magnitude). The procedure
+is fully configurable with the options in @ref{Upper-limit settings}. If
+one value for the whole image is required, you can either use the surface
+brightness limit above or make a circular aperture and feed it into
+MakeCatalog to request an upper-limit magnitude for it@footnote{If you
+intend to make apertures manually and not use a detection map (for example
+from @ref{Segment}), don't forget to use the @option{--upmaskfile} to give
+NoiseChisel's output (or any a binary map, marking detected pixels, see
+@ref{NoiseChisel output}) as a mask. Otherwise, the footprints may randomly
+fall over detections, giving higly skewed distributions, with wrong
+upper-limit distributions. See The description of @option{--upmaskfile} in
+@ref{Upper-limit settings} for more.}.
 
 @end table
 
@@ -17559,14 +17894,13 @@ magnitude}.
 @node Upper-limit settings, MakeCatalog output columns, MakeCatalog inputs and 
basic settings, Invoking astmkcatalog
 @subsubsection Upper-limit settings
 
-
-The upper limit magnitude was discussed in @ref{Quantifying measurement
+The upper-limit magnitude was discussed in @ref{Quantifying measurement
 limits}. Unlike other measured values/columns in MakeCatalog, the upper
-limit magnitude needs several defined parameters which are discussed
-here. All the upper limit magnitude specific options start with @option{up}
-for upper-limit, except for @option{--envseed} that is also present in
-other programs and is general for any job requiring random number
-generation (see @ref{Generating random numbers}).
+limit magnitude needs several extra parameters which are discussed
+here. All the options specific to the upper-limit measurements start with
+@option{up} for ``upper-limit''. The only exception is @option{--envseed}
+that is also present in other programs and is general for any job requiring
+random number generation in Gnuastro (see @ref{Generating random numbers}).
 
 @cindex Reproducibility
 One very important consideration in Gnuastro is reproducibility. Therefore,
@@ -17574,29 +17908,41 @@ the values to all of these parameters along with 
others (like the random
 number generator type and seed) are also reported in the comments of the
 final catalog when the upper limit magnitude column is desired. The random
 seed that is used to define the random positions for each object or clump
-is unique and set based on the given seed, the total number of objects and
-clumps and also the labels of the clumps and objects. So with identical
-inputs, an identical upper-limit magnitude will be found. But even if the
-ordering of the object/clump labels differs (and the seed is the same) the
-result will not be the same.
-
-MakeCatalog will randomly place the object/clump footprint over the image
-and when the footprint doesn't fall on any object or masked region (see
-@option{--upmaskfile}) it will be used until the desired number
-(@option{--upnum}) of samples are found to estimate the distribution's
-standard deviation (see @ref{Quantifying measurement limits}). Otherwise it
-will be ignored and another random position will be generated. But when the
-profile is very large or the image is significantly covered by detections,
-it might not be possible to find the desired number of
-samplings. MakeProfiles will continue searching until 50 times the value
-given to @option{--upnum}. If @option{--upnum} good samples cannot be found
-until this limit, it will set the upper-limit magnitude for that object to
-NaN (blank).
+is unique and set based on the (optionally) given seed, the total number of
+objects and clumps and also the labels of the clumps and objects. So with
+identical inputs, an identical upper-limit magnitude will be
+found. However, even if the seed is identical, when the ordering of the
+object/clump labels differs between different runs, the result of
+upper-limit measurements will not be identical.
+
+MakeCatalog will randomly place the object/clump footprint over the
+dataset. When the randomly placed footprint doesn't fall on any object or
+masked region (see @option{--upmaskfile}) it will be used in the final
+distribution. Otherwise that particular random position will be ignored and
+another random position will be generated. Finally, when the distribution
+has the desired number of successfully measured random samples
+(@option{--upnum}) the distribution's properties will be measured and
+placed in the catalog.
+
+When the profile is very large or the image is significantly covered by
+detections, it might not be possible to find the desired number of
+samplings in a reasonable time. MakeProfiles will continue searching until
+it is unable to find a successful position (since the last successful
+measurement@footnote{The counting of failed positions restarts on every
+successful measurement.}), for a large multiple of @option{--upnum}
+(currently@footnote{In Gnuastro's source, this constant number is defined
+as the @code{MKCATALOG_UPPERLIMIT_MAXFAILS_MULTIP} macro in
+@file{bin/mkcatalog/main.h}, see @ref{Downloading the source}.} this is
+10). If @option{--upnum} successful samples cannot be found until this
+limit is reached, MakeCatalog will set the upper-limit magnitude for that
+object to NaN (blank).
 
 MakeCatalog will also print a warning if the range of positions available
 for the labeled region is smaller than double the size of the region. In
 such cases, the limited range of random positions can artificially decrease
-the standard deviation of the final distribution.
+the standard deviation of the final distribution. If your dataset can allow
+it (it is large enough), it is recommended to use a larger range if you see
+such warnings.
 
 @table @option
 
@@ -18241,15 +18587,15 @@ identify the extension. When the second input is 
FITS, the extension must
 be specified with @option{--hdu2}.
 
 When @option{--quiet} is not called, Match will print the number of matches
-found in standard output (on the command-line). If no match was found, no
-output file will be created (table or log file). When matches are found, by
+found in standard output (on the command-line). When matches are found, by
 default, the output file(s) will be the re-arranged input tables such that
 the rows match each other: both output tables will have the same number of
 rows which are matched with each other. If @option{--outcols} is called,
 the output is a single table with rows chosen from either of the two inputs
 in any order, see the description of @option{--outcols}. If the
 @option{--logasoutput} option is called, the output will be a single table
-with the contents of the log file, see below.
+with the contents of the log file, see below. If no matches are found, the
+columns of the output table(s) will have zero rows (with proper meta-data).
 
 If no output file name is given with the @option{--output} option, then
 automatic output @ref{Automatic output} will be used to determine the
@@ -18261,17 +18607,18 @@ file. With @option{--outcols} and 
@option{--logasoutput}, the FITS output
 will be a single table (in one extension).
 
 When the @option{--log} option is called (see @ref{Operating mode
-options}), Match will also create a file named @file{astmatch.fits} (or
-@file{astmatch.txt}, depending on @option{--tableformat}, see @ref{Input
-output options}) in the directory it is run in. This log table will have
-three columns. The first and second columns show the matching row/record
-number (counting from 1) of the first and second input catalogs
-respectively. The third column is the distance between the two matched
-positions. The units of the distance are the same as the given coordinates
-(given the possible ellipticity, see description of @option{--aperture}
-below). When @option{--logasoutput} is called, no log file (with a fixed
-name) will be created. In this case, the output file (possibly given by the
-@option{--output} option) will have the contents of this log file.
+options}), and there was a match, Match will also create a file named
+@file{astmatch.fits} (or @file{astmatch.txt}, depending on
+@option{--tableformat}, see @ref{Input output options}) in the directory it
+is run in. This log table will have three columns. The first and second
+columns show the matching row/record number (counting from 1) of the first
+and second input catalogs respectively. The third column is the distance
+between the two matched positions. The units of the distance are the same
+as the given coordinates (given the possible ellipticity, see description
+of @option{--aperture} below). When @option{--logasoutput} is called, no
+log file (with a fixed name) will be created. In this case, the output file
+(possibly given by the @option{--output} option) will have the contents of
+this log file.
 
 @cartouche
 @noindent
@@ -21769,6 +22116,14 @@ of @code{0} (zero). Gnuastro also comes with some 
wrappers to make it
 easier to use libgit2 (see @ref{Git wrappers}).
 @end deffn
 
+@deffn Macro GAL_CONFIG_HAVE_FITS_IS_REENTRANT
+@cindex CFITSIO
+This macro will have a value of 1 when the CFITSIO of the host system has
+the @code{fits_is_reentrant} function (available from CFITSIO version
+3.30). This function is used to see if CFITSIO was configured to read a
+FITS file simultaneously on different threads.
+@end deffn
+
 
 @deffn Macro GAL_CONFIG_HAVE_WCSLIB_VERSION
 WCSLIB is the reference library for world coordinate system transformation
@@ -23087,6 +23442,40 @@ the two coordinates @code{a} and @code{b} (each an 
array of @code{ndim}
 elements).
 @end deftypefun
 
+@deftypefun {gal_data_t *} gal_dimension_collapse_sum (gal_data_t @code{*in}, 
size_t @code{c_dim}, gal_data_t @code{*weight})
+Collapse the input dataset (@code{in}) along the given dimension
+(@code{c_dim}, in C definition: starting from zero, from the slowest
+dimension), by summing all elements in that direction. If
+@code{weight!=NULL}, it must be a single-dimensional array, with the same
+size as the dimension to be collapsed. The respective weight will be
+multiplied to each element during the collapse.
+
+For generality, the returned dataset will have a @code{GAL_TYPE_FLOAT64}
+type. See @ref{Copying datasets} for converting the returned dataset to a
+desired type. Also, for more on the application of this function, see the
+Arithmetic program's @option{collapse-sum} operator (which uses this
+function) in @ref{Arithmetic operators}.
+@end deftypefun
+
+@deftypefun {gal_data_t *} gal_dimension_collapse_mean (gal_data_t @code{*in}, 
size_t @code{c_dim}, gal_data_t @code{*weight})
+Similar to @code{gal_dimension_collapse_sum} (above), but the collapse will
+be done by calculating the mean along the requested dimension, not summing
+over it.
+@end deftypefun
+
+@deftypefun {gal_data_t *} gal_dimension_collapse_number (gal_data_t 
@code{*in}, size_t @code{c_dim})
+Collapse the input dataset (@code{in}) along the given dimension
+(@code{c_dim}, in C definition: starting from zero, from the slowest
+dimension), by counting how many non-blank elements there are along that
+dimension.
+
+For generality, the returned dataset will have a @code{GAL_TYPE_INT32}
+type. See @ref{Copying datasets} for converting the returned dataset to a
+desired type. Also, for more on the application of this function, see the
+Arithmetic program's @option{collapse-number} operator (which uses this
+function) in @ref{Arithmetic operators}.
+@end deftypefun
+
 @deffn {Function-like macro} GAL_DIMENSION_NEIGHBOR_OP (@code{index}, 
@code{ndim}, @code{dsize}, @code{connectivity}, @code{dinc}, @code{operation})
 Parse the neighbors of the element located at @code{index} and do the
 requested operation on them. This is defined as a macro to allow easy
@@ -24738,16 +25127,19 @@ of table formats based on the filename (see 
@ref{Table input output}).
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_fits_tab_read (char @code{*filename}, char 
@code{*hdu}, size_t @code{numrows}, gal_data_t @code{*colinfo}, 
gal_list_sizet_t @code{*indexll}, size_t @code{minmapsize})
-Read the columns given in the list @code{indexll} from a FITS table into a
-linked list of data structures, see @ref{List of size_t} and @ref{List of
+Read the columns given in the list @code{indexll} from a FITS table (in
+@file{filename} and HDU/extension @code{hdu}) into the returned linked list
+of data structures, see @ref{List of size_t} and @ref{List of
 gal_data_t}. If the necessary space for each column is larger than
 @code{minmapsize}, don't keep it in the RAM, but in a file in the HDD/SSD,
 see the description under the same name in @ref{Generic data container}.
 
-Note that this is a low-level function, so the output data linked list is
-the inverse of the input indexs linked list. It is recommended to use
-@code{gal_table_read} for generic reading of tables, see @ref{Table input
-output}.
+Each column will have @code{numrows} rows and @code{colinfo} contains any
+further information about the columns (returned by
+@code{gal_fits_tab_info}, described above). Note that this is a low-level
+function, so the output data linked list is the inverse of the input indexs
+linked list. It is recommended to use @code{gal_table_read} for generic
+reading of tables, see @ref{Table input output}.
 @end deftypefun
 
 @deftypefun void gal_fits_tab_write (gal_data_t @code{*cols}, gal_list_str_t 
@code{*comments}, int @code{tableformat}, char @code{*filename})
@@ -29115,7 +29507,7 @@ significantly slow down the compilation (the 
@command{make} command). So
 during development it is recommended to configure Gnuastro as follows:
 
 @example
-$ ./configure --disable-shared CFLAGS="-g -O0"
+$ ./configure --enable-debug --disable-shared
 @end example
 
 @noindent
diff --git a/lib/arithmetic.c b/lib/arithmetic.c
index 6f31f51..3e84f95 100644
--- a/lib/arithmetic.c
+++ b/lib/arithmetic.c
@@ -183,16 +183,16 @@ arithmetic_not(gal_data_t *data, int flags)
   /* Go over the pixels and set the output values. */
   switch(data->type)
     {
-    case GAL_TYPE_UINT8:   TYPE_CASE_FOR_NOT(uint8_t);   break;
-    case GAL_TYPE_INT8:    TYPE_CASE_FOR_NOT(int8_t);    break;
-    case GAL_TYPE_UINT16:  TYPE_CASE_FOR_NOT(uint16_t);  break;
-    case GAL_TYPE_INT16:   TYPE_CASE_FOR_NOT(int16_t);   break;
-    case GAL_TYPE_UINT32:  TYPE_CASE_FOR_NOT(uint32_t);  break;
-    case GAL_TYPE_INT32:   TYPE_CASE_FOR_NOT(int32_t);   break;
-    case GAL_TYPE_UINT64:  TYPE_CASE_FOR_NOT(uint64_t);  break;
-    case GAL_TYPE_INT64:   TYPE_CASE_FOR_NOT(int64_t);   break;
-    case GAL_TYPE_FLOAT32: TYPE_CASE_FOR_NOT(float);     break;
-    case GAL_TYPE_FLOAT64: TYPE_CASE_FOR_NOT(double);    break;
+    case GAL_TYPE_UINT8:   TYPE_CASE_FOR_NOT( uint8_t  );   break;
+    case GAL_TYPE_INT8:    TYPE_CASE_FOR_NOT( int8_t   );   break;
+    case GAL_TYPE_UINT16:  TYPE_CASE_FOR_NOT( uint16_t );   break;
+    case GAL_TYPE_INT16:   TYPE_CASE_FOR_NOT( int16_t  );   break;
+    case GAL_TYPE_UINT32:  TYPE_CASE_FOR_NOT( uint32_t );   break;
+    case GAL_TYPE_INT32:   TYPE_CASE_FOR_NOT( int32_t  );   break;
+    case GAL_TYPE_UINT64:  TYPE_CASE_FOR_NOT( uint64_t );   break;
+    case GAL_TYPE_INT64:   TYPE_CASE_FOR_NOT( int64_t  );   break;
+    case GAL_TYPE_FLOAT32: TYPE_CASE_FOR_NOT( float    );   break;
+    case GAL_TYPE_FLOAT64: TYPE_CASE_FOR_NOT( double   );   break;
 
     case GAL_TYPE_BIT:
       error(EXIT_FAILURE, 0, "%s: bit datatypes are not yet supported, "
@@ -1330,63 +1330,63 @@ gal_arithmetic_operator_string(int operator)
 {
   switch(operator)
     {
-    case GAL_ARITHMETIC_OP_PLUS:         return "+";
-    case GAL_ARITHMETIC_OP_MINUS:        return "-";
-    case GAL_ARITHMETIC_OP_MULTIPLY:     return "*";
-    case GAL_ARITHMETIC_OP_DIVIDE:       return "/";
-    case GAL_ARITHMETIC_OP_MODULO:       return "%";
-
-    case GAL_ARITHMETIC_OP_LT:           return "<";
-    case GAL_ARITHMETIC_OP_LE:           return "<=";
-    case GAL_ARITHMETIC_OP_GT:           return ">";
-    case GAL_ARITHMETIC_OP_GE:           return ">=";
-    case GAL_ARITHMETIC_OP_EQ:           return "==";
-    case GAL_ARITHMETIC_OP_NE:           return "!=";
-    case GAL_ARITHMETIC_OP_AND:          return "and";
-    case GAL_ARITHMETIC_OP_OR:           return "or";
-    case GAL_ARITHMETIC_OP_NOT:          return "not";
-    case GAL_ARITHMETIC_OP_ISBLANK:      return "isblank";
-    case GAL_ARITHMETIC_OP_WHERE:        return "where";
-
-    case GAL_ARITHMETIC_OP_BITAND:       return "bitand";
-    case GAL_ARITHMETIC_OP_BITOR:        return "bitor";
-    case GAL_ARITHMETIC_OP_BITXOR:       return "bitxor";
-    case GAL_ARITHMETIC_OP_BITLSH:       return "lshift";
-    case GAL_ARITHMETIC_OP_BITRSH:       return "rshift";
-    case GAL_ARITHMETIC_OP_BITNOT:       return "bitnot";
-
-    case GAL_ARITHMETIC_OP_ABS:          return "abs";
-    case GAL_ARITHMETIC_OP_POW:          return "pow";
-    case GAL_ARITHMETIC_OP_SQRT:         return "sqrt";
-    case GAL_ARITHMETIC_OP_LOG:          return "log";
-    case GAL_ARITHMETIC_OP_LOG10:        return "log10";
-
-    case GAL_ARITHMETIC_OP_MINVAL:       return "minvalue";
-    case GAL_ARITHMETIC_OP_MAXVAL:       return "maxvalue";
-    case GAL_ARITHMETIC_OP_NUMVAL:       return "numvalue";
-    case GAL_ARITHMETIC_OP_SUMVAL:       return "sumvalue";
-    case GAL_ARITHMETIC_OP_MEANVAL:      return "meanvalue";
-    case GAL_ARITHMETIC_OP_STDVAL:       return "stdvalue";
-    case GAL_ARITHMETIC_OP_MEDIANVAL:    return "medianvalue";
-
-    case GAL_ARITHMETIC_OP_MIN:          return "min";
-    case GAL_ARITHMETIC_OP_MAX:          return "max";
-    case GAL_ARITHMETIC_OP_NUM:          return "num";
-    case GAL_ARITHMETIC_OP_SUM:          return "sum";
-    case GAL_ARITHMETIC_OP_MEAN:         return "mean";
-    case GAL_ARITHMETIC_OP_STD:          return "std";
-    case GAL_ARITHMETIC_OP_MEDIAN:       return "median";
-
-    case GAL_ARITHMETIC_OP_TO_UINT8:     return "uchar";
-    case GAL_ARITHMETIC_OP_TO_INT8:      return "char";
-    case GAL_ARITHMETIC_OP_TO_UINT16:    return "ushort";
-    case GAL_ARITHMETIC_OP_TO_INT16:     return "short";
-    case GAL_ARITHMETIC_OP_TO_UINT32:    return "uint";
-    case GAL_ARITHMETIC_OP_TO_INT32:     return "int";
-    case GAL_ARITHMETIC_OP_TO_UINT64:    return "ulong";
-    case GAL_ARITHMETIC_OP_TO_INT64:     return "long";
-    case GAL_ARITHMETIC_OP_TO_FLOAT32:   return "float32";
-    case GAL_ARITHMETIC_OP_TO_FLOAT64:   return "float64";
+    case GAL_ARITHMETIC_OP_PLUS:            return "+";
+    case GAL_ARITHMETIC_OP_MINUS:           return "-";
+    case GAL_ARITHMETIC_OP_MULTIPLY:        return "*";
+    case GAL_ARITHMETIC_OP_DIVIDE:          return "/";
+    case GAL_ARITHMETIC_OP_MODULO:          return "%";
+
+    case GAL_ARITHMETIC_OP_LT:              return "<";
+    case GAL_ARITHMETIC_OP_LE:              return "<=";
+    case GAL_ARITHMETIC_OP_GT:              return ">";
+    case GAL_ARITHMETIC_OP_GE:              return ">=";
+    case GAL_ARITHMETIC_OP_EQ:              return "==";
+    case GAL_ARITHMETIC_OP_NE:              return "!=";
+    case GAL_ARITHMETIC_OP_AND:             return "and";
+    case GAL_ARITHMETIC_OP_OR:              return "or";
+    case GAL_ARITHMETIC_OP_NOT:             return "not";
+    case GAL_ARITHMETIC_OP_ISBLANK:         return "isblank";
+    case GAL_ARITHMETIC_OP_WHERE:           return "where";
+
+    case GAL_ARITHMETIC_OP_BITAND:          return "bitand";
+    case GAL_ARITHMETIC_OP_BITOR:           return "bitor";
+    case GAL_ARITHMETIC_OP_BITXOR:          return "bitxor";
+    case GAL_ARITHMETIC_OP_BITLSH:          return "lshift";
+    case GAL_ARITHMETIC_OP_BITRSH:          return "rshift";
+    case GAL_ARITHMETIC_OP_BITNOT:          return "bitnot";
+
+    case GAL_ARITHMETIC_OP_ABS:             return "abs";
+    case GAL_ARITHMETIC_OP_POW:             return "pow";
+    case GAL_ARITHMETIC_OP_SQRT:            return "sqrt";
+    case GAL_ARITHMETIC_OP_LOG:             return "log";
+    case GAL_ARITHMETIC_OP_LOG10:           return "log10";
+
+    case GAL_ARITHMETIC_OP_MINVAL:          return "minvalue";
+    case GAL_ARITHMETIC_OP_MAXVAL:          return "maxvalue";
+    case GAL_ARITHMETIC_OP_NUMVAL:          return "numvalue";
+    case GAL_ARITHMETIC_OP_SUMVAL:          return "sumvalue";
+    case GAL_ARITHMETIC_OP_MEANVAL:         return "meanvalue";
+    case GAL_ARITHMETIC_OP_STDVAL:          return "stdvalue";
+    case GAL_ARITHMETIC_OP_MEDIANVAL:       return "medianvalue";
+
+    case GAL_ARITHMETIC_OP_MIN:             return "min";
+    case GAL_ARITHMETIC_OP_MAX:             return "max";
+    case GAL_ARITHMETIC_OP_NUM:             return "num";
+    case GAL_ARITHMETIC_OP_SUM:             return "sum";
+    case GAL_ARITHMETIC_OP_MEAN:            return "mean";
+    case GAL_ARITHMETIC_OP_STD:             return "std";
+    case GAL_ARITHMETIC_OP_MEDIAN:          return "median";
+
+    case GAL_ARITHMETIC_OP_TO_UINT8:        return "uchar";
+    case GAL_ARITHMETIC_OP_TO_INT8:         return "char";
+    case GAL_ARITHMETIC_OP_TO_UINT16:       return "ushort";
+    case GAL_ARITHMETIC_OP_TO_INT16:        return "short";
+    case GAL_ARITHMETIC_OP_TO_UINT32:       return "uint";
+    case GAL_ARITHMETIC_OP_TO_INT32:        return "int";
+    case GAL_ARITHMETIC_OP_TO_UINT64:       return "ulong";
+    case GAL_ARITHMETIC_OP_TO_INT64:        return "long";
+    case GAL_ARITHMETIC_OP_TO_FLOAT32:      return "float32";
+    case GAL_ARITHMETIC_OP_TO_FLOAT64:      return "float64";
 
     default:
       error(EXIT_FAILURE, 0, "%s: operator code %d not recognized",
@@ -1412,7 +1412,7 @@ gal_arithmetic(int operator, int flags, ...)
   /* Prepare the variable arguments (starting after the flags argument). */
   va_start(va, flags);
 
-  /* Depending on the operator do the job: */
+  /* Depending on the operator, do the job: */
   switch(operator)
     {
 
@@ -1474,13 +1474,12 @@ gal_arithmetic(int operator, int flags, ...)
       out=arithmetic_from_statistics(operator, flags, d1);
       break;
 
-    /* Absolute operator */
+    /* Absolute operator. */
     case GAL_ARITHMETIC_OP_ABS:
       d1 = va_arg(va, gal_data_t *);
       out=arithmetic_abs(flags, d1);
       break;
 
-
     /* Multi-operand operators */
     case GAL_ARITHMETIC_OP_MIN:
     case GAL_ARITHMETIC_OP_MAX:
diff --git a/lib/dimension.c b/lib/dimension.c
index 3d8d8e2..9b1aea7 100644
--- a/lib/dimension.c
+++ b/lib/dimension.c
@@ -28,6 +28,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <error.h>
 #include <stdlib.h>
 
+#include <gnuastro/wcs.h>
 #include <gnuastro/pointer.h>
 #include <gnuastro/dimension.h>
 
@@ -59,10 +60,14 @@ gal_dimension_is_different(gal_data_t *first, gal_data_t 
*second)
   if(first->ndim!=second->ndim)
     return 1;
 
-  /* Check if the sizes along all dimensions are the same: */
-  for(i=0;i<first->ndim;++i)
-    if( first->dsize[i] != second->dsize[i] )
-      return 1;
+  /* If the sizes are not zero, check if each dimension also has the same
+     length. */
+  if(first->size==0 && first->size==second->size)
+    return 0;
+  else
+    for(i=0;i<first->ndim;++i)
+      if( first->dsize[i] != second->dsize[i] )
+        return 1;
 
   /* If it got to here, we know the dimensions have the same length. */
   return 0;
@@ -265,3 +270,428 @@ gal_dimension_dist_manhattan(size_t *a, size_t *b, size_t 
ndim)
   for(i=0;i<ndim;++i) out += (a[i] > b[i]) ? (a[i]-b[i]) : (b[i]-a[i]);
   return out;
 }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/************************************************************************/
+/********************    Collapsing a dimension    **********************/
+/************************************************************************/
+enum dimension_collapse_operation
+{
+ DIMENSION_COLLAPSE_INVALID,    /* ==0 by C standard. */
+
+ DIMENSION_COLLAPSE_SUM,
+ DIMENSION_COLLAPSE_MEAN,
+ DIMENSION_COLLAPSE_NUMBER,
+};
+
+
+
+
+
+static gal_data_t *
+dimension_collapse_sanity_check(gal_data_t *in, gal_data_t *weight,
+                                size_t c_dim, int hasblank, size_t *cnum,
+                                double **warr)
+{
+  gal_data_t *wht=NULL;
+
+  /* The requested dimension to collapse cannot be larger than the input's
+     number of dimensions. */
+  if( c_dim > (in->ndim-1) )
+    error(EXIT_FAILURE, 0, "%s: the input has %zu dimensions, but you have "
+          "asked to collapse dimension %zu", __func__, in->ndim, c_dim);
+
+  /* If there is no blank value, there is no point in calculating the
+     number of points in each collapsed dataset (when necessary). In that
+     case, `cnum!=0'. */
+  if(hasblank==0)
+    *cnum=in->dsize[c_dim];
+
+  /* Weight sanity checks */
+  if(weight)
+    {
+      if( weight->ndim!=1 )
+        error(EXIT_FAILURE, 0, "%s: the weight dataset has %zu dimensions, "
+              "it must be one-dimensional", __func__, weight->ndim);
+      if( in->dsize[c_dim]!=weight->size )
+        error(EXIT_FAILURE, 0, "%s: the weight dataset has %zu elements, "
+              "but the input dataset has %zu elements in dimension %zu",
+              __func__, weight->size, in->dsize[c_dim], c_dim);
+      wht = ( weight->type == GAL_TYPE_FLOAT64
+              ? weight
+              : gal_data_copy_to_new_type(weight, GAL_TYPE_FLOAT64) );
+      *warr = wht->array;
+    }
+
+  /* Return the weight data structure. */
+  return wht;
+}
+
+
+
+
+/* Set the collapsed output sizes. */
+static void
+dimension_collapse_sizes(gal_data_t *in, size_t c_dim, size_t *outndim,
+                         size_t *outdsize)
+{
+  size_t i, a=0;
+
+  if(in->ndim==1)
+    *outndim=outdsize[0]=1;
+  else
+    {
+      *outndim=in->ndim-1;
+      for(i=0;i<in->ndim;++i)
+        if(i!=c_dim) outdsize[a++]=in->dsize[i];
+    }
+}
+
+
+
+
+
+/* Depending on the operator, write the result into the output. */
+#define COLLAPSE_WRITE(OIND,IIND) {                                     \
+    /* We need the sum when number operator is requested. */            \
+    if(farr) farr[ OIND ] += (warr ? warr[w] : 1) * inarr[ IIND ];      \
+                                                                        \
+    /* We don't need the number in some situations. */                  \
+    if(iarr)                                                            \
+      {                                                                 \
+        if(num->type==GAL_TYPE_UINT8) iarr[ OIND ] = 1;                 \
+        else                        ++iarr[ OIND ];                     \
+      }                                                                 \
+                                                                        \
+    /* If the sum of weights for is needed, add it. */                  \
+    if(wsumarr) wsumarr[ OIND ] += warr[w];                             \
+  }
+
+
+
+/* Deal properly with blanks. */
+#define COLLAPSE_CHECKBLANK(OIND,IIND) {                                \
+    if(hasblank)                                                        \
+      {                                                                 \
+        if(B==B) /* An integer type: blank can be checked with `=='. */ \
+          {                                                             \
+            if( inarr[IIND] != B )           COLLAPSE_WRITE(OIND,IIND); \
+          }                                                             \
+        else     /* A floating point type where NAN != NAN. */          \
+          {                                                             \
+            if( inarr[IIND] == inarr[IIND] ) COLLAPSE_WRITE(OIND,IIND); \
+          }                                                             \
+      }                                                                 \
+    else                                     COLLAPSE_WRITE(OIND,IIND); \
+  }
+
+
+
+#define COLLAPSE_DIM(IT) {                                              \
+    IT B, *inarr=in->array;                                             \
+    if(hasblank) gal_blank_write(&B, in->type);                         \
+    switch(in->ndim)                                                    \
+      {                                                                 \
+      /* 1D input dataset. */                                           \
+      case 1:                                                           \
+        for(i=0;i<in->dsize[0];++i)                                     \
+          {                                                             \
+            if(weight) w=i;                                             \
+            COLLAPSE_CHECKBLANK(0,i);                                   \
+          }                                                             \
+        break;                                                          \
+                                                                        \
+      /* 2D input dataset. */                                           \
+      case 2:                                                           \
+        for(i=0;i<in->dsize[0];++i)                                     \
+          for(j=0;j<in->dsize[1];++j)                                   \
+            {                                                           \
+              /* In a more easy to understand format:                   \
+                 dim==0 --> a=j;                                        \
+                 dim==1 --> a=i; */                                     \
+              a = c_dim==0 ? j : i;                                     \
+              if(weight) w = c_dim == 0 ? i : j;                        \
+              COLLAPSE_CHECKBLANK(a, i*in->dsize[1] + j);               \
+            }                                                           \
+        break;                                                          \
+                                                                        \
+      /* 3D input dataset. */                                           \
+      case 3:                                                           \
+        slice=in->dsize[1]*in->dsize[2];                                \
+        for(i=0;i<in->dsize[0];++i)                                     \
+          for(j=0;j<in->dsize[1];++j)                                   \
+            for(k=0;k<in->dsize[2];++k)                                 \
+              {                                                         \
+                /* In a more easy to understand format:                 \
+                   dim==0 --> a=j; b=k;                                 \
+                   dim==1 --> a=i; b=k;                                 \
+                   dim==2 --> a=i; b=j;   */                            \
+                a = c_dim==0 ? j : i;                                   \
+                b = c_dim==2 ? j : k;                                   \
+                if(weight) w = c_dim==0 ? i : (c_dim==1 ? j : k);       \
+                COLLAPSE_CHECKBLANK(a*outdsize[1]+b,                    \
+                                    i*slice + j*in->dsize[2] + k);      \
+              }                                                         \
+        break;                                                          \
+                                                                        \
+        /* Input dataset's dimensionality not yet supported. */         \
+      default:                                                          \
+        error(EXIT_FAILURE, 0, "%s: %zu-dimensional datasets not yet "  \
+              "supported, please contact us at %s to add this feature", \
+              __func__, in->ndim, PACKAGE_BUGREPORT);                   \
+      }                                                                 \
+  }
+
+
+
+
+
+gal_data_t *
+gal_dimension_collapse_sum(gal_data_t *in, size_t c_dim, gal_data_t *weight)
+{
+  double *wsumarr=NULL;
+  uint8_t *ii, *iarr=NULL;
+  size_t a, b, i, j, k, w=-1, cnum=0;
+  size_t outdsize[10], slice, outndim;
+  int hasblank=gal_blank_present(in, 0);
+  double *dd, *df, *warr=NULL, *farr=NULL;
+  gal_data_t *wht=NULL, *sum=NULL, *num=NULL;
+
+  /* Basic sanity checks. */
+  wht=dimension_collapse_sanity_check(in, weight, c_dim, hasblank,
+                                      &cnum, &warr);
+
+  /* Set the size of the collapsed output. */
+  dimension_collapse_sizes(in, c_dim, &outndim, outdsize);
+
+  /* Allocate the sum (output) dataset. */
+  sum=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, outndim, outdsize, in->wcs,
+                     1, in->minmapsize, NULL, NULL, NULL);
+
+  /* The number dataset (when there are blank values).*/
+  if(hasblank)
+    num=gal_data_alloc(NULL, GAL_TYPE_INT8, outndim, outdsize, NULL,
+                       1, in->minmapsize, NULL, NULL, NULL);
+
+  /* Set the array pointers. */
+  if(sum) farr=sum->array;
+  if(num) iarr=num->array;
+
+  /* Parse the dataset. */
+  switch(in->type)
+    {
+    case GAL_TYPE_UINT8:     COLLAPSE_DIM( uint8_t  );   break;
+    case GAL_TYPE_INT8:      COLLAPSE_DIM( int8_t   );   break;
+    case GAL_TYPE_UINT16:    COLLAPSE_DIM( uint16_t );   break;
+    case GAL_TYPE_INT16:     COLLAPSE_DIM( int16_t  );   break;
+    case GAL_TYPE_UINT32:    COLLAPSE_DIM( uint32_t );   break;
+    case GAL_TYPE_INT32:     COLLAPSE_DIM( int32_t  );   break;
+    case GAL_TYPE_UINT64:    COLLAPSE_DIM( uint64_t );   break;
+    case GAL_TYPE_INT64:     COLLAPSE_DIM( int64_t  );   break;
+    case GAL_TYPE_FLOAT32:   COLLAPSE_DIM( float    );   break;
+    case GAL_TYPE_FLOAT64:   COLLAPSE_DIM( double   );   break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: type value (%d) not recognized",
+            __func__, in->type);
+    }
+
+  /* If `num' is zero on any element, set its sum to NaN. */
+  if(num)
+    {
+      ii = num->array;
+      df = (dd=sum->array) + sum->size;
+      do if(*ii++==0) *dd=NAN; while(++dd<df);
+    }
+
+  /* Remove the respective dimension in the WCS structure also (if any
+     exists). Note that `sum->ndim' has already been changed. So we'll use
+     `in->wcs'. */
+  gal_wcs_remove_dimension(sum->wcs, in->ndim-c_dim);
+
+  /* Clean up and return. */
+  if(wht!=weight) gal_data_free(wht);
+  if(num) gal_data_free(num);
+  return sum;
+}
+
+
+
+
+
+gal_data_t *
+gal_dimension_collapse_mean(gal_data_t *in, size_t c_dim,
+                            gal_data_t *weight)
+{
+  double wsum=NAN;
+  double *wsumarr=NULL;
+  int32_t *ii, *iarr=NULL;
+  size_t a, b, i, j, k, w=-1, cnum=0;
+  size_t outdsize[10], slice, outndim;
+  int hasblank=gal_blank_present(in, 0);
+  gal_data_t *wht=NULL, *sum=NULL, *num=NULL;
+  double *dd, *dw, *df, *warr=NULL, *farr=NULL;
+
+
+  /* Basic sanity checks. */
+  wht=dimension_collapse_sanity_check(in, weight, c_dim, hasblank,
+                                      &cnum, &warr);
+
+  /* Set the size of the collapsed output. */
+  dimension_collapse_sizes(in, c_dim, &outndim, outdsize);
+
+  /* The sum array. */
+  sum=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, outndim, outdsize, in->wcs,
+                     1, in->minmapsize, NULL, NULL, NULL);
+
+  /* If a weighted mean is requested. */
+  if( weight )
+    {
+      /* There are blank values, so we'll need to keep the sums of the
+         weights for each collapsed dimension */
+      if( hasblank )
+        wsumarr=gal_pointer_allocate(GAL_TYPE_FLOAT64, sum->size, 1,
+                                     __func__, "wsumarr");
+
+      /* There aren't any blank values, so one summation over the
+         weights is enough to calculate the weighted mean. */
+      else
+        {
+          wsum=0.0f;
+          df=(dd=weight->array)+weight->size;
+          do wsum += *dd++; while(dd<df);
+        }
+    }
+  /* No weight is given, so we'll need the number of elements. */
+  else if( hasblank )
+    num=gal_data_alloc(NULL, GAL_TYPE_INT32, outndim, outdsize, NULL,
+                       1, in->minmapsize, NULL, NULL, NULL);
+
+  /* Set the array pointers. */
+  if(sum) farr=sum->array;
+  if(num) iarr=num->array;
+
+  /* Parse the dataset. */
+  switch(in->type)
+    {
+    case GAL_TYPE_UINT8:     COLLAPSE_DIM( uint8_t  );   break;
+    case GAL_TYPE_INT8:      COLLAPSE_DIM( int8_t   );   break;
+    case GAL_TYPE_UINT16:    COLLAPSE_DIM( uint16_t );   break;
+    case GAL_TYPE_INT16:     COLLAPSE_DIM( int16_t  );   break;
+    case GAL_TYPE_UINT32:    COLLAPSE_DIM( uint32_t );   break;
+    case GAL_TYPE_INT32:     COLLAPSE_DIM( int32_t  );   break;
+    case GAL_TYPE_UINT64:    COLLAPSE_DIM( uint64_t );   break;
+    case GAL_TYPE_INT64:     COLLAPSE_DIM( int64_t  );   break;
+    case GAL_TYPE_FLOAT32:   COLLAPSE_DIM( float    );   break;
+    case GAL_TYPE_FLOAT64:   COLLAPSE_DIM( double   );   break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: type value (%d) not recognized",
+            __func__, in->type);
+    }
+
+  /* If `num' is zero on any element, set its sum to NaN. */
+  if(num)
+    {
+      ii = num->array;
+      df = (dd=sum->array) + sum->size;
+      do if(*ii++==0) *dd=NAN; while(++dd<df);
+    }
+
+  /* Divide the sum by the number. */
+  df = (dd=sum->array) + sum->size;
+  if(weight)
+    {
+      if(hasblank) { dw=wsumarr;  do *dd /= *dw++; while(++dd<df); }
+      else                        do *dd /= wsum;  while(++dd<df);
+    }
+  else
+    if(num) { ii = num->array;    do *dd /= *ii++; while(++dd<df); }
+    else                          do *dd /= cnum;  while(++dd<df);
+
+  /* Correct the WCS, clean up and return. */
+  gal_wcs_remove_dimension(sum->wcs, in->ndim-c_dim);
+  if(wht!=weight) gal_data_free(wht);
+  if(wsumarr) free(wsumarr);
+  gal_data_free(num);
+  return sum;
+}
+
+
+
+
+
+gal_data_t *
+gal_dimension_collapse_number(gal_data_t *in, size_t c_dim)
+{
+  double *wsumarr=NULL;
+  double *warr=NULL, *farr=NULL;
+  int32_t *ii, *iif, *iarr=NULL;
+  size_t a, b, i, j, k, w, cnum=0;
+  size_t outdsize[10], slice, outndim;
+  int hasblank=gal_blank_present(in, 0);
+  gal_data_t *weight=NULL, *wht=NULL, *num=NULL;
+
+  /* Basic sanity checks. */
+  wht=dimension_collapse_sanity_check(in, weight, c_dim, hasblank,
+                                      &cnum, &warr);
+
+  /* Set the size of the collapsed output. */
+  dimension_collapse_sizes(in, c_dim, &outndim, outdsize);
+
+  /* The number dataset (when there are blank values).*/
+  num=gal_data_alloc(NULL, GAL_TYPE_INT32, outndim, outdsize, in->wcs,
+                     1, in->minmapsize, NULL, NULL, NULL);
+
+  /* Set the array pointers. */
+  iarr=num->array;
+
+  /* Parse the input dataset (if necessary). */
+  if(hasblank)
+    switch(in->type)
+      {
+      case GAL_TYPE_UINT8:     COLLAPSE_DIM( uint8_t  );   break;
+      case GAL_TYPE_INT8:      COLLAPSE_DIM( int8_t   );   break;
+      case GAL_TYPE_UINT16:    COLLAPSE_DIM( uint16_t );   break;
+      case GAL_TYPE_INT16:     COLLAPSE_DIM( int16_t  );   break;
+      case GAL_TYPE_UINT32:    COLLAPSE_DIM( uint32_t );   break;
+      case GAL_TYPE_INT32:     COLLAPSE_DIM( int32_t  );   break;
+      case GAL_TYPE_UINT64:    COLLAPSE_DIM( uint64_t );   break;
+      case GAL_TYPE_INT64:     COLLAPSE_DIM( int64_t  );   break;
+      case GAL_TYPE_FLOAT32:   COLLAPSE_DIM( float    );   break;
+      case GAL_TYPE_FLOAT64:   COLLAPSE_DIM( double   );   break;
+      default:
+        error(EXIT_FAILURE, 0, "%s: type value (%d) not recognized",
+              __func__, in->type);
+      }
+  else
+    {
+      iif=(ii=num->array)+num->size;
+      do *ii++ = cnum; while(ii<iif);
+    }
+
+  /* Remove the respective dimension in the WCS structure also (if any
+     exists). Note that `sum->ndim' has already been changed. So we'll use
+     `in->wcs'. */
+  gal_wcs_remove_dimension(num->wcs, in->ndim-c_dim);
+
+  /* Return. */
+  if(wht!=weight) gal_data_free(wht);
+  return num;
+}
diff --git a/lib/fits.c b/lib/fits.c
index 18ee668..772c621 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -2404,10 +2404,7 @@ fits_tab_read_ascii_float_special(char *filename, char 
*hdu, fitsfile *fptr,
 
 
 
-/* Read the column indexs given in the `indexll' linked list from a FITS
-   table into a linked list of data structures, note that this is a
-   low-level function, so the output data linked list is the inverse of the
-   input indexs linked list. */
+/* Read the column indexs into a dataset. */
 gal_data_t *
 gal_fits_tab_read(char *filename, char *hdu, size_t numrows,
                   gal_data_t *allcols, gal_list_sizet_t *indexll,
@@ -2415,88 +2412,121 @@ gal_fits_tab_read(char *filename, char *hdu, size_t 
numrows,
 {
   size_t i=0;
   void *blank;
-  size_t dsize;
   char **strarr;
   fitsfile *fptr;
   gal_data_t *out=NULL;
   gal_list_sizet_t *ind;
   int isfloat, status=0, anynul=0, hdutype;
 
-  /* Open the FITS file */
-  fptr=gal_fits_hdu_open_format(filename, hdu, 1);
+  /* We actually do have columns to read. */
+  if(numrows)
+    {
+      /* Open the FITS file */
+      fptr=gal_fits_hdu_open_format(filename, hdu, 1);
 
-  /* See if its a Binary or ASCII table (necessary for floating point blank
-     values). */
-  if (fits_get_hdu_type(fptr, &hdutype, &status) )
-    gal_fits_io_error(status, NULL);
+      /* See if its a Binary or ASCII table (necessary for floating point
+         blank values). */
+      if (fits_get_hdu_type(fptr, &hdutype, &status) )
+        gal_fits_io_error(status, NULL);
 
-  /* Pop each index and read/store the array. */
-  for(ind=indexll; ind!=NULL; ind=ind->next)
-    {
-      /* Allocate the necessary data structure (including the array) for
-         this column. */
-      dsize=numrows;
-      gal_list_data_add_alloc(&out, NULL, allcols[ind->v].type, 1, &dsize,
-                              NULL, 0, minmapsize, allcols[ind->v].name,
-                              allcols[ind->v].unit, allcols[ind->v].comment);
-
-      /* For a string column, we need an allocated array for each element,
-         even in binary values. This value should be stored in the
-         disp_width element of the data structure, which is done
-         automatically in `gal_fits_table_info'. */
-      if(out->type==GAL_TYPE_STRING)
+      /* Pop each index and read/store the array. */
+      for(ind=indexll; ind!=NULL; ind=ind->next)
         {
-          strarr=out->array;
-          for(i=0;i<numrows;++i)
+          /* Allocate the necessary data structure (including the array)
+             for this column. */
+          gal_list_data_add_alloc(&out, NULL, allcols[ind->v].type, 1,
+                                  &numrows, NULL, 0, minmapsize,
+                                  allcols[ind->v].name, allcols[ind->v].unit,
+                                  allcols[ind->v].comment);
+
+          /* For a string column, we need an allocated array for each element,
+             even in binary values. This value should be stored in the
+             disp_width element of the data structure, which is done
+             automatically in `gal_fits_table_info'. */
+          if(out->type==GAL_TYPE_STRING)
             {
-              errno=0;
-              strarr[i]=calloc(allcols[ind->v].disp_width+1,
-                               sizeof *strarr[i]);
-              if(strarr[i]==NULL)
-                error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for "
-                      "strarr[%zu]", __func__,
-                      (allcols[ind->v].disp_width+1) * sizeof *strarr[i], i);
+              strarr=out->array;
+              for(i=0;i<numrows;++i)
+                {
+                  errno=0;
+                  strarr[i]=calloc(allcols[ind->v].disp_width+1,
+                                   sizeof *strarr[i]);
+                  if(strarr[i]==NULL)
+                    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for "
+                          "strarr[%zu]", __func__,
+                          (allcols[ind->v].disp_width+1) * sizeof *strarr[i],
+                          i);
+                }
             }
-        }
 
-      /* Allocate a blank value for the given type and read/store the
-         column using CFITSIO. Note that for binary tables, we only need
-         blank values for integer types. For binary floating point types,
-         the FITS standard defines blanks as NaN (same as almost any other
-         software like Gnuastro). However if a blank value is specified,
-         CFITSIO will convert other special numbers like `inf' to NaN
-         also. We want to be able to distringuish `inf' and NaN here, so
-         for floating point types in binary tables, we won't define any
-         blank value. In ASCII tables, CFITSIO doesn't read the `NAN'
-         values (that it has written itself) unless we specify a blank
-         pointer/value. */
-      isfloat = out->type==GAL_TYPE_FLOAT32 || out->type==GAL_TYPE_FLOAT64;
-      blank = ( ( hdutype==BINARY_TBL && isfloat )
-                ? NULL
-                : gal_blank_alloc_write(out->type) );
-      fits_read_col(fptr, gal_fits_type_to_datatype(out->type), ind->v+1,
-                    1, 1, out->size, blank, out->array, &anynul, &status);
-
-      /* In the ASCII table format, CFITSIO might not be able to read `INF'
-         or `-INF'. In this case, it will set status to `BAD_C2D' or
-         `BAD_C2F'. So, we'll use our own parser for the column values. */
-      if( hdutype==ASCII_TBL
-          && isfloat
-          && (status==BAD_C2D || status==BAD_C2F) )
-        {
-          fits_tab_read_ascii_float_special(filename, hdu, fptr, out,
-                                            ind->v+1, numrows, minmapsize);
-          status=0;
+          /* Allocate a blank value for the given type and read/store the
+             column using CFITSIO. Note that for binary tables, we only
+             need blank values for integer types. For binary floating point
+             types, the FITS standard defines blanks as NaN (same as almost
+             any other software like Gnuastro). However if a blank value is
+             specified, CFITSIO will convert other special numbers like
+             `inf' to NaN also. We want to be able to distringuish `inf'
+             and NaN here, so for floating point types in binary tables, we
+             won't define any blank value. In ASCII tables, CFITSIO doesn't
+             read the `NAN' values (that it has written itself) unless we
+             specify a blank pointer/value. */
+          isfloat = ( out->type==GAL_TYPE_FLOAT32
+                      || out->type==GAL_TYPE_FLOAT64 );
+          blank = ( ( hdutype==BINARY_TBL && isfloat )
+                    ? NULL
+                    : gal_blank_alloc_write(out->type) );
+          fits_read_col(fptr, gal_fits_type_to_datatype(out->type), ind->v+1,
+                        1, 1, out->size, blank, out->array, &anynul, &status);
+
+          /* In the ASCII table format, CFITSIO might not be able to read
+             `INF' or `-INF'. In this case, it will set status to `BAD_C2D'
+             or `BAD_C2F'. So, we'll use our own parser for the column
+             values. */
+          if( hdutype==ASCII_TBL
+              && isfloat
+              && (status==BAD_C2D || status==BAD_C2F) )
+            {
+              fits_tab_read_ascii_float_special(filename, hdu, fptr, out,
+                                                ind->v+1, numrows,
+                                                minmapsize);
+              status=0;
+            }
+
+          /* Clean up and sanity check. */
+          if(blank) free(blank);
+          gal_fits_io_error(status, NULL);
         }
 
-      /* Clean up and sanity check. */
-      if(blank) free(blank);
+      /* Close the FITS file */
+      fits_close_file(fptr, &status);
       gal_fits_io_error(status, NULL);
     }
 
-  /* Close the FITS file */
-  fits_close_file(fptr, &status);
-  gal_fits_io_error(status, NULL);
+  /* There are no rows to read (`numrows==NULL'). Make an empty-sized
+     array. */
+  else
+    {
+      /* We are setting a 1-element array to avoid any allocation
+         errors. Then we are freeing the allocated spaces and correcting
+         the sizes. */
+      numrows=1;
+      for(ind=indexll; ind!=NULL; ind=ind->next)
+        {
+          /* Do the allocation. */
+          gal_list_data_add_alloc(&out, NULL, allcols[ind->v].type, 1,
+                                  &numrows, NULL, 0, minmapsize,
+                                  allcols[ind->v].name, allcols[ind->v].unit,
+                                  allcols[ind->v].comment);
+
+          /* Correct the array and sizes. */
+          out->size=0;
+          free(out->array);
+          free(out->dsize);
+          out->dsize=out->array=NULL;
+        }
+    }
+
+  /* Return the output. */
   return out;
 }
 
diff --git a/lib/gnuastro-internal/config.h.in 
b/lib/gnuastro-internal/config.h.in
index 47a3b96..67c1383 100644
--- a/lib/gnuastro-internal/config.h.in
+++ b/lib/gnuastro-internal/config.h.in
@@ -34,27 +34,28 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 /* Configuration macros: */
 
-#define GAL_CONFIG_VERSION              "@VERSION@"
+#define GAL_CONFIG_VERSION                  "@VERSION@"
 
-#define GAL_CONFIG_HAVE_LIBGIT2         @HAVE_LIBGIT2@
+#define GAL_CONFIG_HAVE_LIBGIT2             @HAVE_LIBGIT2@
 
-#define GAL_CONFIG_HAVE_WCSLIB_VERSION  @HAVE_WCSLIB_VERSION@
+#define GAL_CONFIG_HAVE_FITS_IS_REENTRANT   @HAVE_FITS_IS_REENTRANT@
+#define GAL_CONFIG_HAVE_WCSLIB_VERSION      @HAVE_WCSLIB_VERSION@
 
-#define GAL_CONFIG_HAVE_PTHREAD_BARRIER @HAVE_PTHREAD_BARRIER@
+#define GAL_CONFIG_HAVE_PTHREAD_BARRIER     @HAVE_PTHREAD_BARRIER@
 
-#define GAL_CONFIG_BIN_OP_UINT8         @HAVE_BIN_OP_UINT8@
-#define GAL_CONFIG_BIN_OP_INT8          @HAVE_BIN_OP_INT8@
-#define GAL_CONFIG_BIN_OP_UINT16        @HAVE_BIN_OP_UINT16@
-#define GAL_CONFIG_BIN_OP_INT16         @HAVE_BIN_OP_INT16@
-#define GAL_CONFIG_BIN_OP_UINT32        @HAVE_BIN_OP_UINT32@
-#define GAL_CONFIG_BIN_OP_INT32         @HAVE_BIN_OP_INT32@
-#define GAL_CONFIG_BIN_OP_UINT64        @HAVE_BIN_OP_UINT64@
-#define GAL_CONFIG_BIN_OP_INT64         @HAVE_BIN_OP_INT64@
-#define GAL_CONFIG_BIN_OP_FLOAT32       @HAVE_BIN_OP_FLOAT32@
-#define GAL_CONFIG_BIN_OP_FLOAT64       @HAVE_BIN_OP_FLOAT64@
+#define GAL_CONFIG_BIN_OP_UINT8             @HAVE_BIN_OP_UINT8@
+#define GAL_CONFIG_BIN_OP_INT8              @HAVE_BIN_OP_INT8@
+#define GAL_CONFIG_BIN_OP_UINT16            @HAVE_BIN_OP_UINT16@
+#define GAL_CONFIG_BIN_OP_INT16             @HAVE_BIN_OP_INT16@
+#define GAL_CONFIG_BIN_OP_UINT32            @HAVE_BIN_OP_UINT32@
+#define GAL_CONFIG_BIN_OP_INT32             @HAVE_BIN_OP_INT32@
+#define GAL_CONFIG_BIN_OP_UINT64            @HAVE_BIN_OP_UINT64@
+#define GAL_CONFIG_BIN_OP_INT64             @HAVE_BIN_OP_INT64@
+#define GAL_CONFIG_BIN_OP_FLOAT32           @HAVE_BIN_OP_FLOAT32@
+#define GAL_CONFIG_BIN_OP_FLOAT64           @HAVE_BIN_OP_FLOAT64@
 
-#define GAL_CONFIG_SIZEOF_LONG          @SIZEOF_LONG@
-#define GAL_CONFIG_SIZEOF_SIZE_T        @SIZEOF_SIZE_T@
+#define GAL_CONFIG_SIZEOF_LONG              @SIZEOF_LONG@
+#define GAL_CONFIG_SIZEOF_SIZE_T            @SIZEOF_SIZE_T@
 
 
 /* C++ Preparations */
diff --git a/lib/gnuastro/dimension.h b/lib/gnuastro/dimension.h
index 5c83a0d..a0623a2 100644
--- a/lib/gnuastro/dimension.h
+++ b/lib/gnuastro/dimension.h
@@ -97,6 +97,23 @@ gal_dimension_dist_manhattan(size_t *a, size_t *b, size_t 
ndim);
 
 
 /************************************************************************/
+/********************    Collapsing a dimension    **********************/
+/************************************************************************/
+gal_data_t *
+gal_dimension_collapse_sum(gal_data_t *in, size_t c_dim, gal_data_t *weight);
+
+gal_data_t *
+gal_dimension_collapse_mean(gal_data_t *in, size_t c_dim,
+                            gal_data_t *weight);
+
+gal_data_t *
+gal_dimension_collapse_number(gal_data_t *in, size_t c_dim);
+
+
+
+
+
+/************************************************************************/
 /********************          Neighbors           **********************/
 /************************************************************************/
 /* Purpose
diff --git a/lib/gnuastro/wcs.h b/lib/gnuastro/wcs.h
index 72be5a0..13c2bb5 100644
--- a/lib/gnuastro/wcs.h
+++ b/lib/gnuastro/wcs.h
@@ -74,6 +74,9 @@ struct wcsprm *
 gal_wcs_copy(struct wcsprm *wcs);
 
 void
+gal_wcs_remove_dimension(struct wcsprm *wcs, size_t fitsdim);
+
+void
 gal_wcs_on_tile(gal_data_t *tile);
 
 double *
diff --git a/lib/txt.c b/lib/txt.c
index a8655e0..6b16337 100644
--- a/lib/txt.c
+++ b/lib/txt.c
@@ -863,7 +863,7 @@ gal_txt_read(char *filename, size_t *dsize, gal_data_t 
*info,
   char **tokens;
   gal_data_t *out=NULL;
   gal_list_sizet_t *ind;
-  size_t maxcolnum=0, rowind=0, lineno=0, ndim;
+  size_t one=1, maxcolnum=0, rowind=0, lineno=0, ndim;
   size_t linelen=10;        /* `linelen' will be increased by `getline'. */
 
   /* Open the file. */
@@ -892,13 +892,27 @@ gal_txt_read(char *filename, size_t *dsize, gal_data_t 
*info,
     case TXT_FORMAT_TABLE:
       for(ind=indexll; ind!=NULL; ind=ind->next)
         {
+          /* Allocate the necessary space. We are setting a 1-element array
+             to avoid any allocation errors. Then we are freeing the
+             allocated spaces and correcting the sizes.*/
           ndim=1;
           maxcolnum = maxcolnum>ind->v+1 ? maxcolnum : ind->v+1;
-          gal_list_data_add_alloc(&out, NULL, info[ind->v].type, ndim, dsize,
-                                  NULL, 0, minmapsize, info[ind->v].name,
-                                  info[ind->v].unit, info[ind->v].comment);
+          gal_list_data_add_alloc(&out, NULL, info[ind->v].type, ndim,
+                                  dsize[0]?dsize:&one, NULL, 0, minmapsize,
+                                  info[ind->v].name, info[ind->v].unit,
+                                  info[ind->v].comment);
           out->disp_width=info[ind->v].disp_width;
           out->status=ind->v+1;
+
+          /* If there were no actual rows (dsize[0]==0), free the allocated
+             spaces and correct the size. */
+          if(dsize[0]==0)
+            {
+              out->size=0;
+              free(out->array);
+              free(out->dsize);
+              out->dsize=out->array=NULL;
+            }
         }
       break;
 
diff --git a/lib/wcs.c b/lib/wcs.c
index 47a4420..dfb3d83 100644
--- a/lib/wcs.c
+++ b/lib/wcs.c
@@ -258,6 +258,176 @@ gal_wcs_copy(struct wcsprm *wcs)
 
 
 
+/* Remove the algorithm part of CTYPE (anything after, and including, a
+   `-') if necessary. */
+static void
+wcs_ctype_noalgorithm(char *str)
+{
+  size_t i, len=strlen(str);
+  for(i=0;i<len;++i) if(str[i]=='-') { str[i]='\0'; break; }
+}
+
+
+
+
+/* See if the CTYPE string ends with TAN. */
+static int
+wcs_ctype_has_tan(char *str)
+{
+  size_t len=strlen(str);
+
+  return !strcmp(&str[len-3], "TAN");
+}
+
+
+
+
+
+/* Remove dimension. */
+#define WCS_REMOVE_DIM_CHECK 0
+void
+gal_wcs_remove_dimension(struct wcsprm *wcs, size_t fitsdim)
+{
+  size_t c, i, j, naxis=wcs->naxis;
+
+  /* Sanity check. */
+  if(fitsdim==0 || fitsdim>wcs->naxis)
+    error(EXIT_FAILURE, 0, "%s: requested dimension (fitsdim=%zu) must be "
+          "larger than zero and smaller than the number of dimensions in "
+          "the given WCS structure (%zu)", __func__, fitsdim, naxis);
+
+  /* If the WCS structure is NULL, just return. */
+  if(wcs==NULL) return;
+
+  /**************************************************/
+#if WCS_REMOVE_DIM_CHECK
+  printf("\n\nfitsdim: %zu\n", fitsdim);
+  printf("\n##################\n");
+  /*
+  wcs->pc[0]=0;   wcs->pc[1]=1;   wcs->pc[2]=2;
+  wcs->pc[3]=3;   wcs->pc[4]=4;   wcs->pc[5]=5;
+  wcs->pc[6]=6;   wcs->pc[7]=7;   wcs->pc[8]=8;
+  */
+  for(i=0;i<wcs->naxis;++i)
+    {
+      for(j=0;j<wcs->naxis;++j)
+        printf("%-5g", wcs->pc[i*wcs->naxis+j]);
+      printf("\n");
+    }
+#endif
+  /**************************************************/
+
+
+  /* First loop over the arrays. */
+  for(i=0;i<naxis;++i)
+    {
+      /* The dimensions are in FITS order, but counting starts from 0, so
+         we'll have to subtract 1 from `fitsdim'. */
+      if(i>fitsdim-1)
+        {
+          /* 1-D arrays. */
+          if(wcs->crpix) wcs->crpix[i-1] = wcs->crpix[i];
+          if(wcs->cdelt) wcs->cdelt[i-1] = wcs->cdelt[i];
+          if(wcs->crval) wcs->crval[i-1] = wcs->crval[i];
+          if(wcs->crota) wcs->crota[i-1] = wcs->crota[i];
+          if(wcs->crder) wcs->crder[i-1] = wcs->crder[i];
+          if(wcs->csyer) wcs->csyer[i-1] = wcs->csyer[i];
+
+          /* The strings are all statically allocated, so we don't need to
+             check. */
+          memcpy(wcs->cunit[i-1], wcs->cunit[i], 72);
+          memcpy(wcs->ctype[i-1], wcs->ctype[i], 72);
+          memcpy(wcs->cname[i-1], wcs->cname[i], 72);
+
+          /* For 2-D arrays, just bring up all the rows. We'll fix the
+             columns in a second loop. */
+          for(j=0;j<naxis;++j)
+            {
+              if(wcs->pc) wcs->pc[ (i-1)*naxis+j ] = wcs->pc[ i*naxis+j ];
+              if(wcs->cd) wcs->cd[ (i-1)*naxis+j ] = wcs->cd[ i*naxis+j ];
+            }
+        }
+    }
+
+
+  /**************************************************/
+#if WCS_REMOVE_DIM_CHECK
+  printf("\n###### Respective row removed (replaced).\n");
+  for(i=0;i<wcs->naxis;++i)
+    {
+      for(j=0;j<wcs->naxis;++j)
+        printf("%-5g", wcs->pc[i*wcs->naxis+j]);
+      printf("\n");
+    }
+#endif
+  /**************************************************/
+
+
+  /* Second loop for 2D arrays. */
+  c=0;
+  for(i=0;i<naxis;++i)
+    for(j=0;j<naxis;++j)
+      if(j!=fitsdim-1)
+        {
+          if(wcs->pc) wcs->pc[ c ] = wcs->pc[ i*naxis+j ];
+          if(wcs->cd) wcs->cd[ c ] = wcs->cd[ i*naxis+j ];
+          ++c;
+        }
+
+
+  /* Correct the total number of dimensions in the WCS structure. */
+  naxis = wcs->naxis -= 1;
+
+
+  /* The `TAN' algorithm needs two dimensions. So we need to remove it when
+     it can cause confusion. */
+  switch(naxis)
+    {
+    /* The `TAN' algorithm cannot be used for any single-dimensional
+       dataset. So we'll have to remove it if it exists. */
+    case 1:
+      wcs_ctype_noalgorithm(wcs->ctype[0]);
+      break;
+
+    /* For any other dimensionality, `TAN' should be kept only when exactly
+       two dimensions have it. */
+    default:
+
+      c=0;
+      for(i=0;i<naxis;++i)
+        if( wcs_ctype_has_tan(wcs->ctype[i]) )
+          ++c;
+
+      if(c!=2)
+        for(i=0;i<naxis;++i)
+          if( wcs_ctype_has_tan(wcs->ctype[i]) )
+            wcs_ctype_noalgorithm(wcs->ctype[i]);
+      break;
+    }
+
+
+
+  /**************************************************/
+#if WCS_REMOVE_DIM_CHECK
+  printf("\n###### Respective column removed.\n");
+  for(i=0;i<naxis;++i)
+    {
+      for(j=0;j<naxis;++j)
+        printf("%-5g", wcs->pc[i*naxis+j]);
+      printf("\n");
+    }
+  printf("\n###### One final string\n");
+  for(i=0;i<naxis;++i)
+    printf("%s\n", wcs->ctype[i]);
+  exit(0);
+#endif
+  /**************************************************/
+}
+
+
+
+
+
 /* Using the block data structure of the tile, add a WCS structure for
    it. In many cases, tiles are created for internal processing, so there
    is no need to keep their WCS. Hence for preformance reasons, when
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f38faf5..a0406e3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -42,6 +42,9 @@
 ## .log file is created. The check if the input for a test exists or not
 ## should be checked in the test that depends on it, it can't be done here
 ## in the Makefile.
+if COND_CHECK_WITH_VALGRIND
+  MAYBE_CHECK_WITH_PROGRAM = "valgrind"
+endif
 if COND_HASGHOSTSCRIPT
   MAYBE_HASGHOSTSCRIPT = "yes"
 endif
@@ -51,6 +54,11 @@ endif
 if COND_HASGNULIBTOOL
   MAYBE_HASGNULIBTOOL = "yes"
 endif
+if COND_HASCXX
+  MAYBE_CXX_PROGS    = versioncxx
+  MAYBE_CXX_TESTS    = lib/versioncxx.sh
+  versioncxx_SOURCES = lib/versioncxx.cpp
+endif
 if COND_ARITHMETIC
   MAYBE_ARITHMETIC_TESTS = arithmetic/snimage.sh arithmetic/onlynumbers.sh \
   arithmetic/where.sh arithmetic/or.sh arithmetic/connected-components.sh
@@ -197,13 +205,14 @@ endif
 
 
 # Environment variables for the test scripts.
-AM_TESTS_ENVIRONMENT =                              \
-export mkdir_p="$(MKDIR_P)";                        \
-export topsrc=$(top_srcdir);                        \
-export topbuild=$(top_builddir);                    \
-export haslibjpeg=$(MAYBE_HASLIBJPEG);              \
-export hasgnulibtool=$(MAYBE_HASGNULIBTOOL);        \
-export hasghostscript=$(MAYBE_HASGHOSTSCRIPT);
+AM_TESTS_ENVIRONMENT =                                   \
+export mkdir_p="$(MKDIR_P)";                             \
+export topsrc=$(top_srcdir);                             \
+export topbuild=$(top_builddir);                         \
+export haslibjpeg=$(MAYBE_HASLIBJPEG);                   \
+export hasgnulibtool=$(MAYBE_HASGNULIBTOOL);             \
+export hasghostscript=$(MAYBE_HASGHOSTSCRIPT);           \
+export check_with_program=$(MAYBE_CHECK_WITH_PROGRAM);
 
 
 
@@ -228,9 +237,8 @@ AM_CPPFLAGS = -I\$(top_srcdir)/lib -I\$(top_builddir)/lib
 
 # Rest of library check settings.
 LDADD = -lgnuastro
-check_PROGRAMS = multithread versioncxx
+check_PROGRAMS = multithread $(MAYBE_CXX_PROGS)
 multithread_SOURCES = lib/multithread.c
-versioncxx_SOURCES  = lib/versioncxx.cpp
 lib/multithread.sh: mkprof/mosaic1.sh.log
 
 
@@ -239,7 +247,7 @@ lib/multithread.sh: mkprof/mosaic1.sh.log
 
 # Final Tests
 # ===========
-TESTS = prepconf.sh lib/multithread.sh lib/versioncxx.sh                   \
+TESTS = prepconf.sh lib/multithread.sh $(MAYBE_CXX_TESTS)                  \
   $(MAYBE_ARITHMETIC_TESTS) $(MAYBE_BUILDPROG_TESTS)                       \
   $(MAYBE_CONVERTT_TESTS) $(MAYBE_CONVOLVE_TESTS) $(MAYBE_COSMICCAL_TESTS) \
   $(MAYBE_CROP_TESTS) $(MAYBE_FITS_TESTS) $(MAYBE_MATCH_TESTS)             \
diff --git a/tests/arithmetic/connected-components.sh 
b/tests/arithmetic/connected-components.sh
index dfdf058..442f4bb 100755
--- a/tests/arithmetic/connected-components.sh
+++ b/tests/arithmetic/connected-components.sh
@@ -49,5 +49,9 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img 2 connected-components -hDETECTIONS   \
-          --output=connected-components.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img 2 connected-components -hDETECTIONS   \
+                              --output=connected-components.fits
diff --git a/tests/arithmetic/onlynumbers.sh b/tests/arithmetic/onlynumbers.sh
index 2a99de9..db4bdf0 100755
--- a/tests/arithmetic/onlynumbers.sh
+++ b/tests/arithmetic/onlynumbers.sh
@@ -44,4 +44,8 @@ if [ ! -f $execname ]; then echo "$execname not created."; 
exit 77; fi
 
 # Actual test script
 # ==================
-$execname -1 3.45 x
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname -1 3.45 x
diff --git a/tests/arithmetic/or.sh b/tests/arithmetic/or.sh
index 9437258..a24fc67 100755
--- a/tests/arithmetic/or.sh
+++ b/tests/arithmetic/or.sh
@@ -49,4 +49,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img 1 eq $img 3 eq or -gOBJECTS --output=or.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img 1 eq $img 3 eq or -gOBJECTS --output=or.fits
diff --git a/tests/arithmetic/snimage.sh b/tests/arithmetic/snimage.sh
index 37dcb2d..4be0b94 100755
--- a/tests/arithmetic/snimage.sh
+++ b/tests/arithmetic/snimage.sh
@@ -51,5 +51,9 @@ if [ ! -f $imgnc    ]; then echo "$imgnc does not exist."; 
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $imgin $imgnc - $imgnc / --hdu=1 --hdu=SKY --hdu=SKY_STD  \
-          --output=snimage.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $imgin $imgnc - $imgnc / --hdu=1 --hdu=SKY  \
+                               --hdu=SKY_STD  --output=snimage.fits
diff --git a/tests/arithmetic/where.sh b/tests/arithmetic/where.sh
index a8948b0..4e52514 100755
--- a/tests/arithmetic/where.sh
+++ b/tests/arithmetic/where.sh
@@ -49,4 +49,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img $img 0 eq nan where -h1 -h2 --output=where.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img $img 0 eq nan where -h1 -h2 
--output=where.fits
diff --git a/tests/buildprog/simpleio.sh b/tests/buildprog/simpleio.sh
index d76baa0..69baaf8 100755
--- a/tests/buildprog/simpleio.sh
+++ b/tests/buildprog/simpleio.sh
@@ -65,4 +65,9 @@ fi
 #
 # Except for `gnuastro/config.h', all headers are installed in
 # `$topsrc/lib' and `gnuastro/config.h' is in "../lib/"
-$execname $source $img 1 --la=../lib/libgnuastro.la -I$topsrc/lib -I../lib/
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $source $img 1 --la=../lib/libgnuastro.la \
+                              -I$topsrc/lib -I../lib/
diff --git a/tests/convertt/blankch.sh b/tests/convertt/blankch.sh
index 0cbf207..c0af7a8 100755
--- a/tests/convertt/blankch.sh
+++ b/tests/convertt/blankch.sh
@@ -53,4 +53,8 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 
 # Actual test script
 # ==================
-$execname blank $img blank --output=blankch.jpg
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname blank $img blank --output=blankch.jpg
diff --git a/tests/convertt/fitstojpeg.sh b/tests/convertt/fitstojpeg.sh
index 1c17884..8e6bab2 100755
--- a/tests/convertt/fitstojpeg.sh
+++ b/tests/convertt/fitstojpeg.sh
@@ -53,4 +53,8 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 
 # Actual test script
 # ==================
-$execname $img --invert --output=jpg
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --invert --output=jpg
diff --git a/tests/convertt/fitstojpegcmyk.sh b/tests/convertt/fitstojpegcmyk.sh
index 89c7d2a..79b77ce 100755
--- a/tests/convertt/fitstojpegcmyk.sh
+++ b/tests/convertt/fitstojpegcmyk.sh
@@ -52,4 +52,8 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 
 # Actual test script
 # ==================
-$execname blank blank blank $img --output=f2jcmyk.jpg
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname blank blank blank $img --output=f2jcmyk.jpg
diff --git a/tests/convertt/fitstopdf.sh b/tests/convertt/fitstopdf.sh
index 15bd654..9c6e6da 100755
--- a/tests/convertt/fitstopdf.sh
+++ b/tests/convertt/fitstopdf.sh
@@ -55,4 +55,8 @@ fi
 
 # Actual test script
 # ==================
-$execname $img --output=pdf --invert
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --output=pdf --invert
diff --git a/tests/convertt/fitstotxt.sh b/tests/convertt/fitstotxt.sh
index 595a223..7deee8f 100755
--- a/tests/convertt/fitstotxt.sh
+++ b/tests/convertt/fitstotxt.sh
@@ -47,4 +47,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --output=psf.txt
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --output=psf.txt
diff --git a/tests/convertt/jpegtofits.sh b/tests/convertt/jpegtofits.sh
index ea6c028..d1b0e9a 100755
--- a/tests/convertt/jpegtofits.sh
+++ b/tests/convertt/jpegtofits.sh
@@ -53,4 +53,8 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 
 # Actual test script
 # ==================
-$execname $img --output=fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --output=fits
diff --git a/tests/convertt/jpegtotxt.sh b/tests/convertt/jpegtotxt.sh
index 9cc8712..55a4f68 100755
--- a/tests/convertt/jpegtotxt.sh
+++ b/tests/convertt/jpegtotxt.sh
@@ -53,4 +53,8 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 
 # Actual test script
 # ==================
-$execname $img --output=jpegtotxt.txt
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --output=jpegtotxt.txt
diff --git a/tests/convolve/frequency.sh b/tests/convolve/frequency.sh
index da84ec2..80690fe 100755
--- a/tests/convolve/frequency.sh
+++ b/tests/convolve/frequency.sh
@@ -50,4 +50,9 @@ if [ ! -f $psf      ]; then echo "$psf does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --kernel=$psf --domain=frequency 
--output=convolve_frequency.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --kernel=$psf --domain=frequency \
+                              --output=convolve_frequency.fits
diff --git a/tests/convolve/spatial.sh b/tests/convolve/spatial.sh
index 344a870..4cf96c9 100755
--- a/tests/convolve/spatial.sh
+++ b/tests/convolve/spatial.sh
@@ -50,4 +50,9 @@ if [ ! -f $psf      ]; then echo "$psf does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --kernel=$psf --domain=spatial --output=convolve_spatial.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --kernel=$psf --domain=spatial   \
+                              --output=convolve_spatial.fits
diff --git a/tests/cosmiccal/simpletest.sh b/tests/cosmiccal/simpletest.sh
index 08bce79..123fd1c 100755
--- a/tests/cosmiccal/simpletest.sh
+++ b/tests/cosmiccal/simpletest.sh
@@ -44,4 +44,8 @@ if [ ! -f $execname ]; then echo "$execname not created.";    
exit 77; fi
 
 # Actual test script
 # ==================
-$execname --redshift=2.5
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname --redshift=2.5
diff --git a/tests/crop/imgcat.sh b/tests/crop/imgcat.sh
index 9693a79..7a865c8 100755
--- a/tests/crop/imgcat.sh
+++ b/tests/crop/imgcat.sh
@@ -53,7 +53,12 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # The number of threads is one so if CFITSIO does is not configured to
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
 cat=$topsrc/tests/$prog/cat.txt
-$execname $img --catalog=$cat --suffix=_imgcat.fits --numthreads=1      \
-          --zeroisnotblank --coordcol=X_CENTER --coordcol=Y_CENTER      \
-          --namecol=NAME --mode=img --width=201
+$check_with_program $execname $img --catalog=$cat --suffix=_imgcat.fits    \
+                              --numthreads=1 --zeroisnotblank --mode=img   \
+                              --coordcol=X_CENTER --coordcol=Y_CENTER      \
+                              --namecol=NAME  --width=201
diff --git a/tests/crop/imgcenter.sh b/tests/crop/imgcenter.sh
index 03960ef..2aa3753 100755
--- a/tests/crop/imgcenter.sh
+++ b/tests/crop/imgcenter.sh
@@ -53,5 +53,9 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # The number of threads is one so if CFITSIO does is not configured to
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
-$execname $img --center=251,251 --output=crop_imgcenter.fits    \
-          --numthreads=1 --mode=img --width=201
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --center=251,251 --mode=img --width=201 \
+                              --output=crop_imgcenter.fits --numthreads=1
diff --git a/tests/crop/imgcenternoblank.sh b/tests/crop/imgcenternoblank.sh
index cc52403..7d6f3f6 100755
--- a/tests/crop/imgcenternoblank.sh
+++ b/tests/crop/imgcenternoblank.sh
@@ -54,5 +54,10 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # The number of threads is one so if CFITSIO does is not configured to
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
-$execname $img --center=500,500 --noblank --numthreads=1              \
-          --output=crop_imgcenternoblank.fits --mode=img --width=201
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --center=500,500 --noblank --numthreads=1 \
+                              --output=crop_imgcenternoblank.fits --mode=img \
+                              --width=201
diff --git a/tests/crop/imgoutpolygon.sh b/tests/crop/imgoutpolygon.sh
index d742644..36dea9f 100755
--- a/tests/crop/imgoutpolygon.sh
+++ b/tests/crop/imgoutpolygon.sh
@@ -53,6 +53,10 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # The number of threads is one so if CFITSIO does is not configured to
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
-$execname $img $cat --mode=img --zeroisnotblank --outpolygon                  \
-          --polygon=209,50:436.76,151:475.64,438.2:210.6,454.04:121.4,289.88  \
-          --output=imgoutpolygon.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img $cat --mode=img --zeroisnotblank     \
+                              --outpolygon --output=imgoutpolygon.fits  \
+                              
--polygon=209,50:436.76,151:475.64,438.2:210.6,454.04:121.4,289.88
diff --git a/tests/crop/imgpolygon.sh b/tests/crop/imgpolygon.sh
index f3ae21b..e2a0241 100755
--- a/tests/crop/imgpolygon.sh
+++ b/tests/crop/imgpolygon.sh
@@ -49,5 +49,10 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img $cat --mode=img --zeroisnotblank --output=imgpolygon.fits      \
-          --polygon=209,50:436.76,151:475.64,438.2:210.6,454.04:121.4,289.88
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img $cat --mode=img --zeroisnotblank \
+                              --output=imgpolygon.fits              \
+                              
--polygon=209,50:436.76,151:475.64,438.2:210.6,454.04:121.4,289.88
diff --git a/tests/crop/section.sh b/tests/crop/section.sh
index 7d0704d..69b22f5 100755
--- a/tests/crop/section.sh
+++ b/tests/crop/section.sh
@@ -52,5 +52,9 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # The number of threads is one so if CFITSIO does is not configured to
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
-$execname $img --section=-10:*+10,:250 --output=crop_section.fits \
-          --numthreads=1 --mode=img
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --section=-10:*+10,:250 --mode=img    \
+                              --output=crop_section.fits --numthreads=1
diff --git a/tests/crop/wcscat.sh b/tests/crop/wcscat.sh
index ac82a89..3a56af6 100755
--- a/tests/crop/wcscat.sh
+++ b/tests/crop/wcscat.sh
@@ -54,7 +54,12 @@ done
 # The number of threads is one so if CFITSIO does is not configured to
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
 cat=$topsrc/tests/$prog/cat.txt
-$execname $img --catalog=$cat --suffix=_wcscat.fits            \
-          --zeroisnotblank --coordcol=4 --coordcol=DEC_CENTER  \
-          --numthreads=1 --mode=wcs --width=3/3600
+$check_with_program $execname $img --catalog=$cat --suffix=_wcscat.fits  \
+                              --zeroisnotblank --coordcol=4 --mode=wcs   \
+                              --coordcol=DEC_CENTER --numthreads=1       \
+                              --width=3/3600
diff --git a/tests/crop/wcscenter.sh b/tests/crop/wcscenter.sh
index c174aad..51673a0 100755
--- a/tests/crop/wcscenter.sh
+++ b/tests/crop/wcscenter.sh
@@ -49,5 +49,10 @@ done
 
 # Actual test script
 # ==================
-$execname $img --center=0.99917157,1.0008283 --output=crop_wcscenter.fits \
-          --mode=wcs --width=0.3/3600
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --center=0.99917157,1.0008283       \
+                              --output=crop_wcscenter.fits --mode=wcs  \
+                              --width=0.3/3600
diff --git a/tests/crop/wcspolygon.sh b/tests/crop/wcspolygon.sh
index 692a447..6d6f9db 100755
--- a/tests/crop/wcspolygon.sh
+++ b/tests/crop/wcspolygon.sh
@@ -50,5 +50,10 @@ done
 
 # Actual test script
 # ==================
-$execname $img --mode=wcs --zeroisnotblank --output=wcspolygon.fits         \
-        --polygon=0.99980497,1.0001967:0.998378,1.0012267:0.9999766,1.0013217
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --mode=wcs --zeroisnotblank   \
+                              --output=wcspolygon.fits           \
+                              
--polygon=0.99980497,1.0001967:0.998378,1.0012267:0.9999766,1.0013217
diff --git a/tests/during-dev.sh b/tests/during-dev.sh
index 6421c71..7a52e82 100755
--- a/tests/during-dev.sh
+++ b/tests/during-dev.sh
@@ -89,13 +89,24 @@ options=
 # RUN THE PROCEDURES
 # ==================
 
+# Stop the script if there are any errors.
+set -e
+
+
 # First, make sure the variables are set. Note that arguments and options
 # are not absolutly vital! so they are not checked here. The utility will
 # warn and halt if it needs them.
-if [ x"$outdir" = x ];     then echo "outdir is not set.";      exit 1; fi
-if [ x$utilname = x ];     then echo "utilname is not set.";    exit 1; fi
-if [ x"$numjobs" = x ];    then echo "numjobs is not set.";     exit 1; fi
-if [ x"$builddir" = x ];   then echo "builddir is not set.";    exit 1; fi
+if [ x"$outdir"   = x ]; then echo "outdir is not set.";   exit 1; fi
+if [ x"$numjobs"  = x ]; then echo "numjobs is not set.";  exit 1; fi
+if [ x"$utilname" = x ]; then echo "utilname is not set."; exit 1; fi
+if [ x"$builddir" = x ]; then echo "builddir is not set."; exit 1; fi
+
+
+# Make sure `utilname' doesn't start with `ast' (a common mistake).
+nameprefix="${utilname:0:3}"
+if [ x"$nameprefix" = x"ast" ]; then
+    echo "'utilname' must not start with 'ast'."; exit 1;
+fi
 
 
 # If builddir is relative, then append the current directory to make it
diff --git a/tests/fits/copyhdu.sh b/tests/fits/copyhdu.sh
index 95edc6f..15c6d8a 100755
--- a/tests/fits/copyhdu.sh
+++ b/tests/fits/copyhdu.sh
@@ -51,4 +51,8 @@ if [ ! -f $img2     ]; then echo "$img2 does not exist.";  
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img2 --copy="Mock profiles" --output=$img1
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img2 --copy="Mock profiles" --output=$img1
diff --git a/tests/fits/delete.sh b/tests/fits/delete.sh
index 554debb..583c67a 100755
--- a/tests/fits/delete.sh
+++ b/tests/fits/delete.sh
@@ -49,4 +49,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --delete=ABSJUNK --delete=ABSJNK2
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --delete=ABSJUNK --delete=ABSJNK2
diff --git a/tests/fits/print.sh b/tests/fits/print.sh
index 7ea8d8a..c191393 100755
--- a/tests/fits/print.sh
+++ b/tests/fits/print.sh
@@ -49,4 +49,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img
diff --git a/tests/fits/update.sh b/tests/fits/update.sh
index c4419bc..f89e0db 100755
--- a/tests/fits/update.sh
+++ b/tests/fits/update.sh
@@ -49,5 +49,9 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --update=ABSJUNK,8.231,"An updated value.",s \
-          --update=ABSJNK2,1232,"Another updated value",kg
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --update=ABSJUNK,8.231,"An updated 
value.",s \
+                              --update=ABSJNK2,1232,"Another updated value",kg
diff --git a/tests/fits/write.sh b/tests/fits/write.sh
index 27e490a..4ba5bf4 100755
--- a/tests/fits/write.sh
+++ b/tests/fits/write.sh
@@ -49,6 +49,12 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
 cp $img fitstest.fits
-$execname fitstest.fits --write=ABSJUNK,10.92,"A Fits keyword Test.",m/s \
-          --date --write=ABSJNK2,2343fdsa,"Another absolute junk test!"
+$check_with_program $execname fitstest.fits                                    
\
+                              --write=ABSJUNK,10.92,"A Fits keyword Test.",m/s 
\
+                              --date                                           
\
+                              --write=ABSJNK2,2343fdsa,"Another absolute junk 
test!"
diff --git a/tests/lib/multithread.sh b/tests/lib/multithread.sh
index 4e60f05..b9953ca 100755
--- a/tests/lib/multithread.sh
+++ b/tests/lib/multithread.sh
@@ -48,4 +48,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi;
 
 # Actual test script
 # ==================
-$execname
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname
diff --git a/tests/lib/versioncxx.sh b/tests/lib/versioncxx.sh
index 51cf328..c52e9e2 100755
--- a/tests/lib/versioncxx.sh
+++ b/tests/lib/versioncxx.sh
@@ -44,4 +44,8 @@ fi;
 
 # Actual test script
 # ==================
-$execname
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname
diff --git a/tests/match/merged-cols.sh b/tests/match/merged-cols.sh
index 3cc7245..3e4ac2a 100755
--- a/tests/match/merged-cols.sh
+++ b/tests/match/merged-cols.sh
@@ -49,5 +49,10 @@ if [ ! -f $execname ]; then echo "$execname not created."; 
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat1 $cat2 --aperture=0.5 -omatch-merged-cols.txt \
-          --outcols=a1,aEFGH,bACCU1,aIJKL,bACCU2
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat1 $cat2 --aperture=0.5             \
+                              -omatch-merged-cols.txt                \
+                              --outcols=a1,aEFGH,bACCU1,aIJKL,bACCU2
diff --git a/tests/match/positions.sh b/tests/match/positions.sh
index a620873..2622750 100755
--- a/tests/match/positions.sh
+++ b/tests/match/positions.sh
@@ -49,4 +49,9 @@ if [ ! -f $execname ]; then echo "$execname not created."; 
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat1 $cat2 --aperture=0.5 --log --output=match-positions.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat1 $cat2 --aperture=0.5 --log    \
+                              --output=match-positions.fits
diff --git a/tests/mkcatalog/aperturephot.sh b/tests/mkcatalog/aperturephot.sh
index c331029..760cd7a 100755
--- a/tests/mkcatalog/aperturephot.sh
+++ b/tests/mkcatalog/aperturephot.sh
@@ -51,6 +51,10 @@ if [ ! -f $objimg   ]; then echo "$objimg does not exist";  
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $objimg --hdu=1 --valuesfile=$img             \
-          --output=aperturephot.fits                    \
-          --objid --x --y --ra --dec --magnitude --sn
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $objimg --hdu=1 --valuesfile=$img       \
+                              --output=aperturephot.fits              \
+                              --objid --x --y --ra --dec --magnitude --sn
diff --git a/tests/mkcatalog/detections.sh b/tests/mkcatalog/detections.sh
index 5077dee..e6b42b5 100755
--- a/tests/mkcatalog/detections.sh
+++ b/tests/mkcatalog/detections.sh
@@ -51,5 +51,10 @@ if [ ! -f $base     ]; then echo "$base does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $labels -h1 --valuesfile=$base --tableformat=txt            \
-          --output=detections.txt --x --y --ra --dec --magnitude --sn
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $labels -h1 --valuesfile=$base            \
+                              --tableformat=txt --output=detections.txt \
+                              --x --y --ra --dec --magnitude --sn
diff --git a/tests/mkcatalog/objects-clumps.sh 
b/tests/mkcatalog/objects-clumps.sh
index 987d5e7..1cbd776 100755
--- a/tests/mkcatalog/objects-clumps.sh
+++ b/tests/mkcatalog/objects-clumps.sh
@@ -49,5 +49,10 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --x --y --ra --dec --magnitude --upperlimitmag --sn  \
-          --tableformat=txt --clumpscat --output=objects-clumps.txt
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --x --y --ra --dec --magnitude     \
+                              --upperlimitmag --sn --tableformat=txt  \
+                              --clumpscat --output=objects-clumps.txt
diff --git a/tests/mknoise/addnoise.sh b/tests/mknoise/addnoise.sh
index 2720182..206f5b1 100755
--- a/tests/mknoise/addnoise.sh
+++ b/tests/mknoise/addnoise.sh
@@ -56,7 +56,11 @@ if [ ! -f $img2     ]; then echo "$img2 does not exist.";    
exit 77; fi
 
 # Actual test script
 # ==================
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
 export GSL_RNG_SEED=1
 export GSL_RNG_TYPE=ranlxs2
-$execname --envseed $img1
-$execname --envseed $img2
+$check_with_program $execname --envseed $img1
+$check_with_program $execname --envseed $img2
diff --git a/tests/mkprof/clearcanvas.sh b/tests/mkprof/clearcanvas.sh
index 99fb94e..fc50418 100755
--- a/tests/mkprof/clearcanvas.sh
+++ b/tests/mkprof/clearcanvas.sh
@@ -53,5 +53,10 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat --background=$img --mforflatpix --clearcanvas  \
-          --type=int32 --output="clearcanvas.fits"
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat --background=$img --mforflatpix \
+                              --clearcanvas --type=int32           \
+                              --output="clearcanvas.fits"
diff --git a/tests/mkprof/ellipticalmasks.sh b/tests/mkprof/ellipticalmasks.sh
index 1423d7f..25b1167 100755
--- a/tests/mkprof/ellipticalmasks.sh
+++ b/tests/mkprof/ellipticalmasks.sh
@@ -53,5 +53,9 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat --background=$img --mforflatpix --replace --oversample=1 \
-          --output="ellipticalmasks.fits"
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat --background=$img --mforflatpix --replace \
+                              --oversample=1 --output="ellipticalmasks.fits"
diff --git a/tests/mkprof/mosaic1.sh b/tests/mkprof/mosaic1.sh
index b968da7..10eafa6 100755
--- a/tests/mkprof/mosaic1.sh
+++ b/tests/mkprof/mosaic1.sh
@@ -50,5 +50,9 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat --naxis=100,100
-mv 0_mkprofcat1.fits psf.fits
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat --naxis=100,100 && mv 0_mkprofcat1.fits \
+                              psf.fits
diff --git a/tests/mkprof/mosaic2.sh b/tests/mkprof/mosaic2.sh
index c994bc3..7aa6b67 100755
--- a/tests/mkprof/mosaic2.sh
+++ b/tests/mkprof/mosaic2.sh
@@ -55,4 +55,8 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat --naxis=100,100 --crpix=-99,1 --individual
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat --naxis=100,100 --crpix=-99,1 --individual
diff --git a/tests/mkprof/mosaic3.sh b/tests/mkprof/mosaic3.sh
index a2fa97e..7de1e43 100755
--- a/tests/mkprof/mosaic3.sh
+++ b/tests/mkprof/mosaic3.sh
@@ -52,4 +52,8 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat --naxis=100,100 --crpix=1,-99
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat --naxis=100,100 --crpix=1,-99
diff --git a/tests/mkprof/mosaic4.sh b/tests/mkprof/mosaic4.sh
index 0218a39..64a4013 100755
--- a/tests/mkprof/mosaic4.sh
+++ b/tests/mkprof/mosaic4.sh
@@ -53,4 +53,8 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat --naxis=100,100 --crpix=-99,-99
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat --naxis=100,100 --crpix=-99,-99
diff --git a/tests/mkprof/radeccat.sh b/tests/mkprof/radeccat.sh
index 801369f..83c17fe 100755
--- a/tests/mkprof/radeccat.sh
+++ b/tests/mkprof/radeccat.sh
@@ -48,4 +48,9 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $cat --ccol=RA --ccol=Dec --mode=wcs --naxis=100,100
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $cat --ccol=RA --ccol=Dec --mode=wcs   \
+                              --naxis=100,100
diff --git a/tests/noisechisel/noisechisel.sh b/tests/noisechisel/noisechisel.sh
index d2bf1b2..81e8add 100755
--- a/tests/noisechisel/noisechisel.sh
+++ b/tests/noisechisel/noisechisel.sh
@@ -49,5 +49,10 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --tilesize=100,100 --snquant=0.999 --cleangrowndet   \
-          --checkdetection --continueaftercheck
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --tilesize=100,100 --snquant=0.999 \
+                              --cleangrowndet --checkdetection        \
+                              --continueaftercheck
diff --git a/tests/segment/segment.sh b/tests/segment/segment.sh
index 995dfa3..ad32674 100755
--- a/tests/segment/segment.sh
+++ b/tests/segment/segment.sh
@@ -49,4 +49,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --tilesize=100,100 --snquant=0.99
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --tilesize=100,100 --snquant=0.99
diff --git a/tests/statistics/basicstats.sh b/tests/statistics/basicstats.sh
index c850702..7bb6ba4 100755
--- a/tests/statistics/basicstats.sh
+++ b/tests/statistics/basicstats.sh
@@ -49,4 +49,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img -g9500 -l11000 --numasciibins=65
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img -g9500 -l11000 --numasciibins=65
diff --git a/tests/statistics/estimate_sky.sh b/tests/statistics/estimate_sky.sh
index 6e43a38..3735844 100755
--- a/tests/statistics/estimate_sky.sh
+++ b/tests/statistics/estimate_sky.sh
@@ -54,4 +54,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # result will not be too accurate! Here we just want to see if the full
 # tessellation, estimation, interpolation and smoothing go nicely without
 # any errors.
-$execname $img --sky --checksky
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --sky --checksky
diff --git a/tests/table/fits-ascii-to-txt.sh b/tests/table/fits-ascii-to-txt.sh
index c86762e..e3f4393 100755
--- a/tests/table/fits-ascii-to-txt.sh
+++ b/tests/table/fits-ascii-to-txt.sh
@@ -49,4 +49,8 @@ if [ ! -f $table    ]; then echo "$table doesn't exist.";  
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $table --output=from-ascii-table.txt
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $table --output=from-ascii-table.txt
diff --git a/tests/table/fits-binary-to-txt.sh 
b/tests/table/fits-binary-to-txt.sh
index a98826e..8073b74 100755
--- a/tests/table/fits-binary-to-txt.sh
+++ b/tests/table/fits-binary-to-txt.sh
@@ -49,4 +49,8 @@ if [ ! -f $table    ]; then echo "$table doesn't exist.";  
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $table --output=from-binary-table.txt
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $table --output=from-binary-table.txt
diff --git a/tests/table/txt-to-fits-ascii.sh b/tests/table/txt-to-fits-ascii.sh
index 7f6eea0..e5edb5d 100755
--- a/tests/table/txt-to-fits-ascii.sh
+++ b/tests/table/txt-to-fits-ascii.sh
@@ -49,4 +49,9 @@ if [ ! -f $table    ]; then echo "$table doesn't exist.";  
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $table --output=ascii-table.fits --tableformat=fits-ascii
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $table --output=ascii-table.fits   \
+                              --tableformat=fits-ascii
diff --git a/tests/table/txt-to-fits-binary.sh 
b/tests/table/txt-to-fits-binary.sh
index 9649de7..02350f5 100755
--- a/tests/table/txt-to-fits-binary.sh
+++ b/tests/table/txt-to-fits-binary.sh
@@ -49,4 +49,9 @@ if [ ! -f $table    ]; then echo "$table does not exist."; 
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $table --output=binary-table.fits --tableformat=fits-binary
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $table --output=binary-table.fits \
+                              --tableformat=fits-binary
diff --git a/tests/warp/homographic.sh b/tests/warp/homographic.sh
index 58ac8c2..9d48afc 100755
--- a/tests/warp/homographic.sh
+++ b/tests/warp/homographic.sh
@@ -49,5 +49,9 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --output=homographic.fits \
-          --matrix="0.707106781,-0.707106781,0,  0.707106781, 0.707106781,0,  
0.001,0.002,1" --coveredfrac=0.5
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --output=homographic.fits --coveredfrac=0.5 
\
+                              --matrix="0.707106781,-0.707106781,0,  
0.707106781, 0.707106781,0,  0.001,0.002,1"
diff --git a/tests/warp/warp_scale.sh b/tests/warp/warp_scale.sh
index 8f91674..23537ad 100755
--- a/tests/warp/warp_scale.sh
+++ b/tests/warp/warp_scale.sh
@@ -49,4 +49,8 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --scale=1/5 --centeroncorner
+#
+# `check_with_program' can be something like `Valgrind' or an empty
+# string. Such programs will execute the command if present and help in
+# debugging when the developer doesn't have access to the user's system.
+$check_with_program $execname $img --scale=1/5 --centeroncorner



reply via email to

[Prev in Thread] Current Thread [Next in Thread]