gnuastro-commits
[Top][All Lists]
Advanced

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

[gnuastro-commits] master 3339b92 125/125: New implementation of MakeCat


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 3339b92 125/125: New implementation of MakeCatalog now complete
Date: Sun, 23 Apr 2017 22:36:52 -0400 (EDT)

branch: master
commit 3339b922d5e60073f8e32396fd3f702cdd94bb1f
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    New implementation of MakeCatalog now complete
    
    The re-write of MakeCatalog using all the new features provided by
    `gal_data_t' has been completed. The most important difference is that
    MakeCatalog will now treat each object completely independently (on a
    separate thread). Previously it would do two scans over the whole image and
    calculate the properties of all the objects together, but that structure
    was not easily extensible/flexible. This change was done following the
    future plan that was presented in the a given in the 26th ADASS in Trieste
    (https://arxiv.org/abs/1611.06387v1). Another major change is that the
    upper-limit magnitude can now also be found for clumps (thanks to the
    tessellation library of Gnuastro).
    
    This commit also involved several other changes that are listed below:
    
     - A joke: `make love' is a new Gnuastro build-time joke rule. It was
       mentioned by Stuart Feldman (creator of Make) in "The art of unix
       programming" and an old joke, so I thought it would be entertaining to
       have in Gnuastro too!
    
     - The keys that define the options in all Gnuastro programs started with
       the `ARGS_OPTION_KEY_' prefix. But this prefix didn't follow the naming
       convension of having the filename being the first part of the prefix. So
       these key prefixes are now `UI_KEY_' in all the programs (because they
       are defined in each program's `ui.h'.
    
     - Arithmetic was missing the `int32' and `uint32' type converters that
       have been added with this commit.
    
     - MakeProfiles now uses `gal_fits_img_write_to_type' to write the output
       in the user's requested type. This function was defined after the new
       implementation of MakeProfiles.
    
     - New `--cleandilated' option for NoiseChisel to remove dilated objects
       that have a low S/N (it is mainly useful on very clean or mock images),
       for non-clean noise, it will result in a decrease of completeness. With
       this new option, NoiseChisel will also print detection S/N values when
       run with the `--checkdetsn' option.
    
     - The new `GAL_BLANK_SIZE_T' has replaced `GAL_THREADS_NON_THRD_INDEX' for
       the functions that parse over threads.
    
     - The scripts in "Sufi simulates a detection" tutorial were checked and
       corrected to reflect thew updated program features. Also, the
       MakeCatalog section was edited and re-written where necessary to reflect
       the new features.
    
     - The new `gal_fits_key_clean_str_value' will remove the single quotes
       from string value keywords returned by CFITSIO in the same
       string. Previously it would return a new pointer in the same string
       which needed two pointers: one for using, one for freeing and that could
       be buggy.
    
     - The new `gal_tile_series_from_minmax' function will create a list of
       tiles given the minimum and maximum positions of each tile. This is
       currently only used in MakeCatalog, but will be useful in other contexts
       too.
    
     - The old `gal_wcs_xy_array_to_radec' was replaced with
       `gal_wcs_img_to_world'.
    
     - In `tests/during-dec.sh', it is now possible to set the number of jobs
       to run Make on.
---
 Makefile.am                      |   12 +
 bin/TEMPLATE/args.h              |    4 +-
 bin/TEMPLATE/main.c              |    2 +-
 bin/TEMPLATE/main.h              |    2 +-
 bin/TEMPLATE/ui.c                |   10 +-
 bin/TEMPLATE/ui.h                |    4 +-
 bin/arithmetic/arithmetic.c      |    4 +
 bin/convertt/args.h              |   24 +-
 bin/convertt/ui.h                |   24 +-
 bin/convolve/args.h              |   18 +-
 bin/convolve/convolve.c          |    6 +-
 bin/convolve/ui.c                |    8 +-
 bin/convolve/ui.h                |   24 +-
 bin/cosmiccal/args.h             |   14 +-
 bin/cosmiccal/ui.h               |   14 +-
 bin/crop/args.h                  |   46 +-
 bin/crop/crop.c                  |    6 +-
 bin/crop/ui.c                    |    2 +-
 bin/crop/ui.h                    |   46 +-
 bin/fits/Makefile.am             |    2 +-
 bin/fits/args.h                  |   26 +-
 bin/fits/fits.c                  |   16 +-
 bin/fits/ui.h                    |   26 +-
 bin/mkcatalog/Makefile.am        |    2 +-
 bin/mkcatalog/args.h             | 1282 +++++++++++------------
 bin/mkcatalog/astmkcatalog.conf  |   48 +-
 bin/mkcatalog/columns.c          | 2057 ++++++++++++++++++++++---------------
 bin/mkcatalog/columns.h          |   58 +-
 bin/mkcatalog/main.c             |    6 +-
 bin/mkcatalog/main.h             |  479 +++------
 bin/mkcatalog/mkcatalog.c        | 1353 ++++++++++++------------
 bin/mkcatalog/mkcatalog.h        |   25 +-
 bin/mkcatalog/ui.c               | 2086 ++++++++++++++------------------------
 bin/mkcatalog/ui.h               |   95 +-
 bin/mkcatalog/upperlimit.c       |  693 +++++--------
 bin/mkcatalog/upperlimit.h       |   12 +-
 bin/mknoise/args.h               |    8 +-
 bin/mknoise/main.c               |    2 +-
 bin/mknoise/ui.c                 |   28 +-
 bin/mknoise/ui.h                 |   10 +-
 bin/mkprof/args.h                |   74 +-
 bin/mkprof/main.h                |    1 -
 bin/mkprof/mkprof.c              |   24 +-
 bin/mkprof/ui.c                  |    6 +-
 bin/mkprof/ui.h                  |   74 +-
 bin/noisechisel/args.h           |   85 +-
 bin/noisechisel/clumps.c         |    7 +-
 bin/noisechisel/detection.c      |  262 +++--
 bin/noisechisel/main.h           |    2 +
 bin/noisechisel/noisechisel.c    |   35 +-
 bin/noisechisel/segmentation.c   |   11 +-
 bin/noisechisel/sky.c            |    6 +-
 bin/noisechisel/threshold.c      |   32 +-
 bin/noisechisel/ui.c             |    6 +-
 bin/noisechisel/ui.h             |   73 +-
 bin/statistics/args.h            |   80 +-
 bin/statistics/sky.c             |   18 +-
 bin/statistics/statistics.c      |  122 +--
 bin/statistics/ui.c              |   22 +-
 bin/statistics/ui.h              |   82 +-
 bin/table/args.h                 |    4 +-
 bin/table/ui.h                   |    4 +-
 bin/warp/args.h                  |   26 +-
 bin/warp/ui.c                    |   44 +-
 bin/warp/ui.h                    |   26 +-
 bin/warp/warp.c                  |    4 +-
 doc/gnuastro.texi                | 1591 ++++++++++++++++-------------
 lib/Makefile.am                  |   23 +-
 lib/arithmetic.c                 |    4 +-
 lib/binary.c                     |    4 +-
 lib/blank.c                      |   27 +-
 lib/convolve.c                   |    2 +-
 lib/data.c                       |    7 +-
 lib/fits.c                       |  187 ++--
 lib/gnuastro-internal/timing.h   |    2 +-
 lib/gnuastro/blank.h             |   20 +-
 lib/gnuastro/fits.h              |    8 +-
 lib/gnuastro/linkedlist.h        |    3 +
 lib/gnuastro/threads.h           |    4 +-
 lib/gnuastro/tile.h              |   15 +-
 lib/gnuastro/type.h              |    2 +-
 lib/gnuastro/wcs.h               |    4 +
 lib/interpolate.c                |    2 +-
 lib/linkedlist.c                 |   14 +
 lib/statistics.c                 |   26 +-
 lib/threads.c                    |    8 +-
 lib/tile.c                       |   92 +-
 lib/timing.c                     |    8 +-
 lib/txt.c                        |    2 +-
 lib/wcs.c                        |  127 ++-
 tests/during-dev.sh              |    6 +-
 tests/lib/multithread.c          |    4 +-
 tests/mkcatalog/aperturephot.sh  |   11 +-
 tests/noisechisel/noisechisel.sh |    3 +-
 tmpfs-config-make                |   26 +-
 95 files changed, 5944 insertions(+), 6002 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 65f78db..0533489 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -314,3 +314,15 @@ $(top_srcdir)/AUTHORS: $(top_srcdir)/.version
 ## distribution. Note that AUTHORS depends on `.version'.
 dist-hook: $(top_srcdir)/AUTHORS
        echo $(VERSION) > $(distdir)/.tarball-version
+
+
+
+
+## Nice joke
+## =========
+##
+## This joke is taken from the "The art of unix programming", and a quote
+## by Stuart Feldman (creator of Make): "One of the older Unix jokes is
+## "Make love" which results in "Don’t know how to make love"."
+love:
+       @echo "Don't know how to make love!"
diff --git a/bin/TEMPLATE/args.h b/bin/TEMPLATE/args.h
index f48d983..1b78e62 100644
--- a/bin/TEMPLATE/args.h
+++ b/bin/TEMPLATE/args.h
@@ -33,7 +33,7 @@ struct argp_option program_options[] =
   {
     {
       "multivalue",
-      ARGS_OPTION_KEY_MULTIVALUE,
+      UI_KEY_MULTIVALUE,
       "STR",
       0,
       "This option can take multiple values.",
@@ -51,7 +51,7 @@ struct argp_option program_options[] =
 
     {
       "onoff",
-      ARGS_OPTION_KEY_ONOFF,
+      UI_KEY_ONOFF,
       0,
       0,
       "This option takes no value,.",
diff --git a/bin/TEMPLATE/main.c b/bin/TEMPLATE/main.c
index fbc6577..aff41a4 100644
--- a/bin/TEMPLATE/main.c
+++ b/bin/TEMPLATE/main.c
@@ -25,7 +25,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <timing.h>             /* Includes time.h and sys/time.h */
+#include <gnuastro-internal/timing.h>
 
 #include "main.h"
 
diff --git a/bin/TEMPLATE/main.h b/bin/TEMPLATE/main.h
index cad699e..469e3b1 100644
--- a/bin/TEMPLATE/main.h
+++ b/bin/TEMPLATE/main.h
@@ -26,7 +26,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Include necessary headers */
 #include <gnuastro/data.h>
 
-#include <options.h>
+#include <gnuastro-internal/options.h>
 
 /* Progarm names.  */
 #define PROGRAM_NAME   "TEMPLATE"    /* Program full name.       */
diff --git a/bin/TEMPLATE/ui.c b/bin/TEMPLATE/ui.c
index dc95510..5d5101e 100644
--- a/bin/TEMPLATE/ui.c
+++ b/bin/TEMPLATE/ui.c
@@ -30,10 +30,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/fits.h>
 #include <gnuastro/linkedlist.h>
 
-#include <timing.h>
-#include <options.h>
-#include <checkset.h>
-#include <fixedstringmacros.h>
+#include <gnuastro-internal/timing.h>
+#include <gnuastro-internal/options.h>
+#include <gnuastro-internal/checkset.h>
+#include <gnuastro-internal/fixedstringmacros.h>
 
 #include "main.h"
 
@@ -294,7 +294,7 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
TEMPLATEparams *p)
      those headers which make them hard to read and modify. This also helps
      in having a clean environment: everything in those headers is only
      available within the scope of this function. */
-#include <commonopts.h>
+#include <gnuastro-internal/commonopts.h>
 #include "args.h"
 
 
diff --git a/bin/TEMPLATE/ui.h b/bin/TEMPLATE/ui.h
index e6dec00..ec2a41b 100644
--- a/bin/TEMPLATE/ui.h
+++ b/bin/TEMPLATE/ui.h
@@ -35,8 +35,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_MULTIVALUE      = 'm',
-  ARGS_OPTION_KEY_ONOFF           = 'O',
+  UI_KEY_MULTIVALUE      = 'm',
+  UI_KEY_ONOFF           = 'O',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
diff --git a/bin/arithmetic/arithmetic.c b/bin/arithmetic/arithmetic.c
index 44677fe..50ecb9c 100644
--- a/bin/arithmetic/arithmetic.c
+++ b/bin/arithmetic/arithmetic.c
@@ -260,6 +260,10 @@ reversepolish(struct imgarithparams *p)
             { op=GAL_ARITHMETIC_OP_TO_UINT16;     nop=1;  }
           else if (!strcmp(token->v, "int16"))
             { op=GAL_ARITHMETIC_OP_TO_INT16;      nop=1;  }
+          else if (!strcmp(token->v, "uint32"))
+            { op=GAL_ARITHMETIC_OP_TO_UINT32;     nop=1;  }
+          else if (!strcmp(token->v, "int32"))
+            { op=GAL_ARITHMETIC_OP_TO_INT32;      nop=1;  }
           else if (!strcmp(token->v, "uint64"))
             { op=GAL_ARITHMETIC_OP_TO_UINT64;     nop=1;  }
           else if (!strcmp(token->v, "int64"))
diff --git a/bin/convertt/args.h b/bin/convertt/args.h
index 16667fd..9bbcec4 100644
--- a/bin/convertt/args.h
+++ b/bin/convertt/args.h
@@ -33,7 +33,7 @@ struct argp_option program_options[] =
     /* Output */
     {
       "quality",
-      ARGS_OPTION_KEY_QUALITY,
+      UI_KEY_QUALITY,
       "INT",
       0,
       "Quality of output JPEG image (1 to 100).",
@@ -46,7 +46,7 @@ struct argp_option program_options[] =
     },
     {
       "widthincm",
-      ARGS_OPTION_KEY_WIDTHINCM,
+      UI_KEY_WIDTHINCM,
       "FLT",
       0,
       "Width in units of centimeters.",
@@ -59,7 +59,7 @@ struct argp_option program_options[] =
     },
     {
       "borderwidth",
-      ARGS_OPTION_KEY_BORDERWIDTH,
+      UI_KEY_BORDERWIDTH,
       "INT",
       0,
       "EPS/PDF border width in units of 1/72 inch.",
@@ -72,7 +72,7 @@ struct argp_option program_options[] =
     },
     {
       "hex",
-      ARGS_OPTION_KEY_HEX,
+      UI_KEY_HEX,
       0,
       0,
       "Hexadecimal encoding in EPS. Default: ASCII85.",
@@ -95,7 +95,7 @@ struct argp_option program_options[] =
     },
     {
       "fluxlow",
-      ARGS_OPTION_KEY_FLUXLOW,
+      UI_KEY_FLUXLOW,
       "FLT",
       0,
       "Lower flux truncation value.",
@@ -108,7 +108,7 @@ struct argp_option program_options[] =
     },
     {
       "fluxhigh",
-      ARGS_OPTION_KEY_FLUXHIGH,
+      UI_KEY_FLUXHIGH,
       "FLT",
       0,
       "Higher flux truncation value.",
@@ -121,7 +121,7 @@ struct argp_option program_options[] =
     },
     {
       "maxbyte",
-      ARGS_OPTION_KEY_MAXBYTE,
+      UI_KEY_MAXBYTE,
       "INT",
       0,
       "Maximum byte value for all color channels.",
@@ -134,7 +134,7 @@ struct argp_option program_options[] =
     },
     {
       "flminbyte",
-      ARGS_OPTION_KEY_FLMINBYTE,
+      UI_KEY_FLMINBYTE,
       0,
       0,
       "Set value of fluxlow as the minimum byte value.",
@@ -147,7 +147,7 @@ struct argp_option program_options[] =
     },
     {
       "fhmaxbyte",
-      ARGS_OPTION_KEY_FHMAXBYTE,
+      UI_KEY_FHMAXBYTE,
       0,
       0,
       "Set value of fluxhigh as the maximum byte value.",
@@ -160,7 +160,7 @@ struct argp_option program_options[] =
     },
     {
       "change",
-      ARGS_OPTION_KEY_CHANGE,
+      UI_KEY_CHANGE,
       "STR",
       0,
       "Change pixel values `from_1:to_1,from_2:to_2`.",
@@ -173,7 +173,7 @@ struct argp_option program_options[] =
     },
     {
       "changeaftertrunc",
-      ARGS_OPTION_KEY_CHANGEAFTERTRUNC,
+      UI_KEY_CHANGEAFTERTRUNC,
       0,
       0,
       "First truncate then change pixel values.",
@@ -186,7 +186,7 @@ struct argp_option program_options[] =
     },
     {
       "invert",
-      ARGS_OPTION_KEY_INVERT,
+      UI_KEY_INVERT,
       0,
       0,
       "Invert the values in JPEG and EPS/PDF.",
diff --git a/bin/convertt/ui.h b/bin/convertt/ui.h
index b28ccf0..bd60bae 100644
--- a/bin/convertt/ui.h
+++ b/bin/convertt/ui.h
@@ -35,18 +35,18 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_QUALITY             = 'u',
-  ARGS_OPTION_KEY_WIDTHINCM           = 'w',
-  ARGS_OPTION_KEY_BORDERWIDTH         = 'b',
-  ARGS_OPTION_KEY_HEX                 = 'x',
-  ARGS_OPTION_KEY_FLUXLOW             = 'L',
-  ARGS_OPTION_KEY_FLUXHIGH            = 'H',
-  ARGS_OPTION_KEY_MAXBYTE             = 'm',
-  ARGS_OPTION_KEY_FLMINBYTE           = 'A',
-  ARGS_OPTION_KEY_FHMAXBYTE           = 'B',
-  ARGS_OPTION_KEY_CHANGE              = 'c',
-  ARGS_OPTION_KEY_CHANGEAFTERTRUNC    = 'C',
-  ARGS_OPTION_KEY_INVERT              = 'i',
+  UI_KEY_QUALITY             = 'u',
+  UI_KEY_WIDTHINCM           = 'w',
+  UI_KEY_BORDERWIDTH         = 'b',
+  UI_KEY_HEX                 = 'x',
+  UI_KEY_FLUXLOW             = 'L',
+  UI_KEY_FLUXHIGH            = 'H',
+  UI_KEY_MAXBYTE             = 'm',
+  UI_KEY_FLMINBYTE           = 'A',
+  UI_KEY_FHMAXBYTE           = 'B',
+  UI_KEY_CHANGE              = 'c',
+  UI_KEY_CHANGEAFTERTRUNC    = 'C',
+  UI_KEY_INVERT              = 'i',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
diff --git a/bin/convolve/args.h b/bin/convolve/args.h
index 795bb89..c0445d4 100644
--- a/bin/convolve/args.h
+++ b/bin/convolve/args.h
@@ -34,7 +34,7 @@ struct argp_option program_options[] =
     /* Inputs */
     {
       "kernel",
-      ARGS_OPTION_KEY_KERNEL,
+      UI_KEY_KERNEL,
       "STR",
       0,
       "File name of kernel for convolution.",
@@ -47,7 +47,7 @@ struct argp_option program_options[] =
     },
     {
       "khdu",
-      ARGS_OPTION_KEY_KHDU,
+      UI_KEY_KHDU,
       "STR",
       0,
       "HDU containing the kernel.",
@@ -60,7 +60,7 @@ struct argp_option program_options[] =
     },
     {
       "nokernelflip",
-      ARGS_OPTION_KEY_NOKERNELFLIP,
+      UI_KEY_NOKERNELFLIP,
       0,
       0,
       "Do not flip the kernel image.",
@@ -73,7 +73,7 @@ struct argp_option program_options[] =
     },
     {
       "nokernelnorm",
-      ARGS_OPTION_KEY_NOKERNELNORM,
+      UI_KEY_NOKERNELNORM,
       0,
       0,
       "Do not normalize the kernel image.",
@@ -86,7 +86,7 @@ struct argp_option program_options[] =
     },
     {
       "minsharpspec",
-      ARGS_OPTION_KEY_MINSHARPSPEC,
+      UI_KEY_MINSHARPSPEC,
       "FLT",
       0,
       "Deconvolution: min spectrum of sharp img.",
@@ -105,7 +105,7 @@ struct argp_option program_options[] =
     /* Outputs */
     {
       "checkfreqsteps",
-      ARGS_OPTION_KEY_CHECKFREQSTEPS,
+      UI_KEY_CHECKFREQSTEPS,
       0,
       0,
       "View the steps in the frequency domain.",
@@ -118,7 +118,7 @@ struct argp_option program_options[] =
     },
     {
       "noedgecorrection",
-      ARGS_OPTION_KEY_NOEDGECORRECTION,
+      UI_KEY_NOEDGECORRECTION,
       0,
       0,
       "Do not correct the edges in the spatial domain",
@@ -137,7 +137,7 @@ struct argp_option program_options[] =
     /* Operating mode. */
     {
       "domain",
-      ARGS_OPTION_KEY_DOMAIN,
+      UI_KEY_DOMAIN,
       "STR",
       0,
       "Convolution domain: `spatial', `frequency'.",
@@ -150,7 +150,7 @@ struct argp_option program_options[] =
     },
     {
       "makekernel",
-      ARGS_OPTION_KEY_MAKEKERNEL,
+      UI_KEY_MAKEKERNEL,
       "INT",
       0,
       "Make 2*INT kernel to create input image.",
diff --git a/bin/convolve/convolve.c b/bin/convolve/convolve.c
index 4e993f4..0680097 100644
--- a/bin/convolve/convolve.c
+++ b/bin/convolve/convolve.c
@@ -481,7 +481,7 @@ onedimensionfft(void *inparam)
      by each other) is stored in p->pimg. So the check below works
      both in the forward and the backward transformation.
   */
-  for(i=0;indexs[i]!=GAL_THREADS_NON_THRD_INDEX;++i)
+  for(i=0; indexs[i]!=GAL_BLANK_SIZE_T; ++i)
     {
       data = ( indexs[i]<maxindex
                ? &pimg[ 2*indexs[i]*indmultip ]   /* *2 because complex. */
@@ -558,7 +558,7 @@ twodimensionfft(struct convolveparams *p, struct 
fftonthreadparams *fp,
 
       /* Spin off the threads: */
       for(i=0;i<nt;++i)
-        if(indexs[i*thrdcols]!=GAL_THREADS_NON_THRD_INDEX)
+        if(indexs[i*thrdcols]!=GAL_BLANK_SIZE_T)
           {
             fp[i].id=i;
             fp[i].b=&b;
@@ -598,7 +598,7 @@ twodimensionfft(struct convolveparams *p, struct 
fftonthreadparams *fp,
       else nb=nt+1;
       gal_threads_attr_barrier_init(&attr, &b, nb);
       for(i=0;i<nt;++i)
-        if(indexs[i*thrdcols]!=GAL_THREADS_NON_THRD_INDEX)
+        if(indexs[i*thrdcols]!=GAL_BLANK_SIZE_T)
           {
             fp[i].b=&b;
             fp[i].stride=p->ps1; /* On each column, stride is p->ps1 */
diff --git a/bin/convolve/ui.c b/bin/convolve/ui.c
index 582e33d..803f385 100644
--- a/bin/convolve/ui.c
+++ b/bin/convolve/ui.c
@@ -301,11 +301,13 @@ ui_read_kernel(struct convolveparams *p)
                                         p->cp.minmapsize);
 
   /* Convert all the NaN pixels to zero if the kernel contains blank
-     pixels. */
-  if(gal_blank_present(p->kernel))
+     pixels, also update the flags so it is not checked any more. */
+  if(gal_blank_present(p->kernel, 1))
     {
       ff = (f=p->kernel->array) + p->kernel->size;
       do *f = isnan(*f) ? 0.0f : *f; while(++f<ff);
+      p->kernel->flag |= GAL_DATA_FLAG_BLANK_CH;
+      p->kernel->flag &= ~GAL_DATA_FLAG_HASBLANK;
     }
 }
 
@@ -350,7 +352,7 @@ ui_preparations(struct convolveparams *p)
   /* See if there are any blank values. */
   if(p->domain==CONVOLVE_DOMAIN_FREQUENCY)
     {
-      if( gal_blank_present(p->input) )
+      if( gal_blank_present(p->input, 1) )
         fprintf(stderr, "\n----------------------------------------\n"
                 "######## %s WARNING ########\n"
                 "There are blank pixels in `%s' (hdu: `%s') and you have "
diff --git a/bin/convolve/ui.h b/bin/convolve/ui.h
index e557bd1..fc02db3 100644
--- a/bin/convolve/ui.h
+++ b/bin/convolve/ui.h
@@ -35,21 +35,21 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_KERNEL         = 'k',
-  ARGS_OPTION_KEY_KHDU           = 'u',
-  ARGS_OPTION_KEY_MINSHARPSPEC   = 'H',
-  ARGS_OPTION_KEY_CHECKFREQSTEPS = 'C',
-  ARGS_OPTION_KEY_TILESIZE       = 't',
-  ARGS_OPTION_KEY_NUMCHANNELS    = 'c',
-  ARGS_OPTION_KEY_REMAINDERFRAC  = 'r',
-  ARGS_OPTION_KEY_DOMAIN         = 'd',
-  ARGS_OPTION_KEY_MAKEKERNEL     = 'm',
+  UI_KEY_KERNEL         = 'k',
+  UI_KEY_KHDU           = 'u',
+  UI_KEY_MINSHARPSPEC   = 'H',
+  UI_KEY_CHECKFREQSTEPS = 'C',
+  UI_KEY_TILESIZE       = 't',
+  UI_KEY_NUMCHANNELS    = 'c',
+  UI_KEY_REMAINDERFRAC  = 'r',
+  UI_KEY_DOMAIN         = 'd',
+  UI_KEY_MAKEKERNEL     = 'm',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
-  ARGS_OPTION_KEY_NOKERNELFLIP = 1000,
-  ARGS_OPTION_KEY_NOKERNELNORM,
-  ARGS_OPTION_KEY_NOEDGECORRECTION,
+  UI_KEY_NOKERNELFLIP = 1000,
+  UI_KEY_NOKERNELNORM,
+  UI_KEY_NOEDGECORRECTION,
 };
 
 
diff --git a/bin/cosmiccal/args.h b/bin/cosmiccal/args.h
index c99579f..49529d1 100644
--- a/bin/cosmiccal/args.h
+++ b/bin/cosmiccal/args.h
@@ -33,7 +33,7 @@ struct argp_option program_options[] =
   {
     {
       "redshift",
-      ARGS_OPTION_KEY_REDSHIFT,
+      UI_KEY_REDSHIFT,
       "FLT",
       0,
       "Redshift of interest.",
@@ -46,7 +46,7 @@ struct argp_option program_options[] =
     },
     {
       "H0",
-      ARGS_OPTION_KEY_H0,
+      UI_KEY_H0,
       "FLT",
       0,
       "Current expansion rate (Hubble constant).",
@@ -59,7 +59,7 @@ struct argp_option program_options[] =
     },
     {
       "olambda",
-      ARGS_OPTION_KEY_OLAMBDA,
+      UI_KEY_OLAMBDA,
       "FLT",
       0,
       "Current cosmological cst. dens. per crit. dens.",
@@ -72,7 +72,7 @@ struct argp_option program_options[] =
     },
     {
       "omatter",
-      ARGS_OPTION_KEY_OMATTER,
+      UI_KEY_OMATTER,
       "FLT",
       0,
       "Current matter density per critical density.",
@@ -85,7 +85,7 @@ struct argp_option program_options[] =
     },
     {
       "oradiation",
-      ARGS_OPTION_KEY_ORADIATION,
+      UI_KEY_ORADIATION,
       "FLT",
       0,
       "Current radiation density per critical density.",
@@ -102,7 +102,7 @@ struct argp_option program_options[] =
 
     {
       "onlyvolumne",
-      ARGS_OPTION_KEY_ONLYVOLUME,
+      UI_KEY_ONLYVOLUME,
       0,
       0,
       "Only print comoving volume in Mpc^3.",
@@ -115,7 +115,7 @@ struct argp_option program_options[] =
     },
     {
       "onlyabsmagconv",
-      ARGS_OPTION_KEY_ONLYABSMAGCONV,
+      UI_KEY_ONLYABSMAGCONV,
       0,
       0,
       "Only print conversion to absolute magnitude.",
diff --git a/bin/cosmiccal/ui.h b/bin/cosmiccal/ui.h
index 7c4fdab..0090fd8 100644
--- a/bin/cosmiccal/ui.h
+++ b/bin/cosmiccal/ui.h
@@ -35,13 +35,13 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_REDSHIFT       = 'z',
-  ARGS_OPTION_KEY_H0             = 'H',
-  ARGS_OPTION_KEY_OLAMBDA        = 'l',
-  ARGS_OPTION_KEY_OMATTER        = 'm',
-  ARGS_OPTION_KEY_ORADIATION     = 'r',
-  ARGS_OPTION_KEY_ONLYVOLUME     = 'v',
-  ARGS_OPTION_KEY_ONLYABSMAGCONV = 'a',
+  UI_KEY_REDSHIFT       = 'z',
+  UI_KEY_H0             = 'H',
+  UI_KEY_OLAMBDA        = 'l',
+  UI_KEY_OMATTER        = 'm',
+  UI_KEY_ORADIATION     = 'r',
+  UI_KEY_ONLYVOLUME     = 'v',
+  UI_KEY_ONLYABSMAGCONV = 'a',
 
 
   /* Only with long version (start with a value 1000, the rest will be set
diff --git a/bin/crop/args.h b/bin/crop/args.h
index 24bda0e..f973bd4 100644
--- a/bin/crop/args.h
+++ b/bin/crop/args.h
@@ -33,7 +33,7 @@ struct argp_option program_options[] =
     /* Input. */
     {
       "hstartwcs",
-      ARGS_OPTION_KEY_HSTARTWCS,
+      UI_KEY_HSTARTWCS,
       "INT",
       0,
       "Header keyword number to start reading WCS.",
@@ -46,7 +46,7 @@ struct argp_option program_options[] =
     },
     {
       "hendwcs",
-      ARGS_OPTION_KEY_HENDWCS,
+      UI_KEY_HENDWCS,
       "INT",
       0,
       "Header keyword number to stop reading WCS.",
@@ -59,7 +59,7 @@ struct argp_option program_options[] =
     },
     {
       "zeroisnotblank",
-      ARGS_OPTION_KEY_ZEROISNOTBLANK,
+      UI_KEY_ZEROISNOTBLANK,
       0,
       0,
       "0.0 in float or double images are not blank.",
@@ -77,7 +77,7 @@ struct argp_option program_options[] =
     /* Output. */
     {
       "noblank",
-      ARGS_OPTION_KEY_NOBLANK,
+      UI_KEY_NOBLANK,
       0,
       0,
       "Remove parts of the crop box out of input image.",
@@ -90,7 +90,7 @@ struct argp_option program_options[] =
     },
     {
       "suffix",
-      ARGS_OPTION_KEY_SUFFIX,
+      UI_KEY_SUFFIX,
       "STR",
       0,
       "Suffix (postfix) of cropped images.",
@@ -113,7 +113,7 @@ struct argp_option program_options[] =
     },
     {
       "checkcenter",
-      ARGS_OPTION_KEY_CHECKCENTER,
+      UI_KEY_CHECKCENTER,
       "INT",
       0,
       "Width (in pixels) of box at center to check.",
@@ -126,7 +126,7 @@ struct argp_option program_options[] =
     },
     {
       "iwidth",
-      ARGS_OPTION_KEY_IWIDTH,
+      UI_KEY_IWIDTH,
       "INT",
       0,
       "Width (pixels) when crop defined by X,Y.",
@@ -139,7 +139,7 @@ struct argp_option program_options[] =
     },
     {
       "wwidth",
-      ARGS_OPTION_KEY_WWIDTH,
+      UI_KEY_WWIDTH,
       "FLT",
       0,
       "Width (arcseconds) for crops defined by RA,Dec.",
@@ -162,7 +162,7 @@ struct argp_option program_options[] =
     },
     {
       "ra",
-      ARGS_OPTION_KEY_RA,
+      UI_KEY_RA,
       "FLT",
       0,
       "Right ascension of one crop box center.",
@@ -175,7 +175,7 @@ struct argp_option program_options[] =
     },
     {
       "dec",
-      ARGS_OPTION_KEY_DEC,
+      UI_KEY_DEC,
       "FLT",
       0,
       "Declination of one crop box center.",
@@ -188,7 +188,7 @@ struct argp_option program_options[] =
     },
     {
       "xc",
-      ARGS_OPTION_KEY_XC,
+      UI_KEY_XC,
       "FLT",
       0,
       "First axis position of one crop box center.",
@@ -201,7 +201,7 @@ struct argp_option program_options[] =
     },
     {
       "yc",
-      ARGS_OPTION_KEY_YC,
+      UI_KEY_YC,
       "FLT",
       0,
       "Second axis position of one crop box center.",
@@ -226,7 +226,7 @@ struct argp_option program_options[] =
     },
     {
       "catalog",
-      ARGS_OPTION_KEY_CATALOG,
+      UI_KEY_CATALOG,
       "STR",
       0,
       "Input catalog filename.",
@@ -239,7 +239,7 @@ struct argp_option program_options[] =
     },
     {
       "cathdu",
-      ARGS_OPTION_KEY_CATHDU,
+      UI_KEY_CATHDU,
       "STR/INT",
       0,
       "HDU of catalog, if it is a FITS table.",
@@ -252,7 +252,7 @@ struct argp_option program_options[] =
     },
     {
       "namecol",
-      ARGS_OPTION_KEY_NAMECOL,
+      UI_KEY_NAMECOL,
       "STR/INT",
       0,
       "Column no./info of crop filename (no suffix).",
@@ -265,7 +265,7 @@ struct argp_option program_options[] =
     },
     {
       "racol",
-      ARGS_OPTION_KEY_RACOL,
+      UI_KEY_RACOL,
       "STR/INT",
       0,
       "Column number/info of Right Ascension (RA).",
@@ -278,7 +278,7 @@ struct argp_option program_options[] =
     },
     {
       "deccol",
-      ARGS_OPTION_KEY_DECCOL,
+      UI_KEY_DECCOL,
       "STR/INT",
       0,
       "Column number/info of Declination.",
@@ -291,7 +291,7 @@ struct argp_option program_options[] =
     },
     {
       "xcol",
-      ARGS_OPTION_KEY_XCOL,
+      UI_KEY_XCOL,
       "STR/INT",
       0,
       "Column number/info of X (first FITS axis).",
@@ -304,7 +304,7 @@ struct argp_option program_options[] =
     },
     {
       "ycol",
-      ARGS_OPTION_KEY_YCOL,
+      UI_KEY_YCOL,
       "STR/INT",
       0,
       "Column number/info of Y (second FITS axis).",
@@ -327,7 +327,7 @@ struct argp_option program_options[] =
     },
     {
       "section",
-      ARGS_OPTION_KEY_SECTION,
+      UI_KEY_SECTION,
       "STR",
       0,
       "Image section string specifying crop range.",
@@ -340,7 +340,7 @@ struct argp_option program_options[] =
     },
     {
       "polygon",
-      ARGS_OPTION_KEY_POLYGON,
+      UI_KEY_POLYGON,
       "STR",
       0,
       "Polygon vertices of region to crop, keep inside.",
@@ -353,7 +353,7 @@ struct argp_option program_options[] =
     },
     {
       "outpolygon",
-      ARGS_OPTION_KEY_OUTPOLYGON,
+      UI_KEY_OUTPOLYGON,
       0,
       0,
       "Keep the polygon's outside, mask the inside.",
@@ -373,7 +373,7 @@ struct argp_option program_options[] =
     /* Operating mode */
     {
       "mode",
-      ARGS_OPTION_KEY_MODE,
+      UI_KEY_MODE,
       "STR",
       0,
       "Coordinate mode `img' or `wcs'",
diff --git a/bin/crop/crop.c b/bin/crop/crop.c
index 433c119..9a86588 100644
--- a/bin/crop/crop.c
+++ b/bin/crop/crop.c
@@ -204,7 +204,7 @@ imgmodecrop(void *inparam)
   crp->infits=gal_fits_hdu_open_type(img->name, p->cp.hdu, 0);
 
   /* Go over all the outputs that are assigned to this thread: */
-  for(i=0;crp->indexs[i]!=GAL_THREADS_NON_THRD_INDEX;++i)
+  for(i=0; crp->indexs[i]!=GAL_BLANK_SIZE_T; ++i)
     {
       /* Set all the output parameters: */
       crp->out_ind=crp->indexs[i];
@@ -272,7 +272,7 @@ wcsmodecrop(void *inparam)
 
 
   /* Go over all the output objects for this thread. */
-  for(i=0;crp->indexs[i]!=GAL_THREADS_NON_THRD_INDEX;++i)
+  for(i=0; crp->indexs[i]!=GAL_BLANK_SIZE_T; ++i)
     {
       /* Set all the output parameters: */
       crp->out_ind=crp->indexs[i];
@@ -430,7 +430,7 @@ crop(struct cropparams *p)
 
       /* Spin off the threads: */
       for(i=0;i<nt;++i)
-        if(indexs[i*thrdcols]!=GAL_THREADS_NON_THRD_INDEX)
+        if(indexs[i*thrdcols]!=GAL_BLANK_SIZE_T)
           {
             crp[i].p=p;
             crp[i].b=&b;
diff --git a/bin/crop/ui.c b/bin/crop/ui.c
index 85c17c6..5b80d0a 100644
--- a/bin/crop/ui.c
+++ b/bin/crop/ui.c
@@ -562,7 +562,7 @@ ui_read_cols(struct cropparams *p)
       if(corrtype)
         {
           /* Make sure there are no blank values in this column. */
-          if( gal_blank_present(corrtype) )
+          if( gal_blank_present(corrtype, 1) )
             error(EXIT_FAILURE, 0, "%s column has blank values. "
                   "Input columns cannot contain blank values", colname);
 
diff --git a/bin/crop/ui.h b/bin/crop/ui.h
index d42feac..fdb9a70 100644
--- a/bin/crop/ui.h
+++ b/bin/crop/ui.h
@@ -35,32 +35,32 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_CATALOG        = 'C',
-  ARGS_OPTION_KEY_NOBLANK        = 'b',
-  ARGS_OPTION_KEY_CHECKCENTER    = 'c',
-  ARGS_OPTION_KEY_SUFFIX         = 'p',
-  ARGS_OPTION_KEY_NAMECOL        = 'n',
-  ARGS_OPTION_KEY_RACOL          = 'f',
-  ARGS_OPTION_KEY_DECCOL         = 'g',
-  ARGS_OPTION_KEY_RA             = 'r',
-  ARGS_OPTION_KEY_DEC            = 'd',
-  ARGS_OPTION_KEY_XCOL           = 'i',
-  ARGS_OPTION_KEY_YCOL           = 'j',
-  ARGS_OPTION_KEY_XC             = 'x',
-  ARGS_OPTION_KEY_YC             = 'y',
-  ARGS_OPTION_KEY_IWIDTH         = 'a',
-  ARGS_OPTION_KEY_WWIDTH         = 'w',
-  ARGS_OPTION_KEY_SECTION        = 's',
-  ARGS_OPTION_KEY_POLYGON        = 'l',
-  ARGS_OPTION_KEY_ZEROISNOTBLANK = 'z',
-  ARGS_OPTION_KEY_MODE           = 'O',
+  UI_KEY_CATALOG        = 'C',
+  UI_KEY_NOBLANK        = 'b',
+  UI_KEY_CHECKCENTER    = 'c',
+  UI_KEY_SUFFIX         = 'p',
+  UI_KEY_NAMECOL        = 'n',
+  UI_KEY_RACOL          = 'f',
+  UI_KEY_DECCOL         = 'g',
+  UI_KEY_RA             = 'r',
+  UI_KEY_DEC            = 'd',
+  UI_KEY_XCOL           = 'i',
+  UI_KEY_YCOL           = 'j',
+  UI_KEY_XC             = 'x',
+  UI_KEY_YC             = 'y',
+  UI_KEY_IWIDTH         = 'a',
+  UI_KEY_WWIDTH         = 'w',
+  UI_KEY_SECTION        = 's',
+  UI_KEY_POLYGON        = 'l',
+  UI_KEY_ZEROISNOTBLANK = 'z',
+  UI_KEY_MODE           = 'O',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
-  ARGS_OPTION_KEY_CATHDU         = 1000,
-  ARGS_OPTION_KEY_HSTARTWCS,
-  ARGS_OPTION_KEY_HENDWCS,
-  ARGS_OPTION_KEY_OUTPOLYGON,
+  UI_KEY_CATHDU         = 1000,
+  UI_KEY_HSTARTWCS,
+  UI_KEY_HENDWCS,
+  UI_KEY_OUTPOLYGON,
 };
 
 
diff --git a/bin/fits/Makefile.am b/bin/fits/Makefile.am
index aab8b7f..c2229af 100644
--- a/bin/fits/Makefile.am
+++ b/bin/fits/Makefile.am
@@ -32,7 +32,7 @@ astfits_LDADD = -lgnuastro
 
 astfits_SOURCES = main.c ui.c extension.c fits.c keywords.c
 
-EXTRA_DIST = main.h authors-cite.h args.h ui.h extension.c fits.c keywords.h
+EXTRA_DIST = main.h authors-cite.h args.h ui.h extension.c fits.h keywords.h
 
 
 
diff --git a/bin/fits/args.h b/bin/fits/args.h
index 2a57970..8c71928 100644
--- a/bin/fits/args.h
+++ b/bin/fits/args.h
@@ -39,7 +39,7 @@ struct argp_option program_options[] =
     },
     {
       "remove",
-      ARGS_OPTION_KEY_REMOVE,
+      UI_KEY_REMOVE,
       "STR",
       0,
       "Remove extension from input file.",
@@ -52,7 +52,7 @@ struct argp_option program_options[] =
     },
     {
       "copy",
-      ARGS_OPTION_KEY_COPY,
+      UI_KEY_COPY,
       "STR",
       0,
       "Copy extension to output file.",
@@ -65,7 +65,7 @@ struct argp_option program_options[] =
     },
     {
       "cut",
-      ARGS_OPTION_KEY_CUT,
+      UI_KEY_CUT,
       "STR",
       0,
       "Copy extension to output and remove from input.",
@@ -86,7 +86,7 @@ struct argp_option program_options[] =
     },
     {
       "asis",
-      ARGS_OPTION_KEY_ASIS,
+      UI_KEY_ASIS,
       "STR",
       0,
       "Write the argument string as is into the header.",
@@ -99,7 +99,7 @@ struct argp_option program_options[] =
     },
     {
       "delete",
-      ARGS_OPTION_KEY_DELETE,
+      UI_KEY_DELETE,
       "STR",
       0,
       "Delete a keyword from the header.",
@@ -112,7 +112,7 @@ struct argp_option program_options[] =
     },
     {
       "rename",
-      ARGS_OPTION_KEY_RENAME,
+      UI_KEY_RENAME,
       "STR",
       0,
       "Rename keyword, keeping value and comments.",
@@ -125,7 +125,7 @@ struct argp_option program_options[] =
     },
     {
       "update",
-      ARGS_OPTION_KEY_UPDATE,
+      UI_KEY_UPDATE,
       "STR",
       0,
       "Update a keyword value or comments.",
@@ -138,7 +138,7 @@ struct argp_option program_options[] =
     },
     {
       "write",
-      ARGS_OPTION_KEY_WRITE,
+      UI_KEY_WRITE,
       "STR",
       0,
       "Write a keyword (with value, comments and units).",
@@ -151,7 +151,7 @@ struct argp_option program_options[] =
     },
     {
       "history",
-      ARGS_OPTION_KEY_HISTORY,
+      UI_KEY_HISTORY,
       "STR",
       0,
       "Add HISTORY keyword, any length is ok.",
@@ -164,7 +164,7 @@ struct argp_option program_options[] =
     },
     {
       "comment",
-      ARGS_OPTION_KEY_COMMENT,
+      UI_KEY_COMMENT,
       "STR",
       0,
       "Add COMMENT keyword, any length is ok.",
@@ -177,7 +177,7 @@ struct argp_option program_options[] =
     },
     {
       "date",
-      ARGS_OPTION_KEY_DATE,
+      UI_KEY_DATE,
       0,
       0,
       "Set the DATE keyword to the current time.",
@@ -190,7 +190,7 @@ struct argp_option program_options[] =
     },
     {
       "printallkeys",
-      ARGS_OPTION_KEY_PRINTALLKEYS,
+      UI_KEY_PRINTALLKEYS,
       0,
       0,
       "Print all keywords in the selected HDU.",
@@ -206,7 +206,7 @@ struct argp_option program_options[] =
 
     {
       "quitonerror",
-      ARGS_OPTION_KEY_QUITONERROR,
+      UI_KEY_QUITONERROR,
       0,
       0,
       "Quit if there is an error on any action.",
diff --git a/bin/fits/fits.c b/bin/fits/fits.c
index 773c467..e612e0e 100644
--- a/bin/fits/fits.c
+++ b/bin/fits/fits.c
@@ -87,7 +87,7 @@ fits_print_extension_info(struct fitsparams *p)
   char **tstra, **estra, **sstra;
   size_t i, numext, *dsize, ndim;
   int j, nc, numhdu, hdutype, status=0, type;
-  char *msg, *tstr=NULL, *estr=NULL, sstr[1000], extname[FLEN_VALUE];
+  char *msg, *tstr=NULL, sstr[1000], extname[FLEN_VALUE];
 
 
   /* Open the FITS file and read the first extension type, upon moving to
@@ -160,12 +160,11 @@ fits_print_extension_info(struct fitsparams *p)
       switch(status)
         {
         case 0:
-          estr=gal_fits_key_clean_str_value(extname);
+          gal_fits_key_clean_str_value(extname);
           break;
 
         case KEY_NO_EXIST:
           sprintf(extname, "%s", GAL_BLANK_STRING);
-          estr=extname;
           status=0;
           break;
 
@@ -201,10 +200,10 @@ fits_print_extension_info(struct fitsparams *p)
         {
           switch(j)
             {
-            case 0: ui16[i]=i;                                   break;
-            case 1: gal_checkset_allocate_copy(estr, estra+i);   break;
-            case 2: gal_checkset_allocate_copy(tstr, tstra+i);   break;
-            case 3: gal_checkset_allocate_copy(sstr, sstra+i);   break;
+            case 0: ui16[i]=i;                                    break;
+            case 1: gal_checkset_allocate_copy(extname, estra+i); break;
+            case 2: gal_checkset_allocate_copy(tstr, tstra+i);    break;
+            case 3: gal_checkset_allocate_copy(sstr, sstra+i);    break;
             }
           ++j;
         }
@@ -225,7 +224,8 @@ fits_print_extension_info(struct fitsparams *p)
       printf("%s\nRun on %s-----\n", PROGRAM_STRING, ctime(&p->rawtime));
       printf("HDU (extension) information: `%s'.\n", p->filename);
       printf(" Column 1: Index (counting from 0, usable with `--hdu').\n");
-      printf(" Column 2: Name (`EXTNAME' in FITS standard, usable with 
`--hdu').\n");
+      printf(" Column 2: Name (`EXTNAME' in FITS standard, usable with "
+             "`--hdu').\n");
       printf(" Column 3: Image data type or `table' format (ASCII or "
              "binary).\n");
       printf(" Column 4: Size of data in HDU.\n");
diff --git a/bin/fits/ui.h b/bin/fits/ui.h
index 8ce6491..f31ce05 100644
--- a/bin/fits/ui.h
+++ b/bin/fits/ui.h
@@ -35,19 +35,19 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_REMOVE       = 'R',
-  ARGS_OPTION_KEY_COPY         = 'C',
-  ARGS_OPTION_KEY_CUT          = 'k',
-  ARGS_OPTION_KEY_PRINTALLKEYS = 'p',
-  ARGS_OPTION_KEY_ASIS         = 'a',
-  ARGS_OPTION_KEY_DELETE       = 'd',
-  ARGS_OPTION_KEY_RENAME       = 'r',
-  ARGS_OPTION_KEY_UPDATE       = 'u',
-  ARGS_OPTION_KEY_WRITE        = 'w',
-  ARGS_OPTION_KEY_COMMENT      = 'c',
-  ARGS_OPTION_KEY_HISTORY      = 'H',
-  ARGS_OPTION_KEY_DATE         = 't',
-  ARGS_OPTION_KEY_QUITONERROR  = 'Q',
+  UI_KEY_REMOVE       = 'R',
+  UI_KEY_COPY         = 'C',
+  UI_KEY_CUT          = 'k',
+  UI_KEY_PRINTALLKEYS = 'p',
+  UI_KEY_ASIS         = 'a',
+  UI_KEY_DELETE       = 'd',
+  UI_KEY_RENAME       = 'r',
+  UI_KEY_UPDATE       = 'u',
+  UI_KEY_WRITE        = 'w',
+  UI_KEY_COMMENT      = 'c',
+  UI_KEY_HISTORY      = 'H',
+  UI_KEY_DATE         = 't',
+  UI_KEY_QUITONERROR  = 'Q',
 
 
   /* Only with long version (start with a value 1000, the rest will be set
diff --git a/bin/mkcatalog/Makefile.am b/bin/mkcatalog/Makefile.am
index 595f262..7e590bf 100644
--- a/bin/mkcatalog/Makefile.am
+++ b/bin/mkcatalog/Makefile.am
@@ -32,7 +32,7 @@ astmkcatalog_LDADD = -lgnuastro
 
 astmkcatalog_SOURCES = main.c ui.c mkcatalog.c columns.c upperlimit.c
 
-EXTRA_DIST = main.h authors-cite.h args.h ui.h mkcatalog.h columns.h    \
+EXTRA_DIST = main.h authors-cite.h args.h ui.h mkcatalog.h columns.h   \
   upperlimit.h
 
 
diff --git a/bin/mkcatalog/args.h b/bin/mkcatalog/args.h
index beebde0..55cc576 100644
--- a/bin/mkcatalog/args.h
+++ b/bin/mkcatalog/args.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <address@hidden>
 Contributing author(s):
-Copyright (C) 2015, Free Software Foundation, Inc.
+Copyright (C) 2016, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -23,637 +23,893 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #ifndef ARGS_H
 #define ARGS_H
 
-#include <argp.h>
 
-#include <gnuastro/linkedlist.h>
 
-#include <commonargs.h>
-#include <fixedstringmacros.h>
 
 
 
-
-
-
-
-
-
-
-/**************************************************************/
-/**************        argp.h definitions       ***************/
-/**************************************************************/
-
-
-
-
-/* Definition parameters for the argp: */
-const char *argp_program_version=SPACK_STRING"\n"GAL_STRINGS_COPYRIGHT
-  "\n\nWritten by Mohammad Akhlaghi";
-const char *argp_program_bug_address=PACKAGE_BUGREPORT;
-static char args_doc[] = "ASTRdata";
-
-
-
-
-
-const char doc[] =
-  /* Before the list of options: */
-  GAL_STRINGS_TOP_HELP_INFO
-  SPACK_NAME" will create a catalog from an input, labeled, and noise "
-  "identification images.\n"
-  GAL_STRINGS_MORE_HELP_INFO
-  /* After the list of options: */
-  "\v"
-  PACKAGE_NAME" home page: "PACKAGE_URL;
-
-
-
-
-
-/* Available letters for short options:
-
-   f g k l u v w
-   F G J L Q R U W X Y Z
-
-   Number keys used: <= 541
-
-   Options with keys (second structure element) larger than 500 do not
-   have a short version.
- */
-static struct argp_option options[] =
+/* Array of acceptable options. */
+struct argp_option program_options[] =
   {
+    /* Input options. */
     {
-      0, 0, 0, 0,
-      "Input:",
-      1
-    },
-    {
-      "mask",
-      'M',
+      "objectsfile",
+      UI_KEY_OBJECTSFILE,
       "STR",
       0,
-      "Mask image file name.",
-      1
+      "Image containing object/detection labels.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->objectsfile,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
-      "mhdu",
-      'H',
+      "objectshdu",
+      UI_KEY_OBJECTSHDU,
       "STR",
       0,
-      "Mask image header name or number.",
-      1
+      "Object image extension name or number.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->objectshdu,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
-      "objlabs",
-      'O',
+      "clumpsfile",
+      UI_KEY_CLUMPSFILE,
       "STR",
       0,
-      "Image specifying object labels.",
-      1
+      "Image containing clump labels.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->clumpsfile,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
-      "objhdu",
-      501,
+      "clumpshdu",
+      UI_KEY_CLUMPSHDU,
       "STR",
       0,
-      "Object image header name or number.",
-      1
+      "Clump image extension name or number.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->clumpshdu,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
-      "clumplabs",
-      'c',
+      "skyfile",
+      UI_KEY_SKYFILE,
       "STR",
       0,
-      "Image specifying clump labels.",
-      1
-    },
-    {
-      "clumphdu",
-      502,
-      "STR",
-      0,
-      "Clumps image header name or number.",
-      1
-    },
-    {
-      "skyfilename",
-      's',
-      "STR",
-      0,
-      "Sky value image.",
-      1
+      "Image containing sky values.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->skyfile,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
       "skyhdu",
-      503,
+      UI_KEY_SKYHDU,
       "STR",
       0,
-      "Sky image header name or number.",
-      1
+      "Sky image extension name or number.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->skyhdu,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
-      "stdfilename",
-      't',
+      "stdfile",
+      UI_KEY_STDFILE,
       "STR",
       0,
-      "Sky standard deviation image.",
-      1
+      "Image containing sky STD values.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->stdfile,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
       "stdhdu",
-      504,
+      UI_KEY_SKYHDU,
       "STR",
       0,
-      "Sky STD image header name or number.",
-      1
+      "Sky image extension name or number.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->stdhdu,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
       "zeropoint",
-      'z',
+      UI_KEY_ZEROPOINT,
       "FLT",
       0,
-      "Image zeropoint magnitude.",
-      1
+      "Zeropoint magnitude of input dataset.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->zeropoint,
+      GAL_TYPE_FLOAT32,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
       "skysubtracted",
-      'E',
+      UI_KEY_SKYSUBTRACTED,
       0,
       0,
       "Input is already sky subtracted (for S/N).",
-      1
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->skysubtracted,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
       "threshold",
-      'T',
+      UI_KEY_THRESHOLD,
       "FLT",
       0,
-      "Only values larger than this multiple of STD.",
-      1
+      "Use pixels more than this multiple of STD.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->threshold,
+      GAL_TYPE_FLOAT32,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
 
 
 
-
-    {
-      0, 0, 0, 0,
-      "Output:",
-      2
-    },
+    /* Output. */
     {
       "nsigmag",
-      521,
+      UI_KEY_NSIGMAG,
       "FLT",
       0,
       "Multiple of Sky STD to report magnitude of.",
-      2
-    },
-    {
-      "intwidth",
-      516,
-      "INT",
-      0,
-      "Width of integer columns.",
-      2
-    },
-    {
-      "floatwidth",
-      517,
-      "INT",
-      0,
-      "Width of floating point columns.",
-      2
-    },
-    {
-      "accuwidth",
-      518,
-      "INT",
-      0,
-      "Width of more accurate floating point columns.",
-      2
-    },
-    {
-      "floatprecision",
-      519,
-      "INT",
-      0,
-      "Precision of floating point columns.",
-      2
-    },
-    {
-      "accuprecision",
-      520,
-      "INT",
-      0,
-      "Precision of more accurate floating pnt. cols.",
-      2
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->nsigmag,
+      GAL_TYPE_FLOAT32,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
 
 
 
+    /* Upper limit magnitude configurations. */
     {
       0, 0, 0, 0,
       "Upper limit magnitude:",
-      3
+      ARGS_GROUP_UPPERLIMIT
     },
     {
-      "upmask",
-      535,
+      "upmaskfile",
+      UI_KEY_UPMASKFILE,
       "STR",
       0,
       "Mask image file name only for upper limit.",
-      3
+      ARGS_GROUP_UPPERLIMIT,
+      &p->upmaskfile,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
       "upmaskhdu",
-      536,
+      UI_KEY_UPMASKHDU,
       "STR",
       0,
-      "Upper limit mask image header name or number.",
-      3
+      "Mask image HDU only for upper limit.",
+      ARGS_GROUP_UPPERLIMIT,
+      &p->upmaskhdu,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
       "upnum",
-      537,
+      UI_KEY_UPNUM,
       "INT",
       0,
-      "Number of randomly positioned samples.",
-      3
+      "Number of randomly positioned samples",
+      ARGS_GROUP_UPPERLIMIT,
+      &p->upnum,
+      GAL_TYPE_SIZE_T,
+      GAL_OPTIONS_RANGE_GT_0,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
       "envseed",
-      540,
+      UI_KEY_ENVSEED,
       0,
       0,
-      "Random number generator info from environment.",
-      3
+      "Use GSL_RNG_SEED environment variable for seed.",
+      ARGS_GROUP_UPPERLIMIT,
+      &p->envseed,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
     {
-      "upsclipmultip",
-      538,
-      "FLT",
+      "upsigmaclip",
+      UI_KEY_UPSIGMACLIP,
+      "FLT,FLT",
       0,
-      "Sigma clipping multiple of std.",
-      3
-    },
-    {
-      "upsclipaccu",
-      539,
-      "FLT",
-      0,
-      "Acceptable relative diff to stop clipping.",
-      3
+      "Sigma multiple and, tolerance or number.",
+      ARGS_GROUP_UPPERLIMIT,
+      &p->upsigmaclip,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_read_sigma_clip
     },
     {
       "upnsigma",
-      541,
+      UI_KEY_UPNSIGMA,
       "FLT",
       0,
-      "Multiple of final sigma to use.",
-      3
+      "Multiple of sigma to define upperlimit.",
+      ARGS_GROUP_UPPERLIMIT,
+      &p->upnsigma,
+      GAL_TYPE_FLOAT32,
+      GAL_OPTIONS_RANGE_GT_0,
+      GAL_OPTIONS_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
 
 
 
-
-
+    /* Output columns: IMPORTANT: Throughout MakeCatalogs, the order of
+       columns is based on this. So if you make changes/additions, please
+       update all the other places the columns are searched. */
     {
       0, 0, 0, 0,
-      "Catalog columns:",
-      4
+      "Output catalog columns",
+      ARGS_GROUP_COLUMNS
+    },
+    {  /* `ids' is not a unique column, it is a combination of several
+          columns. */
+      "ids",
+      UI_KEY_IDS,
+      0,
+      0,
+      "All IDs of objects and clumps.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
-      "id",
-      'i',
+      "objid",
+      UI_KEY_OBJID,
+      0,
       0,
+      "Object label/ID.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Overall ID of this object or clump.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "hostobjid",
-      'j',
+      UI_KEY_HOSTOBJID,
       0,
       0,
       "ID of object hosting this clump.",
-      4
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "idinhostobj",
-      'I',
+      UI_KEY_IDINHOSTOBJ,
       0,
       0,
       "ID of clump in host object.",
-      4
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "numclumps",
-      'C',
+      UI_KEY_NUMCLUMPS,
       0,
       0,
       "Number of clumps in this object.",
-      4
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "area",
-      'a',
+      UI_KEY_AREA,
       0,
       0,
-      "Number of pixels.",
-      4
+      "Number of pixels in clump or object.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsarea",
-      513,
+      UI_KEY_CLUMPSAREA,
+      0,
       0,
+      "Sum of all clump areas in an object.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Area of clumps in an object.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "x",
-      'x',
+      UI_KEY_X,
       0,
       0,
-      "All obj. flux weighted center (first FITS axis).",
-      4
+      "Flux weighted center in first FITS axis.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "y",
-      'y',
+      UI_KEY_Y,
+      0,
       0,
+      "Flux weighted center in second FITS axis.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "All obj. flux weighted center (second FITS axis).",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "geox",
-      522,
+      UI_KEY_GEOX,
+      0,
       0,
+      "Geometric center in first FITS axis.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "All obj. geometric center (first FITS axis).",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "geoy",
-      523,
+      UI_KEY_GEOY,
       0,
       0,
-      "All obj. geometric center (second FITS axis).",
-      4
+      "Geometric center in second FITS axis.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsx",
-      507,
+      UI_KEY_CLUMPSX,
+      0,
       0,
+      "Flux.wht center of all clumps in obj. (X).",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Clumps flux weighted center (first FITS axis).",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsy",
-      508,
+      UI_KEY_CLUMPSY,
       0,
       0,
-      "Clumps flux weighted center (second FITS axis).",
-      4
+      "Flux.wht center of all clumps in obj. (Y).",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsgeox",
-      524,
+      UI_KEY_CLUMPSGEOX,
+      0,
       0,
+      "Geometric center of all clumps in obj. (X).",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Clumps geometric center (first FITS axis).",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsgeoy",
-      525,
+      UI_KEY_CLUMPSGEOY,
+      0,
       0,
+      "Geometric center of all clumps in obj. (Y).",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Clumps geometric center (second FITS axis).",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "ra",
-      'r',
+      UI_KEY_RA,
       0,
       0,
-      "All object flux weighted center right ascension.",
-      4
+      "Right ascension of flux weighted center.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "dec",
-      'd',
+      UI_KEY_DEC,
+      0,
       0,
+      "Declination of flux weighted center.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "All object flux weighted center declination.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "geora",
-      526,
+      UI_KEY_GEORA,
       0,
       0,
-      "All object geometric center right ascension.",
-      4
+      "Right ascension of geometric center.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "geodec",
-      527,
+      UI_KEY_GEODEC,
+      0,
       0,
+      "Declination of geometric center.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "All object geometric center declination.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsra",
-      509,
+      UI_KEY_CLUMPSRA,
+      0,
       0,
+      "Right ascension of f.wht center of all clumps.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Clumps flux weighted center right ascension.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsdec",
-      510,
+      UI_KEY_CLUMPSDEC,
       0,
       0,
-      "Clumps flux weighted center declination.",
-      4
+      "Declination of f.wht center of all clumps.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsgeora",
-      528,
+      UI_KEY_CLUMPSGEORA,
+      0,
       0,
+      "Right ascension of geo. center of all clumps.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Clumps geometric center right ascension.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsgeodec",
-      529,
+      UI_KEY_CLUMPSGEODEC,
+      0,
       0,
+      "Declination of geometric center of all clumps.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Clumps geometric center declination.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "brightness",
-      'b',
+      UI_KEY_BRIGHTNESS,
       0,
       0,
       "Brightness (sum of pixel values).",
-      4
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpbrightness",
-      511,
+      UI_KEY_CLUMPSBRIGHTNESS,
+      0,
       0,
+      "Brightness of clumps in an object.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Brightness in clumps of an object.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "noriverbrightness",
-      533,
+      UI_KEY_NORIVERBRIGHTNESS,
       0,
       0,
       "Sky (not river) subtracted clump brightness.",
-      4
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "magnitude",
-      'm',
+      UI_KEY_MAGNITUDE,
       0,
       0,
-      "Total magnitude.",
-      4
+      "Total magnitude of objects or clumps.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "magnitudeerr",
-      'e',
+      UI_KEY_MAGNITUDEERR,
+      0,
       0,
+      "Magnitude error of objects or clumps.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Total magnitude error.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "clumpsmagnitude",
-      512,
+      UI_KEY_CLUMPSMAGNITUDE,
       0,
       0,
-      "Total magnitude of clumps in this object.",
-      4
+      "Magnitude of all clumps in object.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "upperlimit",
+      UI_KEY_UPPERLIMIT,
+      0,
+      0,
+      "Upper-limit value, use other options to config.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "upperlimitmag",
-      534,
+      UI_KEY_UPPERLIMITMAG,
       0,
       0,
-      "Upper limit magnitude for each clump or object.",
-      4
+      "Upper-limit mag. use other options to config.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "riverave",
-      514,
+      UI_KEY_RIVERAVE,
+      0,
       0,
+      "Average river value surrounding a clump.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Average river value surrounding this clump.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "rivernum",
-      515,
+      UI_KEY_RIVERNUM,
+      0,
       0,
+      "Number of river pixels around a clump.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Number of river pixels surrounding this clump.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "sn",
-      'n',
+      UI_KEY_SN,
       0,
       0,
-      "Signal to noise ratio column.",
-      4
+      "Signal to noise ratio of objects or clumps.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "sky",
-      505,
+      UI_KEY_SKY,
+      0,
       0,
+      "Average Sky value under this clump or object.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Sky value.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "std",
-      506,
+      UI_KEY_STD,
       0,
       0,
-      "Sky standard deviation.",
-      4
+      "Average Sky standard deviation.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "semimajor",
-      'A',
+      UI_KEY_SEMIMAJOR,
+      0,
       0,
+      "Flux weighted semi-major axis.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Flux weighted Semi-major axis.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "semiminor",
-      'B',
+      UI_KEY_SEMIMINOR,
+      0,
       0,
+      "Flux weighted semi-minor axis.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Flux weighted Semi-minor axis.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "axisratio",
+      UI_KEY_AXISRATIO,
+      0,
+      0,
+      "Flux weighted axis ratio.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "positionangle",
-      'p',
+      UI_KEY_POSITIONANGLE,
+      0,
       0,
+      "Flux weighted position angle.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Flux weighted Position angle.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "geosemimajor",
-      530,
+      UI_KEY_GEOSEMIMAJOR,
       0,
       0,
-      "Flux weighted Semi-major axis.",
-      4
+      "Geometric semi-major axis.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
       "geosemiminor",
-      531,
+      UI_KEY_GEOSEMIMINOR,
+      0,
       0,
+      "Geometric semi-minor axis.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Flux weighted Semi-minor axis.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
     {
-      "geopositionangle",
-      532,
+      "geoaxisratio",
+      UI_KEY_GEOAXISRATIO,
+      0,
       0,
+      "Geometric axis ratio.",
+      ARGS_GROUP_COLUMNS,
       0,
-      "Flux weighted Position angle.",
-      4
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
-
-
-
-
     {
-      0, 0, 0, 0,
-      "Operating modes:",
-      -1
+      "geopositionangle",
+      UI_KEY_GEOPOSITIONANGLE,
+      0,
+      0,
+      "Geometric position angle.",
+      ARGS_GROUP_COLUMNS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
     },
 
 
-
     {0}
   };
 
@@ -661,378 +917,24 @@ static struct argp_option options[] =
 
 
 
-/* Parse a single option: */
-static error_t
-parse_opt(int key, char *arg, struct argp_state *state)
-{
-  /* Save the arguments structure: */
-  struct mkcatalogparams *p = state->input;
-
-  /* Set the pointer to the common parameters for all programs
-     here: */
-  state->child_inputs[0]=&p->cp;
-
-  /* In case the user incorrectly uses the equal sign (for example
-     with a short format or with space in the long format, then `arg`
-     start with (if the short version was called) or be (if the long
-     version was called with a space) the equal sign. So, here we
-     check if the first character of arg is the equal sign, then the
-     user is warned and the program is stopped: */
-  if(arg && arg[0]=='=')
-    argp_error(state, "incorrect use of the equal sign (`=`). For short "
-               "options, `=` should not be used and for long options, "
-               "there should be no space between the option, equal sign "
-               "and value");
-
-  switch(key)
-    {
-
-    /* Input: */
-    case 'M':
-      gal_checkset_allocate_copy_set(arg, &p->up.maskname,
-                                     &p->up.masknameset);
-      break;
-    case 'H':
-      gal_checkset_allocate_copy_set(arg, &p->up.mhdu, &p->up.mhduset);
-      break;
-    case 'O':
-      gal_checkset_allocate_copy_set(arg, &p->up.objlabsname,
-                                     &p->up.objlabsnameset);
-      break;
-    case 501:
-      gal_checkset_allocate_copy_set(arg, &p->up.objhdu, &p->up.objhduset);
-      break;
-    case 'c':
-      gal_checkset_allocate_copy_set(arg, &p->up.clumplabsname,
-                                     &p->up.clumplabsnameset);
-      break;
-    case 502:
-      gal_checkset_allocate_copy_set(arg, &p->up.clumphdu,
-                                     &p->up.clumphduset);
-      break;
-    case 's':
-      gal_checkset_allocate_copy_set(arg, &p->up.skyname, &p->up.skynameset);
-      break;
-    case 503:
-      gal_checkset_allocate_copy_set(arg, &p->up.skyhdu, &p->up.skyhduset);
-      break;
-    case 't':
-      gal_checkset_allocate_copy_set(arg, &p->up.stdname, &p->up.stdnameset);
-      break;
-    case 504:
-      gal_checkset_allocate_copy_set(arg, &p->up.stdhdu, &p->up.stdhduset);
-      break;
-    case 'z':
-      gal_checkset_any_float(arg, &p->zeropoint, "zeropoint", key, SPACK,
-                             NULL, 0);
-      p->up.zeropointset=1;
-      break;
-    case 'E':
-      p->skysubtracted=1;
-      p->up.skysubtractedset=1;
-      break;
-    case 'T':
-      gal_checkset_any_double(arg, &p->threshold, "threshold", key, SPACK,
-                              NULL, 0);
-      p->up.thresholdset=1;
-      break;
-
-
-    /* Output: */
-    case 521:
-      gal_checkset_any_double(arg, &p->nsigmag, "nsigmag", key, SPACK,
-                              NULL, 0);
-      p->up.nsigmagset=1;
-      break;
-    case 516:
-      gal_checkset_int_l_zero(arg, &p->intwidth, "intwidth", key, SPACK,
-                              NULL, 0);
-      p->up.intwidthset=1;
-      break;
-    case 517:
-      gal_checkset_int_l_zero(arg, &p->floatwidth, "floatwidth", key, SPACK,
-                              NULL, 0);
-      p->up.floatwidthset=1;
-      break;
-    case 518:
-      gal_checkset_int_l_zero(arg, &p->accuwidth, "accuwidth", key, SPACK,
-                              NULL, 0);
-      p->up.floatwidthset=1;
-      break;
-    case 519:
-      gal_checkset_int_l_zero(arg, &p->floatprecision, "flatprecision",
-                              key, SPACK, NULL, 0);
-      p->up.floatprecisionset=1;
-      break;
-    case 520:
-      gal_checkset_int_l_zero(arg, &p->accuprecision, "accuprecision",
-                              key, SPACK, NULL, 0);
-      p->up.accuprecisionset=1;
-      break;
-
-
-    /* Upper limit magnitude */
-    case 535:
-      gal_checkset_allocate_copy_set(arg, &p->up.upmaskname,
-                                     &p->up.upmasknameset);
-      break;
-    case 536:
-      gal_checkset_allocate_copy_set(arg, &p->up.upmaskhdu,
-                                     &p->up.upmaskhduset);
-      break;
-    case 537:
-      gal_checkset_sizet_l_zero(arg, &p->upnum, "upnum", key, SPACK,
-                                NULL, 0);
-      p->up.upnumset=1;
-      break;
-    case 540:
-      p->envseed=1;
-      p->up.envseedset=1;
-      break;
-    case 538:
-      gal_checkset_float_l_0(arg, &p->upsclipmultip, "upsclipmultip",
-                             key, SPACK, NULL, 0);
-      p->up.upsclipmultipset=1;
-      break;
-    case 539:
-      gal_checkset_float_l_0_s_1(arg, &p->upsclipaccu, "upsclipaccu",
-                                 key, SPACK, NULL, 0);
-      p->up.upsclipaccuset=1;
-      break;
-    case 541:
-      gal_checkset_float_l_0(arg, &p->upnsigma, "upnsigma", key,
-                             SPACK, NULL, 0);
-      p->up.upnsigmaset=1;
-      break;
-
-
-
-    /* Catalog columns: */
-    case 'i':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATID);
-      p->up.idset=1;
-      break;
-    case 'j':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATHOSTOBJID);
-      p->up.hostobjidset=1;
-      break;
-    case 'I':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATIDINHOSTOBJ);
-      p->up.idinhostobjset=1;
-      break;
-    case 'C':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATNUMCLUMPS);
-      p->up.numclumpsset=1;
-      break;
-    case 'a':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATAREA);
-      p->up.areaset=1;
-      break;
-    case 513:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSAREA);
-      p->up.clumpsareaset=1;
-      break;
-    case 'x':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATX);
-      p->up.xset=1;
-      break;
-    case 'y':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATY);
-      p->up.yset=1;
-      break;
-    case 522:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOX);
-      p->up.geoxset=1;
-      break;
-    case 523:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOY);
-      p->up.geoyset=1;
-      break;
-    case 507:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSX);
-      p->up.clumpsxset=1;
-      break;
-    case 508:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSY);
-      p->up.clumpsyset=1;
-      break;
-    case 524:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSGEOX);
-      p->up.clumpsgeoxset=1;
-      break;
-    case 525:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSGEOY);
-      p->up.clumpsgeoyset=1;
-      break;
-    case 'r':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATRA);
-      p->up.raset=1;
-      break;
-    case 'd':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATDEC);
-      p->up.decset=1;
-      break;
-    case 526:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATGEORA);
-      p->up.georaset=1;
-      break;
-    case 527:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATGEODEC);
-      p->up.geodecset=1;
-      break;
-    case 509:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSRA);
-      p->up.clumpsraset=1;
-      break;
-    case 510:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSDEC);
-      p->up.clumpsdecset=1;
-      break;
-    case 528:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSGEORA);
-      p->up.clumpsgeoraset=1;
-      break;
-    case 529:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSGEODEC);
-      p->up.clumpsgeodecset=1;
-      break;
-    case 'b':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATBRIGHTNESS);
-      p->up.brightnessset=1;
-      break;
-    case 511:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSBRIGHTNESS);
-      p->up.clumpsbrightnessset=1;
-      break;
-    case 533:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATNORIVERBRIGHTNESS);
-      p->up.noriverbrightnessset=1;
-      break;
-    case 'm':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATMAGNITUDE);
-      p->up.magnitudeset=1;
-      break;
-    case 'e':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATMAGNITUDEERR);
-      p->up.magnitudeerrset=1;
-      break;
-    case 512:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSMAGNITUDE);
-      p->up.clumpsmagnitudeset=1;
-      break;
-    case 534:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATUPPERLIMITMAG);
-      p->up.upperlimitmagset=1;
-      break;
-    case 514:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATRIVERAVE);
-      p->up.riveraveset=1;
-      break;
-    case 515:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATRIVERNUM);
-      p->up.rivernumset=1;
-      break;
-    case 'n':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATSN);
-      p->up.snset=1;
-      break;
-    case 505:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATSKY);
-      p->up.skyset=1;
-      break;
-    case 506:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATSTD);
-      p->up.stdset=1;
-      break;
-    case 'A':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATSEMIMAJOR);
-      p->up.semimajorset=1;
-      break;
-    case 'B':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATSEMIMINOR);
-      p->up.semiminorset=1;
-      break;
-    case 'p':
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATPOSITIONANGLE);
-      p->up.positionangleset=1;
-      break;
-    case 530:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOSEMIMAJOR);
-      p->up.geosemimajorset=1;
-      break;
-    case 531:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOSEMIMINOR);
-      p->up.geosemiminorset=1;
-      break;
-    case 532:
-      gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOPOSITIONANGLE);
-      p->up.geopositionangleset=1;
-      break;
-
-
-
-
-    /* Operating modes: */
-
-
-    /* Read the non-option arguments: */
-    case ARGP_KEY_ARG:
-
-      /* See what type of input value it is and put it in. */
-      if( gal_fits_name_is_fits(arg) )
-        {
-          if(p->up.inputname)
-            argp_error(state, "only one input image should be given");
-          else
-            p->up.inputname=arg;
-        }
-      else
-        argp_error(state, "%s is not a valid file type", arg);
-      break;
+/* Define the child argp structure
+   -------------------------------
 
+   NOTE: these parts can be left untouched.*/
+struct argp
+gal_options_common_child = {gal_commonopts_options,
+                            gal_options_common_argp_parse,
+                            NULL, NULL, NULL, NULL, NULL};
 
+/* Use the child argp structure in list of children (only one for now). */
+struct argp_child
+children[]=
+{
+  {&gal_options_common_child, 0, NULL, 0},
+  {0, 0, 0, 0}
+};
 
-
-
-    /* The command line options and arguments are finished. */
-    case ARGP_KEY_END:
-      if(p->cp.setdirconf==0 && p->cp.setusrconf==0
-         && p->cp.printparams==0)
-        {
-          if(state->arg_num==0)
-            argp_error(state, "no argument given");
-          if(p->up.inputname==NULL)
-            argp_error(state, "no input FITS image(s) provided");
-        }
-      break;
-
-
-
-
-
-    default:
-      return ARGP_ERR_UNKNOWN;
-    }
-  return 0;
-}
-
-
-
-
-
-/* Specify the children parsers: */
-struct argp_child children[]=
-  {
-    {&commonargp, 0, NULL, 0},
-    {0, 0, 0, 0}
-  };
-
-
-
-
-
-/* Basic structure defining the whole argument reading process. */
-static struct argp thisargp = {options, parse_opt, args_doc,
-                               doc, children, NULL, NULL};
-
+/* Set all the necessary argp parameters. */
+struct argp
+thisargp = {program_options, parse_opt, args_doc, doc, children, NULL, NULL};
 #endif
diff --git a/bin/mkcatalog/astmkcatalog.conf b/bin/mkcatalog/astmkcatalog.conf
index cdd17c8..40393ee 100644
--- a/bin/mkcatalog/astmkcatalog.conf
+++ b/bin/mkcatalog/astmkcatalog.conf
@@ -18,46 +18,20 @@
 # without any warranty.
 
 # Input:
- objhdu          2
- clumphdu        3
- skyhdu        SKY
- stdhdu    SKY_STD
- zeropoint     0.0
- skysubtracted   0
+ hdu                1
+ objectshdu         2
+ clumpshdu          3
+ skyhdu           SKY
+ stdhdu       SKY_STD
+ zeropoint        0.0
+ skysubtracted      0
 
 # Output:
- nsigmag         1
- intwidth        6
- floatwidth     13
- accuwidth      15
- floatprecision  3
- accuprecision   8
+ nsigmag            1
 
 # Upper limit magnitude:
- upnum         200
- upsclipmultip   3
- upsclipaccu   0.2
- upnsigma        1
+ upnum            100
+ upsigmaclip    3,0.2
+ upnsigma           1
 
 # Catalog columns:
-# sn              1
-# std             1
-# sky             1
-# rivernum        1
-# riverave        1
-# clumpsdec       1
-# clumpsra        1
-# clumpsy         1
-# clumpsx         1
-# clumpsmagnitude 1
-# clumpsarea      1
-# numclumps       1
-# magnitude       1
-# area            1
-# dec             1
-# ra              1
-# y               1
-# x               1
-# idinhostobj     1
-# hostobjid       1
- id              1
diff --git a/bin/mkcatalog/columns.c b/bin/mkcatalog/columns.c
index dbbc29b..68f75ea 100644
--- a/bin/mkcatalog/columns.c
+++ b/bin/mkcatalog/columns.c
@@ -2,18 +2,10 @@
 MakeCatalog - Make a catalog from an input and labeled image.
 MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) package.
 
-ABOUT THIS FILE: The information tables are fully explained in the
-  comments of main.h. After the raw information is read in the first
-  and second pass, the job of the functions here is to process the raw
-  columns that are needed into useful knowledge and print them. for
-  example those functions will only record the weighted sum of pixel
-  positions and the total weight, here the weighted sum is divided by
-  the total weight to yeild an average.
-
 Original author:
      Mohammad Akhlaghi <address@hidden>
 Contributing author(s):
-Copyright (C) 2015, Free Software Foundation, Inc.
+Copyright (C) 2016, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -34,57 +26,60 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdio.h>
 #include <errno.h>
 #include <error.h>
-#include <string.h>
 #include <stdlib.h>
+#include <pthread.h>
 
-#include <gnuastro/wcs.h>
+#include <gnuastro/linkedlist.h>
 
 #include "main.h"
-
-#include "columns.h"
 #include "mkcatalog.h"
-#include "upperlimit.h"
-
-
-
-
-
 
+#include "ui.h"
+#include "columns.h"
 
 
 
 
 /******************************************************************/
-/*******        Information table modifications       *************/
+/*******************     Intermediate arrays     ******************/
 /******************************************************************/
-/* Correct the average Sky and Sky standard deviation value for
-   objects and clumps. Note that during the passes, these were just
-   sums of pixel values, they need to be divided by the area of the
-   object/clump, which is done here. */
-void
-setskystd(struct mkcatalogparams *p, size_t col)
+/* Allocate RA-DEC internal arrays. These arrays are defined to keep all
+   the positions in one place and do the RA-DEC conversion once in the
+   end. They are all allocated together, but we don't know if RA is
+   requested first or Dec or if they are requested multiple times. So
+   before the allocation, we'll check the first one.
+
+   The space that is allocated in `columns_define_alloc' is for the final
+   values that are written in the output file. */
+static void
+columns_alloc_radec(struct mkcatalogparams *p)
 {
-  size_t ac;
-  double *row = p->info + p->icols;
-  double *end = row + (p->icols * p->num);
-
-  /* Only do the correction if this column is not already flagged. */
-  if(p->info[col]==0.0f)
+  if(p->rd_vo==NULL)
     {
-
-      /* Set the area column: */
-      ac = p->obj0clump1 ? CALLAREA : OALLAREA;
-
-      /* Go over every row and do the correction. */
-      do
+      /* Allocate the space for all dimensions. */
+      errno=0;
+      p->rd_vo = malloc(p->input->ndim * sizeof *p->rd_vo);
+      if(p->rd_vo==NULL)
+        error(EXIT_FAILURE, 0, "%zu bytes for p->rd_vo, "
+              "`columns_alloc_radec'", p->input->ndim * sizeof *p->rd_vo );
+
+      /* Space for each dimension. */
+      p->rd_vo[0] = gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numobjects);
+      p->rd_vo[1] = gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numobjects);
+      if(p->clumps)
         {
-          row[col] = ( row[ac]>0.0f ? row[col]/row[ac] : NAN );
-          row+=p->icols;
+          /* Allocate the space for all dimensions. */
+          errno=0;
+          p->rd_vc = malloc(p->input->ndim * sizeof *p->rd_vc);
+          if(p->rd_vc==NULL)
+            error(EXIT_FAILURE, 0, "%zu bytes for p->rd_vo, "
+                  "`columns_alloc_radec'",
+                  p->input->ndim * sizeof *p->rd_vc );
+
+          /* Space for each dimension. */
+          p->rd_vc[0]=gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numclumps);
+          p->rd_vc[1]=gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numclumps);
         }
-      while(row<end);
-
-      /* Set the flag so this operation is not done again. */
-      p->info[col]=1.0f;
     }
 }
 
@@ -92,39 +87,36 @@ setskystd(struct mkcatalogparams *p, size_t col)
 
 
 
-/* Correct the average river value, after the passes, it is just the
-   sum. */
-void
-setaveriver(struct mkcatalogparams *p)
+/* Similar to `columns_alloc_radec'. */
+static void
+columns_alloc_georadec(struct mkcatalogparams *p)
 {
-  double *row = p->info + p->icols;
-  double *end = row + (p->icols * p->num);
-
-  /* Only do the correction if this column is not already flagged. */
-  if(p->info[CRivAve]==0.0f)
+  if(p->rd_go==NULL)
     {
-
-      /* Make sure the Sky values are corrected */
-      setskystd(p, CSKY);
-
-      /* Go over every row and do the correction. Note that in cases
-         where the grown clumps are used instead of the clumps, we are
-         not going to have any rivers (row[CRivArea]==0.0f). In such
-         situations, set the per-pixel average river value to the Sky
-         value under the clump. The reason is that for the clumps, Sky
-         subtraction was not done on the Clump brightness, so this
-         value will be used, and if there was no river, then we need
-         something to replace it. */
-      do
+      /* Allocate the space for all dimensions. */
+      errno=0;
+      p->rd_go = malloc(p->input->ndim * sizeof *p->rd_go);
+      if(p->rd_go==NULL)
+        error(EXIT_FAILURE, 0, "%zu bytes for p->rd_go, "
+              "`columns_alloc_radec'", p->input->ndim * sizeof *p->rd_go );
+
+      /* Space for each dimension. */
+      p->rd_go[0] = gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numobjects);
+      p->rd_go[1] = gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numobjects);
+      if(p->clumps)
         {
-          row[CRivAve] = ( row[CRivArea]>0.0f
-                           ? row[CRivAve]/row[CRivArea] : row[CSKY] );
-          row+=p->icols;
+          /* Allocate the space for all dimensions. */
+          errno=0;
+          p->rd_gc = malloc(p->input->ndim * sizeof *p->rd_gc);
+          if(p->rd_gc==NULL)
+            error(EXIT_FAILURE, 0, "%zu bytes for p->rd_go, "
+                  "`columns_alloc_radec'",
+                  p->input->ndim * sizeof *p->rd_gc );
+
+          /* Space for each dimension. */
+          p->rd_gc[0]=gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numclumps);
+          p->rd_gc[1]=gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numclumps);
         }
-      while(row<end);
-
-      /* Set the flag so this operation is not done again. */
-      p->info[CRivAve]=1.0f;
     }
 }
 
@@ -132,37 +124,22 @@ setaveriver(struct mkcatalogparams *p)
 
 
 
-/* The clump brightness values are not Sky subtracted since the river
-   values (which are also not Sky subtracted) should be subtracted
-   from them. Here that job is done. */
-void
-setclumpbrightness(struct mkcatalogparams *p)
+/* Similar to `columns_alloc_radec'. */
+static void
+columns_alloc_clumpsradec(struct mkcatalogparams *p)
 {
-  double *row = p->info + p->icols;
-  double *end = row + (p->icols * p->num);
-
-  /* Only do the correction if this column is not already flagged. */
-  if(p->info[CBrightness]==0.0f)
+  if(p->rd_vcc==NULL)
     {
-
-      /* Make sure the average river value is calculated */
-      setaveriver(p);
-
-      /* On a clump, we have to subtract the average river flux
-         multiplied by the the area of the clump. The value in the
-         CBrightness column is simply the sum of pixels. Note that
-         here we are multiplying by the area of the clump (CALLAREA)
-         irrespective of threshold, while in setaveriver(), we divided
-         by the area of the river (CRivArea). */
-      do
-        {
-          row[CBrightness] -= row[CRivAve]*row[CAREA];
-          row+=p->icols;
-        }
-      while(row<end);
-
-      /* Set the flag so this operation is not done again. */
-      p->info[CBrightness]=1.0f;
+      /* Allocate the space for all dimensions. */
+      errno=0;
+      p->rd_vcc = malloc(p->input->ndim * sizeof *p->rd_vcc);
+      if(p->rd_vcc==NULL)
+        error(EXIT_FAILURE, 0, "%zu bytes for p->rd_vcc, "
+              "`columns_alloc_radec'", p->input->ndim * sizeof *p->rd_vcc );
+
+      /* Space for each dimension. */
+      p->rd_vcc[0] = gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numobjects);
+      p->rd_vcc[1] = gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numobjects);
     }
 }
 
@@ -170,49 +147,22 @@ setclumpbrightness(struct mkcatalogparams *p)
 
 
 
-/* Find the geometric center of the profile (average position,
-   ignoring any flux of the pixels). */
-void
-geoxy(struct mkcatalogparams *p, size_t col)
+/* Similar to `columns_alloc_radec'. */
+static void
+columns_alloc_clumpsgeoradec(struct mkcatalogparams *p)
 {
-  size_t ac=-1;
-  double *row = p->info + p->icols;
-  double *end = row + (p->icols * p->num);
-
-  /* Only if this column is not flagged as already done (==1.0f). */
-  if(p->info[col]==0.0f)
+  if(p->rd_gcc==NULL)
     {
-
-      /* First, set the columns to use for the conversion. */
-      if(p->obj0clump1)                         ac=CAREA;
-      else
-        {
-          if      (col==OGeoX || col==OGeoY)    ac=OAREA;
-          else if (col==OGeoCX || col==OGeoCY)  ac=OAREAC;
-          else
-            error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                  "can fix this. The given column in the --OBJECTS-- "
-                  "information table was not recognized for calculating the "
-                  "geometric X and/or Y", PACKAGE_BUGREPORT);
-        }
-
-      /* Go over all the rows and correct this column. */
-      do
-        {
-          /* Set the value for this row. Note that unlike the
-             calculations here that start counting with zero, the FITS
-             standard starts counting from 1, so add a one after
-             dividing by the area. If the area is zero, then set
-             NaN. */
-          row[col] = row[ac]>0.0f ? row[col] / row[ac] : NAN;
-
-          /* Go onto the next row: */
-          row+=p->icols;
-        }
-      while(row<end);
-
-      /* Flag this column as complete for future reference. */
-      p->info[col]=1.0f;
+      /* Allocate the space for all dimensions. */
+      errno=0;
+      p->rd_gcc = malloc(p->input->ndim * sizeof *p->rd_gcc);
+      if(p->rd_gcc==NULL)
+        error(EXIT_FAILURE, 0, "%zu bytes for p->rd_gcc, "
+              "`columns_alloc_radec'", p->input->ndim * sizeof *p->rd_gcc );
+
+      /* Space for each dimension. */
+      p->rd_gcc[0] = gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numobjects);
+      p->rd_gcc[1] = gal_data_malloc_array(GAL_TYPE_FLOAT64, p->numobjects);
     }
 }
 
@@ -220,855 +170,1300 @@ geoxy(struct mkcatalogparams *p, size_t col)
 
 
 
-/* A low-level function used to find the flux weighted center, since
-   it is needed by multiple columns. The geometric center for this
-   axis colum (geocol) and area column (areacol) are needed for backup
-   (when there might not be any positive flux pixel/data values to use
-   for weight). */
-void
-flxwhtimg(struct mkcatalogparams *p, size_t col)
-{
-  size_t wc=-1, gc=-1;
-  double *row = p->info + p->icols;
-  double *end = row + (p->icols * p->num);;
 
 
-  /* Only if this column is not flagged as already done (==1.0f). */
-  if(p->info[col]==0.0f)
-    {
 
-      /* First, set the columns to use for the conversion. */
-      if(p->obj0clump1)
-        {
-          wc=CPosBright;
-          if     (col==CFlxWhtX) gc=CGeoX;
-          else if(col==CFlxWhtY) gc=CGeoY;
-          else
-            error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                  "can fix this. The given column in the --CLUMPS-- "
-                  "information table was not recognized for calculating the "
-                  "final flux weighted X and/or Y", PACKAGE_BUGREPORT);
-        }
-      else
-        {
-          if (col==OFlxWhtX || col==OFlxWhtY)
-            {
-              wc=OPosBright;
-              gc = col==OFlxWhtX ? OGeoX : OGeoY;
-            }
-          else if (col==OFlxWhtCX || col==OFlxWhtCY)
-            {
-              wc=OPosBrightC;
-              gc = col==OFlxWhtCX ? OGeoCX : OGeoCY;
-            }
-          else
-            error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                  "can fix this. The given column in the --OBJECTS-- "
-                  "information table was not recognized for calculating the "
-                  "final flux weighted X and/or Y", PACKAGE_BUGREPORT);
-        }
 
 
-      /* The geometric positions act as a backup for the flux weighted
-         centers, so make sure the appropriate geometric center is
-         defined. */
-      geoxy(p, gc);
 
-      /* For a check, uncomment these two lines:
-      printf("\n\nInfocol: %zu (%s, %zu)\n", col,
-             p->info==p->oinfo?"Objects":"Clumps", p->num);
-      Then add these two lines before and after row[col] in the loop*/
-      /*printf("%zu: %f --> ", (row-p->info)/p->icols, row[col]);*/
-      /*printf("%f\n", row[col]);*/
 
-      /* Go over all the rows and correct this column. */
-      do
-        {
-          /* Set the value for this row. When a positive weight is
-             present, we are adding with one (1) because of the FITS
-             standard. */
-          row[col] = row[wc]>0.0f ? (row[col]/row[wc]) : row[gc];
 
-          /* Go onto the next row: */
-          row+=p->icols;
-        }
-      while(row<end);
 
-      /* Set the flag for this column to one, so this whole proces is
-         not done again. */
-      p->info[col]=1.0f;
-    }
-}
 
 
 
 
 
-/* To correct the second moment, we need three variables: the first
-   moment in first axis, first moment in second axis and the total
-   weight. The first two are the same when the second moment is a
-   power of one axis. The weight is either the total positive flux
-   used for the weights, or is the area. */
+
+/******************************************************************/
+/**********       Column definition/allocation      ***************/
+/******************************************************************/
+/* Set the necessary parameters for each output column and allocate the
+   space necessary to keep the values. */
 void
-setsecondmoment(struct mkcatalogparams *p, size_t col)
+columns_define_alloc(struct mkcatalogparams *p)
 {
-  double *row = p->info + p->icols;
-  double *end = row + (p->icols * p->num);
-  size_t fc=-1, sc=-1, wc=-1, sfc=-1, ssc=-1;
+  struct gal_linkedlist_ill *colcode;
+  int disp_fmt=0, disp_width=0, disp_precision=0;
+  struct gal_linkedlist_stll *strtmp, *noclumpimg=NULL;
+  char *name=NULL, *unit=NULL, *ocomment=NULL, *ccomment=NULL;
+  uint8_t otype=GAL_TYPE_INVALID, ctype=GAL_TYPE_INVALID, *oiflag, *ciflag;
+
+  /* Allocate the array for which intermediate parameters are
+     necessary. The basic issue is that higher-level calculations require a
+     smaller domain of raw measurements. So to avoid having to calculate
+     something multiple times, each parameter will flag the intermediate
+     parameters it requires in these arrays. */
+  oiflag = p->oiflag = gal_data_malloc_array(GAL_TYPE_UINT8, OCOL_NUMCOLS);
+  ciflag = p->ciflag = gal_data_malloc_array(GAL_TYPE_UINT8, CCOL_NUMCOLS);
+
+  /* Allocate the columns. */
+  for(colcode=p->columnids; colcode!=NULL; colcode=colcode->next)
+    {
+      /* Set the column-specific parameters, please follow the same order
+         as `args.h'. IMPORTANT: we want the names to be the same as the
+         option names. Note that zero `disp_' variables will be
+         automatically determined.*/
+      switch(colcode->v)
+        {
+        case UI_KEY_OBJID:
+          name           = "OBJ_ID";
+          unit           = "counter";
+          ocomment       = "Object identifier.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_INT32;  /* Same type as clumps image. */
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          /* Is an internal parameter. */
+          break;
 
+        case UI_KEY_HOSTOBJID:
+          name           = "HOST_OBJ_ID";
+          unit           = "counter";
+          ocomment       = NULL;
+          ccomment       = "Object identifier hosting this clump.";
+          otype          = GAL_TYPE_INVALID;
+          ctype          = GAL_TYPE_INT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          /* Is an internal parameter. */
+          break;
 
-  /* Only if this column is not flagged as already done (==1.0f). */
-  if(p->info[col]==0.0f)
-    {
+        case UI_KEY_IDINHOSTOBJ:
+          name           = "ID_IN_HOST_OBJ";
+          unit           = "counter";
+          ocomment       = NULL;
+          ccomment       = "ID of clump in its host object.";
+          otype          = GAL_TYPE_INVALID;
+          ctype          = GAL_TYPE_INT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          /* Is an internal parameter. */
+          break;
 
-      /* First, set the columns to use for the conversion. Note that
-         since we also need to correct the column, we have merged the
-         setting of fc and sc and the calling of the flxwhtimg
-         function into one call.  */
-      if(p->obj0clump1)
-        switch(col)
-          {
-          /* Clump brightness weighted */
-          case CFlxWhtXX:
-            wc=CPosBright; fc=sc=CFlxWhtX; flxwhtimg(p, fc);
-            sfc=ssc=CPOSSHIFTX;                   break;
-          case CFlxWhtYY:
-            wc=CPosBright; fc=sc=CFlxWhtY; flxwhtimg(p, fc);
-            sfc=ssc=CPOSSHIFTY;                   break;
-          case CFlxWhtXY:
-            wc=CPosBright;
-            flxwhtimg(p, fc=CFlxWhtX); flxwhtimg(p, sc=CFlxWhtY);
-            sfc=CPOSSHIFTX;  ssc=CPOSSHIFTY;      break;
-
-          /* Clump geometric: */
-          case CGeoXX:
-            wc=CAREA;       fc=sc=CGeoX;    geoxy(p, fc);
-            sfc=ssc=CPOSSHIFTX;                   break;
-          case CGeoYY:
-            wc=CAREA;       fc=sc=CGeoY;    geoxy(p, fc);
-            sfc=ssc=CPOSSHIFTY;                   break;
-          case CGeoXY:
-            wc=CAREA; geoxy(p, fc=CGeoX); geoxy(p, sc=CGeoY);
-            sfc=CPOSSHIFTX;  ssc=CPOSSHIFTY;      break;
+        case UI_KEY_NUMCLUMPS:
+          name           = "NUM_CLUMPS";
+          unit           = "counter";
+          ocomment       = "Number of clumps in this object.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_INT32;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = 0;
+          disp_width     = 5;
+          disp_precision = 0;
+          /* Is an internal parameter. */
+          break;
 
-          default:
-            error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                  "can fix this. The given column in setsecondmoment's "
-                  "--CLUMP-- information table (%zu) was not recognized for "
-                  "correcting the second moment", PACKAGE_BUGREPORT, col);
-          }
-      else
-        switch(col)
-          {
-          /* All object brightness weighted: */
-          case OFlxWhtXX:
-            wc=OPosBright;  fc=sc=OFlxWhtX; flxwhtimg(p, fc);
-            sfc=ssc=OPOSSHIFTX;                   break;
-          case OFlxWhtYY:
-            wc=OPosBright;  fc=sc=OFlxWhtY; flxwhtimg(p, fc);
-            sfc=ssc=OPOSSHIFTY;                   break;
-          case OFlxWhtXY:
-            wc=OPosBright;
-            flxwhtimg(p, fc=OFlxWhtX); flxwhtimg(p, sc=OFlxWhtY);
-            sfc=OPOSSHIFTX;  ssc=OPOSSHIFTY;      break;
-
-          /* All object geometric: */
-          case OGeoXX:
-            wc=OAREA;       fc=sc=OGeoX;    geoxy(p, fc);
-            sfc=ssc=OPOSSHIFTX;                   break;
-          case OGeoYY:
-            wc=OAREA;       fc=sc=OGeoY;    geoxy(p, fc);
-            sfc=ssc=OPOSSHIFTY;                   break;
-          case OGeoXY:
-            wc=OAREA; geoxy(p, fc=OGeoX); geoxy(p, sc=OGeoY);
-            sfc=OPOSSHIFTX;  ssc=OPOSSHIFTY;      break;
-
-          /* Clumps in object brightness weighted: */
-          case OFlxWhtCXX:
-            wc=OPosBrightC; fc=sc=OFlxWhtCX; flxwhtimg(p, fc);
-            sfc=ssc=OPOSSHIFTX;                   break;
-          case OFlxWhtCYY:
-            wc=OPosBrightC; fc=sc=OFlxWhtCY; flxwhtimg(p, fc);
-            sfc=ssc=OPOSSHIFTY;                   break;
-          case OFlxWhtCXY:
-            wc=OPosBrightC;
-            flxwhtimg(p, fc=OFlxWhtCX); flxwhtimg(p, sc=OFlxWhtCY);
-            sfc=OPOSSHIFTX;  ssc=OPOSSHIFTY;      break;
-
-          /* Clumps in object geometric: */
-          case OGeoCXX:
-            wc=OAREAC;      fc=sc=OGeoCX;    geoxy(p, fc);
-            sfc=ssc=OPOSSHIFTX;                   break;
-          case OGeoCYY:
-            wc=OAREAC;      fc=sc=OGeoCY;    geoxy(p, fc);
-            sfc=ssc=OPOSSHIFTY;                   break;
-          case OGeoCXY:
-            wc=OAREAC; geoxy(p, fc=OGeoCX); geoxy(p, sc=OGeoCY);
-            sfc=OPOSSHIFTX;  ssc=OPOSSHIFTY;      break;
-          default:
-            error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                  "can fix this. The given column in setsecondmoment's "
-                  "--OBJECT-- information table (%zu) was not recognized for "
-                  "correcting the second moment", PACKAGE_BUGREPORT, col);
-          }
+        case UI_KEY_AREA:
+          name           = "AREA";
+          unit           = "counter";
+          ocomment       = "Number of pixels covered.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_INT32;
+          ctype          = GAL_TYPE_INT32;
+          disp_fmt       = 0;
+          disp_width     = 5;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM ] = 1;
+          ciflag[ CCOL_NUM ] = 1;
+          break;
 
-      /* Go over all the rows and correct this column. */
-      do
-        {
+        case UI_KEY_CLUMPSAREA:
+          name           = "CLUMPS_AREA";
+          unit           = "counter";
+          ocomment       = "Total number of clump pixels in object.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_INT32;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = 0;
+          disp_width     = 5;
+          disp_precision = 0;
+          oiflag[ OCOL_C_NUM ] = 1;
+          break;
 
-          /* Set the value for this row, including the shift in
-             calculating the second order moments. */
-          row[col] = ( row[col]/row[wc] -
-                       (row[fc]-row[sfc]) * (row[sc]-row[ssc]) );
+        case UI_KEY_X:
+          name           = "X";
+          unit           = "position";
+          ocomment       = "Flux weighted center (FITS axis 1).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_VX ] = 1;
+          ciflag[ CCOL_VX ] = 1;
+          break;
 
-          /* Go onto the next row: */
-          row+=p->icols;
-        }
-      while(row<end);
+        case UI_KEY_Y:
+          name           = "Y";
+          unit           = "position";
+          ocomment       = "Flux weighted center (FITS axis 2).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_VY ] = 1;
+          ciflag[ CCOL_VY ] = 1;
+          break;
 
-      /* Set the flag for this column to one, so this whole proces is
-         not done again. */
-      p->info[col]=1.0f;
-    }
-}
+        case UI_KEY_GEOX:
+          name           = "GEO_X";
+          unit           = "position";
+          ocomment       = "Geometric center (FITS axis 1).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_GX ] = 1;
+          ciflag[ CCOL_GX ] = 1;
+          break;
 
+        case UI_KEY_GEOY:
+          name           = "GEO_Y";
+          unit           = "position";
+          ocomment       = "Geometric center (FITS axis 2).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_GY ] = 1;
+          ciflag[ CCOL_GY ] = 1;
+          break;
 
+        case UI_KEY_CLUMPSX:
+          name           = "CLUMPS_X";
+          unit           = "position";
+          ocomment       = "Flux weighted center of clumps (FITS axis 1).";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_C_VX ] = 1;
+          break;
 
+        case UI_KEY_CLUMPSY:
+          name           = "CLUMPS_Y";
+          unit           = "position";
+          ocomment       = "Flux weighted center of clumps (FITS axis 2).";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_C_VY ] = 1;
+          break;
 
+        case UI_KEY_CLUMPSGEOX:
+          name           = "CLUMPS_GEO_X";
+          unit           = "position";
+          ocomment       = "Geometric center of clumps (FITS axis 1).";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_C_GX ] = 1;
+          break;
 
-/* Fill in the RA and Dec columns, note that we will need the X and Y
-   colums first for this. */
-void
-preparewcs(struct mkcatalogparams *p, size_t col)
-{
-  /* Initialize all the columns to -1 (largest possible number in the
-     C's unsigned char, so if there is any bugs, we get a good error. */
-  int wht0geo1=-1;
-  size_t xc=-1, yc=-1, rc=-1, dc=-1;
-
-
-  /* RA and Dec are usually needed together and must also be
-     calculated together, but here, we are giving the user complete
-     freedom in setting the columns in which ever order they want. So
-     after calculating the RA and Dec once for either of the two,
-     there is no more need to do the calculation again.  */
-  if(p->info[col]==0.0f)
-    {
+        case UI_KEY_CLUMPSGEOY:
+          name           = "CLUMPS_GEO_Y";
+          unit           = "position";
+          ocomment       = "Geometric center of clumps (FITS axis 2).";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_C_GY ] = 1;
+          break;
 
-      /* First, set the columns to use for the conversion. */
-      if(p->obj0clump1)
-        {
-          /* Clump, flux weighted: */
-          if(col==CFlxWhtRA || col==CFlxWhtDec)
-            {
-              xc=CFlxWhtX;  yc=CFlxWhtY;
-              rc=CFlxWhtRA; dc=CFlxWhtDec;
-              wht0geo1=0;
-            }
-          /* Clump, geometric: */
-          else if(col==CGeoRA || col==CGeoDec)
-            {
-              xc=CGeoX;     yc=CGeoY;
-              rc=CGeoRA;    dc=CGeoDec;
-              wht0geo1=1;
-            }
-          else
-            error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                  "can fix this. The given column in the --CLUMPS-- "
-                  "information table was not recognized for calculating the "
-                  "RA and Dec", PACKAGE_BUGREPORT);
-        }
-      else
-        {
-          /* All clumps in object, flux weighted: */
-          if(col==OFlxWhtCRA || col==OFlxWhtCDec)
-            {
-              xc=OFlxWhtCX;  yc=OFlxWhtCY;
-              rc=OFlxWhtCRA; dc=OFlxWhtCDec;
-              wht0geo1=0;
-            }
-          /* All clumps in object, geometric: */
-          else if(col==OGeoCRA || col==OGeoCDec)
-            {
-              xc=OGeoCX;  yc=OGeoCY;
-              rc=OGeoCRA; dc=OGeoCDec;
-              wht0geo1=1;
-            }
-          /* All object, flux weighted */
-          else if(col==OFlxWhtRA || col==OFlxWhtDec)
-            {
-              xc=OFlxWhtX;  yc=OFlxWhtY;
-              rc=OFlxWhtRA; dc=OFlxWhtDec;
-              wht0geo1=0;
-            }
-          /* All object, geometric */
-          else if(col==OGeoRA || col==OGeoDec)
-            {
-              xc=OGeoX;     yc=OGeoY;
-              rc=OGeoRA;    dc=OGeoDec;
-              wht0geo1=1;
-            }
-          else
-            error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                  "can fix this. The given column in the --OBJECT-- "
-                  "information table was not recognized for calculating the "
-                  "RA and Dec", PACKAGE_BUGREPORT);
-        }
+        case UI_KEY_RA:
+          name           = "RA";
+          unit           = "degrees";
+          ocomment       = "Flux weighted center right ascension.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT64;
+          ctype          = GAL_TYPE_FLOAT64;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 13;
+          disp_precision = 7;
+          oiflag[ OCOL_C_VY ] = 1;
+          oiflag[ OCOL_C_VY ] = 1;
+          columns_alloc_radec(p);
+          break;
 
+        case UI_KEY_DEC:
+          name           = "DEC";
+          unit           = "degrees";
+          ocomment       = "Flux weighted center declination.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT64;
+          ctype          = GAL_TYPE_FLOAT64;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 13;
+          disp_precision = 7;
+          oiflag[ OCOL_C_VY ] = 1;
+          oiflag[ OCOL_C_VY ] = 1;
+          columns_alloc_radec(p);
+          break;
 
-      /* Finalize the relevant X and Y positions first (which are
-         needed for the WCS conversion). Note that if they are ready
-         to use (their flag is 1.0f), these functions will not do
-         anything. But if the user hasn't already asked for X and Y,
-         then these columns will be corrected here.*/
-      switch(wht0geo1)
-        {
-        case 0:
-          flxwhtimg(p, xc);
-          flxwhtimg(p, yc);
+        case UI_KEY_GEORA:
+          name           = "GEO_RA";
+          unit           = "degrees";
+          ocomment       = "Geometric center right ascension.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT64;
+          ctype          = GAL_TYPE_FLOAT64;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 13;
+          disp_precision = 7;
+          oiflag[ OCOL_GX ] = 1;
+          oiflag[ OCOL_GY ] = 1;
+          ciflag[ CCOL_GX ] = 1;
+          ciflag[ CCOL_GY ] = 1;
+          columns_alloc_georadec(p);
           break;
-        case 1:
-          geoxy(p, xc);
-          geoxy(p, yc);
+
+        case UI_KEY_GEODEC:
+          name           = "GEO_DEC";
+          unit           = "degrees";
+          ocomment       = "Geometric center declination.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT64;
+          ctype          = GAL_TYPE_FLOAT64;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 13;
+          disp_precision = 7;
+          oiflag[ OCOL_GX ] = 1;
+          oiflag[ OCOL_GY ] = 1;
+          ciflag[ CCOL_GX ] = 1;
+          ciflag[ CCOL_GY ] = 1;
+          columns_alloc_georadec(p);
           break;
-        default:
-          error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                "can fix this. The value of the wht0geo1 variable (%d) is "
-                "not recognized", PACKAGE_BUGREPORT, wht0geo1);
-        }
 
+        case UI_KEY_CLUMPSRA:
+          name           = "CLUMPS_RA";
+          unit           = "degrees";
+          ocomment       = "RA of all clumps flux weighted center.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT64;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 13;
+          disp_precision = 7;
+          oiflag[ OCOL_C_VX ] = 1;
+          oiflag[ OCOL_C_VY ] = 1;
+          columns_alloc_clumpsradec(p);
+          break;
 
-      /* Do the conversion. Note that the p->icols is added because
-         the first row is not used by any object or colump (since
-         their indexes begin from 1).*/
-      gal_wcs_xy_array_to_radec(p->wcs, p->info+p->icols+xc,
-                                p->info+p->icols+rc, p->num, p->icols);
+        case UI_KEY_CLUMPSDEC:
+          name           = "CLUMPS_DEC";
+          unit           = "degrees";
+          ocomment       = "Declination of all clumps flux weighted center.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT64;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 15;
+          disp_precision = 7;
+          oiflag[ OCOL_C_VX ] = 1;
+          oiflag[ OCOL_C_VY ] = 1;
+          columns_alloc_clumpsradec(p);
+          break;
 
+        case UI_KEY_CLUMPSGEORA:
+          name           = "CLUMPS_RA";
+          unit           = "degrees";
+          ocomment       = "RA of all clumps geometric center.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT64;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 13;
+          disp_precision = 7;
+          oiflag[ OCOL_C_GX ] = 1;
+          oiflag[ OCOL_C_GY ] = 1;
+          columns_alloc_clumpsgeoradec(p);
+          break;
 
-      /* Set the flag of the converted columns to 1.0f, so the
-         calculations are not repeated if any of the columns is needed
-         again. Note that it is irrelevant which one of the RA or Dec
-         were calculated, so we are not using `col' here. */
-      p->info[rc]=p->info[dc]=1.0f;
-    }
-}
+        case UI_KEY_CLUMPSGEODEC:
+          name           = "CLUMPS_DEC";
+          unit           = "degrees";
+          ocomment       = "Declination of all clumps geometric center.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT64;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 13;
+          disp_precision = 7;
+          oiflag[ OCOL_C_GX ] = 1;
+          oiflag[ OCOL_C_GY ] = 1;
+          columns_alloc_clumpsgeoradec(p);
+          break;
 
+        case UI_KEY_BRIGHTNESS:
+          name           = "BRIGHTNESS";
+          unit           = p->input->unit ? p->input->unit : "pixelunit";
+          ocomment       = "Brightness (sum of sky subtracted values).";
+          ccomment       = "Brightness (sum of pixels subtracted by rivers).";
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 4;
+          oiflag[ OCOL_SUM ]     = 1;
+          ciflag[ CCOL_SUM ]     = 1;
+          ciflag[ CCOL_RIV_NUM ] = 1;
+          ciflag[ CCOL_RIV_SUM ] = 1;
+          break;
 
+        case UI_KEY_CLUMPSBRIGHTNESS:
+          name           = "CLUMPS_BRIGHTNESS";
+          unit           = p->input->unit ? p->input->unit : "pixelunit";
+          ocomment       = "Brightness (sum of pixel values) in clumps.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 4;
+          oiflag[ OCOL_C_SUM ] = 1;
+          break;
 
+        case UI_KEY_NORIVERBRIGHTNESS:
+          name           = "NO_RIVER_BRIGHTNESS";
+          unit           = p->input->unit ? p->input->unit : "pixelunit";
+          ocomment       = NULL;
+          ccomment       = "Brightness (sum of sky subtracted values).";
+          otype          = GAL_TYPE_INVALID;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 4;
+          ciflag[ CCOL_SUM ] = 1;
+          break;
 
+        case UI_KEY_MAGNITUDE:
+          name           = "MAGNITUDE";
+          unit           = "log";
+          ocomment       = "Magnitude.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 8;
+          disp_precision = 3;
+          oiflag[ OCOL_SUM ] = 1;
+          ciflag[ CCOL_SUM ] = 1;
+          p->hasmag      = 1;
+          break;
 
+        case UI_KEY_MAGNITUDEERR:
+          name           = "MAGNITUDE_ERR";
+          unit           = "log";
+          ocomment       = "Error in measuring magnitude.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 8;
+          disp_precision = 3;
+          oiflag[ OCOL_SUMSTD ] = 1;
+          oiflag[ OCOL_NUM    ] = 1;
+          oiflag[ OCOL_SUM    ] = 1;
+          ciflag[ CCOL_SUMSTD ] = 1;
+          ciflag[ CCOL_NUM    ] = 1;
+          ciflag[ CCOL_SUM    ] = 1;
+          break;
 
+        case UI_KEY_CLUMPSMAGNITUDE:
+          name           = "CLUMPS_MAGNITUDE";
+          unit           = "log";
+          ocomment       = "Magnitude in all clumps.";
+          ccomment       = NULL;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_INVALID;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 8;
+          disp_precision = 3;
+          oiflag[ OCOL_C_SUM ] = 1;
+          p->hasmag      = 1;
+          break;
 
+        case UI_KEY_UPPERLIMIT:
+          name           = "UPPERLIMIT";
+          unit           = p->input->unit;
+          ocomment       = "Upper limit value (random positionings).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 8;
+          disp_precision = 3;
+          p->upperlimit  = 1;
+          /* Upper limit measurement doesn't need per-pixel calculations. */
+          break;
 
+        case UI_KEY_UPPERLIMITMAG:
+          name           = "UPPERLIMIT_MAG";
+          unit           = "log";
+          ocomment       = "Upper limit magnitude (random positionings).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 8;
+          disp_precision = 3;
+          p->upperlimit  = 1;
+          p->hasmag      = 1;
+          /* Upper limit magnitude doesn't need per-pixel calculations. */
+          break;
 
+        case UI_KEY_RIVERAVE:
+          name           = "RIVER_AVE";
+          unit           = p->input->unit ? p->input->unit : "pixelunit";
+          ocomment       = NULL;
+          ccomment       = "Average river value surrounding this clump.";
+          otype          = GAL_TYPE_INVALID;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 4;
+          ciflag[ CCOL_RIV_NUM ] = 1;
+          ciflag[ CCOL_RIV_SUM ] = 1;
+          break;
 
+        case UI_KEY_RIVERNUM:
+          name           = "RIVER_NUM";
+          unit           = "counter";
+          ocomment       = NULL;
+          ccomment       = "Number of river pixels around this clump.";
+          otype          = GAL_TYPE_INVALID;
+          ctype          = GAL_TYPE_INT32;
+          disp_fmt       = 0;
+          disp_width     = 5;
+          disp_precision = 0;
+          ciflag[ CCOL_RIV_NUM ] = 1;
+          break;
 
+        case UI_KEY_SN:
+          name           = "SN";
+          unit           = "ratio";
+          ocomment       = "Signal to noise ratio.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_SUMSTD ] = 1;
+          oiflag[ OCOL_NUM    ] = 1;
+          oiflag[ OCOL_SUM    ] = 1;
+          ciflag[ CCOL_SUMSTD ] = 1;
+          ciflag[ CCOL_NUM    ] = 1;
+          ciflag[ CCOL_SUM    ] = 1;
+          break;
 
+        case UI_KEY_SKY:
+          name           = "SKY";
+          unit           = p->input->unit ? p->input->unit : "pixelunit";
+          ocomment       = "Average input sky value.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 4;
+          oiflag[ OCOL_NUM    ] = 1;
+          oiflag[ OCOL_SUMSKY ] = 1;
+          ciflag[ CCOL_NUM    ] = 1;
+          ciflag[ CCOL_SUMSKY ] = 1;
+          break;
 
+        case UI_KEY_STD:
+          name           = "STD";
+          unit           = p->input->unit ? p->input->unit : "pixelunit";
+          ocomment       = "Average of input standard deviation.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 4;
+          oiflag[ OCOL_NUM    ] = 1;
+          oiflag[ OCOL_SUMSTD ] = 1;
+          ciflag[ CCOL_NUM    ] = 1;
+          ciflag[ CCOL_SUMSTD ] = 1;
+          break;
 
+        case UI_KEY_SEMIMAJOR:
+          name           = "SEMI_MAJOR";
+          unit           = "pixel";
+          ocomment       = "Flux weighted semi-major axis.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_VXX ] = 1;
+          oiflag[ OCOL_VYY ] = 1;
+          oiflag[ OCOL_VXY ] = 1;
+          ciflag[ CCOL_VXX ] = 1;
+          ciflag[ CCOL_VYY ] = 1;
+          ciflag[ CCOL_VXY ] = 1;
+          break;
 
+        case UI_KEY_SEMIMINOR:
+          name           = "SEMI_MINOR";
+          unit           = "pixel";
+          ocomment       = "Flux weighted semi-minor axis.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_VXX ] = 1;
+          oiflag[ OCOL_VYY ] = 1;
+          oiflag[ OCOL_VXY ] = 1;
+          ciflag[ CCOL_VXX ] = 1;
+          ciflag[ CCOL_VYY ] = 1;
+          ciflag[ CCOL_VXY ] = 1;
+          break;
 
+        case UI_KEY_AXISRATIO:
+          name           = "AXIS_RATIO";
+          unit           = "ratio";
+          ocomment       = "Flux weighted axis ratio (minor/major).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 7;
+          disp_precision = 3;
+          oiflag[ OCOL_VXX ] = 1;
+          oiflag[ OCOL_VYY ] = 1;
+          oiflag[ OCOL_VXY ] = 1;
+          ciflag[ CCOL_VXX ] = 1;
+          ciflag[ CCOL_VYY ] = 1;
+          ciflag[ CCOL_VXY ] = 1;
+          break;
 
+        case UI_KEY_POSITIONANGLE:
+          name           = "POSITION_ANGLE";
+          unit           = "degrees";
+          ocomment       = "Position angle.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_VXX ] = 1;
+          oiflag[ OCOL_VYY ] = 1;
+          oiflag[ OCOL_VXY ] = 1;
+          ciflag[ CCOL_VXX ] = 1;
+          ciflag[ CCOL_VYY ] = 1;
+          ciflag[ CCOL_VXY ] = 1;
+          break;
 
+        case UI_KEY_GEOSEMIMAJOR:
+          name           = "GEO_SEMI_MAJOR";
+          unit           = "pixel";
+          ocomment       = "Geometric semi-major axis.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_GXX ] = 1;
+          oiflag[ OCOL_GYY ] = 1;
+          oiflag[ OCOL_GXY ] = 1;
+          ciflag[ CCOL_GXX ] = 1;
+          ciflag[ CCOL_GYY ] = 1;
+          ciflag[ CCOL_GXY ] = 1;
+          break;
 
+        case UI_KEY_GEOSEMIMINOR:
+          name           = "GEO_SEMI_MINOR";
+          unit           = "pixel";
+          ocomment       = "Geometric semi-minor axis.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_GXX ] = 1;
+          oiflag[ OCOL_GYY ] = 1;
+          oiflag[ OCOL_GXY ] = 1;
+          ciflag[ CCOL_GXX ] = 1;
+          ciflag[ CCOL_GYY ] = 1;
+          ciflag[ CCOL_GXY ] = 1;
+          break;
 
-/******************************************************************/
-/***************    Add columns for printing    *******************/
-/******************************************************************/
-void
-idcol(struct mkcatalogparams *p)
-{
-  size_t i;
+        case UI_KEY_GEOAXISRATIO:
+          name           = "GEO_AXIS_RATIO";
+          unit           = "ratio";
+          ocomment       = "Geometric axis ratio (minor/major).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 7;
+          disp_precision = 3;
+          oiflag[ OCOL_VXX ] = 1;
+          oiflag[ OCOL_VYY ] = 1;
+          oiflag[ OCOL_VXY ] = 1;
+          ciflag[ CCOL_VXX ] = 1;
+          ciflag[ CCOL_VYY ] = 1;
+          ciflag[ CCOL_VXY ] = 1;
+          break;
 
-  p->unitp=CATUNITCOUNTER;
-  sprintf(p->description, "%zu: Overall %s ID",
-          p->curcol, p->name);
+        case UI_KEY_GEOPOSITIONANGLE:
+          name           = "GEO_POSITION_ANGLE";
+          unit           = "degrees";
+          ocomment       = "Geometric Position angle.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_FLOAT;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_GXX ] = 1;
+          oiflag[ OCOL_GYY ] = 1;
+          oiflag[ OCOL_GXY ] = 1;
+          ciflag[ CCOL_GXX ] = 1;
+          ciflag[ CCOL_GYY ] = 1;
+          ciflag[ CCOL_GXY ] = 1;
+          break;
 
-  for(i=0;i<p->num;++i)
-    p->cat[ i * p->numcols + p->curcol ] = i+1;
+        default:
+          error(EXIT_FAILURE, 0, "a bug! please contact us at %s to fix the "
+                "problem. The code %d is not an internally recognized "
+                "column code, this is due to some mistake in the programming",
+                PACKAGE_BUGREPORT, colcode->v);
+        }
 
-  p->intcols[p->intcounter++]=p->curcol;
-}
+      /* If this is an objects column, add it to the list of columns. We
+         will be using the `status' element to keep the MakeCatalog code
+         for the columns. */
+      if(otype!=GAL_TYPE_INVALID)
+        {
+          gal_data_add_to_ll(&p->objectcols, NULL, otype, 1, &p->numobjects,
+                             NULL, 0, p->cp.minmapsize, name, unit, ocomment);
+          p->objectcols->status         = colcode->v;
+          p->objectcols->disp_fmt       = disp_fmt;
+          p->objectcols->disp_width     = disp_width;
+          p->objectcols->disp_precision = disp_precision;
+        }
 
 
+      /* Similar to the objects column above but for clumps, but since the
+         clumps image is optional, we need a further check before actually
+         allocating the column. */
+      if(ctype!=GAL_TYPE_INVALID)
+        {
+          /* A clumps image has been given, so allocate space for this
+             column. */
+          if(p->clumps)
+            {
+              gal_data_add_to_ll(&p->clumpcols, NULL, ctype, 1, &p->numclumps,
+                                 NULL, 0, p->cp.minmapsize, name, unit,
+                                 ccomment);
+              p->clumpcols->status         = colcode->v;
+              p->clumpcols->disp_fmt       = disp_fmt;
+              p->clumpcols->disp_width     = disp_width;
+              p->clumpcols->disp_precision = disp_precision;
+            }
 
 
+          /* If this is a clumps-only column and no clumps image was
+             given. Add the column to the list of similar columns to inform
+             the user. */
+          else if(otype==GAL_TYPE_INVALID)
+            gal_linkedlist_add_to_stll(&noclumpimg, name, 1);
+        }
+    }
 
-/* Store IDs related to the host object:
 
-   o1c0==1 --> hostobjid: The ID of object hosting this clump
-   o1c0==0 --> idinhostobj: The ID of clump in object
- */
-void
-hostobj(struct mkcatalogparams *p, int o1c0)
-{
-  char *des;
-  double counter;
-  size_t i, j, n, row=0;
-
-  p->unitp=CATUNITCOUNTER;
-  des = ( o1c0 ? "ID of object hosting this clump"
-          : "ID of clump in host object" );
-  sprintf(p->description, "%zu: %s.",
-          p->curcol, des);
-
-  for(i=1;i<=p->numobjects;++i)
-    if( (n=p->oinfo[i*OCOLUMNS+ONCLUMPS]) > 0.0f)
-      {
-        counter=1.0f;
-        for(j=row;j<row+n;++j)
-          p->cat[ j * p->numcols + p->curcol] = o1c0 ? i : counter++;
-        row+=n;
-      }
+  /* If a warning for clumps columns and no clumps image is necessary make
+     the warning. */
+  if(noclumpimg)
+    {
+      gal_linkedlist_reverse_stll(&noclumpimg);
+      fprintf(stderr, "\n-------\n"
+              "WARNING: the following column(s) are unique to "
+              "clumps (not objects), but the objects image doesn't have "
+              " `WCLUMPS' keyword. So these requested columns will be "
+              "ignored.\n\n");
+      for(strtmp=noclumpimg; strtmp!=NULL; strtmp=strtmp->next)
+        fprintf(stderr, "\t%s\n", strtmp->v);
+      gal_linkedlist_free_stll(noclumpimg, 1);
+      fprintf(stderr, "\n-------\n");
+    }
+
 
-  p->intcols[p->intcounter++]=p->curcol;
+  /* Free the general columns information because it is no longe needed,
+     we'll set it back to NULL afterwards so it is not mistakenly used. */
+  gal_linkedlist_free_ill(p->columnids);
+  p->columnids=NULL;
 }
 
 
 
 
 
-void
-numclumps(struct mkcatalogparams *p)
-{
-  size_t i;
 
-  p->unitp=CATUNITCOUNTER;
-  sprintf(p->description, "%zu: Number of clumps in this object.",
-          p->curcol);
 
-  for(i=0;i<p->numobjects;++i)
-    p->cat[i * p->numcols + p->curcol ] = p->oinfo[(i+1)*OCOLUMNS+ONCLUMPS];
 
-  p->intcols[p->intcounter++]=p->curcol;
-}
 
 
 
 
 
-void
-area(struct mkcatalogparams *p, int cinobj, int isriver)
-{
-  char *type;
-  size_t i, col;
 
-  /* Set the proper column to use */
-  if(p->obj0clump1)
-    {
-      if(isriver)
-        {
-          type="Number of river pixels around this clump";
-          col=CRivArea;
-        }
-      else
-        {
-          type = isnan(p->threshold)
-            ? "Area of this clump"
-            : "Area of this clump above threshold";
-          col = CAREA;
-        }
-    }
-  else
-    {
-      if(cinobj)    /* It is the positions of clumps in object. */
-        {
-          type="Clumps in object area";
-          col = OAREAC;
-        }
-      else          /* It is the position of the object itsself.*/
-        {
-          type="Full object area";
-          col = OAREA;
-        }
-    }
 
-  /* Set the unit and print the header. */
-  p->unitp = isriver ? CATUNITCOUNTER : CATUNITPIXAREA;
-  sprintf(p->description, "%zu: %s.", p->curcol, type);
 
-  /* Fill in the output array. */
-  for(i=0;i<p->num;++i)
-    p->cat[i * p->numcols + p->curcol ] = p->info[(i+1)*p->icols+col];
 
-  /* Set the precision for printing. */
-  p->intcols[p->intcounter++]=p->curcol;
-}
 
 
 
+/******************************************************************/
+/**********            Column calculation           ***************/
+/******************************************************************/
+#define MKC_RATIO(TOP,BOT) ( (BOT)!=0.0f ? (TOP)/(BOT) : NAN )
+#define MKC_MAG(B)         ( ((B)>0) ? -2.5f * log10(B) + p->zeropoint : NAN )
 
 
-void
-position(struct mkcatalogparams *p, size_t col, char *target,
-         char *type, char *axis)
-{
-  size_t i;
-  int wcsax = ( !strcmp(axis, MKCATRA) || !strcmp(axis, MKCATDEC) ) ? 1 : 0;
 
-  /* Set the header information. */
-  sprintf(p->description, "%zu: %s %s (%s).", p->curcol, target, type, axis);
 
-  /* Prepare the respective column, set the units and also the
-     printing accuracy if we are in RA/Dec mode (wcsax==1). */
-  if(wcsax)
+
+/* Calculate the Signal to noise ratio for the object. */
+static double
+columns_sn(struct mkcatalogparams *p, double *row, int o0c1)
+{
+  double var, sn, std, Ni, I, O;
+
+  /* Get all the values as averages (per pixel). */
+  Ni  = row[ o0c1 ? CCOL_NUM : OCOL_NUM ];
+  I   = MKC_RATIO( row[ o0c1 ? CCOL_SUM    : OCOL_SUM ],    Ni );
+  std = MKC_RATIO( row[ o0c1 ? CCOL_SUMSTD : OCOL_SUMSTD ], Ni );
+  var = (p->skysubtracted ? 2.0f : 1.0f) * std * std;
+
+  /* Calculate the S/N. Note that when grown clumps are requested from
+     NoiseChisel, some "clumps" will completely cover their objects and
+     there will be no rivers. So if this is a clump, and the river area is
+     0, we should treat the S/N as a an object. */
+  if( o0c1 && row[ CCOL_RIV_NUM ] )
     {
-      /* Run the respective function to prepare the information table,
-         then set the units and print accuracy. */
-      preparewcs(p, col);
-      p->unitp = CATUNITDEGREE;
-      p->accucols[p->accucounter++]=p->curcol;
+      /* If the Sky is already subtracted, the varience should be counted
+         two times. */
+      O   = row[ CCOL_RIV_SUM ] / row[ CCOL_RIV_NUM ];  /* Outside.  */
+      sn  = ( sqrt(Ni/p->cpscorr) * (I-O)
+              / sqrt( (I>0?I:-1*I) + (O>0?O:-1*O) + var ) );
     }
   else
-    {
-      /* Run the respective function to prepare the information table. */
-      if(!strcmp(type, MKCATGEOC))
-        geoxy(p, col);
-      else if(!strcmp(type, MKCATWHTC))
-        flxwhtimg(p, col);
-      else
-        error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we can "
-              "solve this problem. The value to `type' (%s) is not "
-              "recognized in position (image mode)", PACKAGE_BUGREPORT,
-              type);
-
-      /* Set the units. */
-      p->unitp = CATUNITPIXLENGTH;
-    }
+    sn  = sqrt(Ni/p->cpscorr) * I / sqrt( (I>0?I:-1*I) + var );
 
-  /* Write respective column of the information table into the output. */
-  for(i=0;i<p->num;++i)
-    p->cat[i * p->numcols + p->curcol ] = p->info[(i+1)*p->icols+col];
+  /* Return the derived value. */
+  return sn;
 }
 
 
 
 
 
-/* Note that here, the output column is used, not the input one. */
-void
-secondordermoment(struct mkcatalogparams *p, size_t ocol, char *target)
+/* Do the second order calculations, see "Measuring elliptical parameters"
+   section of the book/manual for a thorough explanation of the
+   derivation. */
+static double
+columns_second_order(struct mkcatalog_passparams *pp, double *row,
+                     int key, int o0c1)
 {
-  double a, *row;
-  char *name=NULL, *type=NULL;
-  size_t i, xxc=-1, yyc=-1, xyc=-1;
+  double x=NAN, y=NAN, xx=NAN, yy=NAN, xy=NAN;
+  double denom, kx=pp->shift[1]+1, ky=pp->shift[0]+1;
 
-  /* Set the necessary columns, and the type of output: */
-  switch(ocol)
+  /* Preparations. */
+  switch(key)
     {
-    /* The brightness weighted second moments. */
-    case CATSEMIMAJOR: case CATSEMIMINOR: case CATPOSITIONANGLE:
-      type="weighted";
-      if(p->obj0clump1) {xxc=CFlxWhtXX; yyc=CFlxWhtYY; xyc=CFlxWhtXY;}
-      else              {xxc=OFlxWhtXX; yyc=OFlxWhtYY; xyc=OFlxWhtXY;}
+    /* Brightness weighted. */
+    case UI_KEY_SEMIMAJOR:
+    case UI_KEY_SEMIMINOR:
+    case UI_KEY_POSITIONANGLE:
+
+      /* Denominator (to be divided). */
+      denom = row[ o0c1 ? CCOL_SUMPOS : OCOL_SUMPOS ];
+
+      /* First order. */
+      x  = MKC_RATIO( row[ o0c1 ? CCOL_VX     : OCOL_VX     ], denom );
+      y  = MKC_RATIO( row[ o0c1 ? CCOL_VY     : OCOL_VY     ], denom );
+
+      /* Second order. */
+      xx = ( MKC_RATIO( row[ o0c1 ? CCOL_VXX    : OCOL_VXX    ], denom )
+             - (x-kx) * (x-kx) );
+      yy = ( MKC_RATIO( row[ o0c1 ? CCOL_VYY    : OCOL_VYY    ], denom )
+             - (y-ky) * (y-ky) );
+      xy = ( MKC_RATIO( row[ o0c1 ? CCOL_VXY    : OCOL_VXY    ], denom )
+             - (x-kx) * (y-ky) );
       break;
 
-    /* The geometric second moments. */
-    case CATGEOSEMIMAJOR: case CATGEOSEMIMINOR: case CATGEOPOSITIONANGLE:
-      type="geometric";
-      if(p->obj0clump1) {xxc=CGeoXX; yyc=CGeoYY; xyc=CGeoXY;}
-      else              {xxc=OGeoXX; yyc=OGeoYY; xyc=OGeoXY;}
+    /* Geometric. */
+    case UI_KEY_GEOSEMIMAJOR:
+    case UI_KEY_GEOSEMIMINOR:
+    case UI_KEY_GEOPOSITIONANGLE:
+
+      /* Denominator (to be divided). */
+      denom = row[ o0c1 ? CCOL_NUM : OCOL_NUM ];
+
+      /* First order. */
+      x  = MKC_RATIO( row[ o0c1 ? CCOL_GX  : OCOL_GX  ], denom );
+      y  = MKC_RATIO( row[ o0c1 ? CCOL_GY  : OCOL_GY  ], denom );
+
+      /* Second order. */
+      xx = ( MKC_RATIO( row[ o0c1 ? CCOL_GXX : OCOL_GXX ], denom )
+             - (x-kx) * (x-kx) );
+      yy = ( MKC_RATIO( row[ o0c1 ? CCOL_GYY : OCOL_GYY ], denom )
+             - (y-ky) * (y-ky) );
+      xy = ( MKC_RATIO( row[ o0c1 ? CCOL_GXY : OCOL_GXY ], denom )
+             - (x-kx) * (y-ky) );
       break;
 
-    /* Output column not recognized */
+    /* Error. */
     default:
-      error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we can "
-            "solve this problem. The value to `ocol' (%zu) is not "
-            "recognized in secondordermoment (first)", PACKAGE_BUGREPORT,
-            ocol);
+      error(EXIT_FAILURE, 0, "a bug! Code %d not a recognized key in "
+            "`columns_second_order'", key);
     }
 
-  /* Prepare the columns which will be needed in the next step. */
-  setsecondmoment(p, xxc);
-  setsecondmoment(p, yyc);
-  setsecondmoment(p, xyc);
-
-  /* Set output name and do the calculation, the calculations are
-     taken from the SExtractor manual. */
-  switch(ocol)
+  /* Return the output. */
+  switch(key)
     {
-    case CATSEMIMAJOR: case CATGEOSEMIMAJOR:
-      name="semi major axis";
-      p->unitp = CATUNITPIXLENGTH;
-      for(i=0;i<p->num;++i)
-        {
-          row = p->info + (i+1)*p->icols;
-          a=(row[xxc]-row[yyc])*(row[xxc]-row[yyc])/4;
-          p->cat[i * p->numcols + p->curcol ] =
-            sqrt( (row[xxc]+row[yyc])/2 + sqrt( a + row[xyc]*row[xyc]) );
-        }
-      break;
-    case CATSEMIMINOR: case CATGEOSEMIMINOR:
-      name="semi minor axis";
-      p->unitp = CATUNITPIXLENGTH;
-      for(i=0;i<p->num;++i)
-        {
-          row = p->info + (i+1)*p->icols;
-          a=(row[xxc]-row[yyc])*(row[xxc]-row[yyc])/4;
-          p->cat[i * p->numcols + p->curcol ] =
-            sqrt( (row[xxc]+row[yyc])/2 - sqrt( a + row[xyc]*row[xyc]) );
-        }
-      break;
-    case CATPOSITIONANGLE: case CATGEOPOSITIONANGLE:
-      name="position angle";
-      p->unitp = CATUNITDEGREE;
-      for(i=0;i<p->num;++i)
-        {
-          row = p->info + (i+1)*p->icols;
-          p->cat[i * p->numcols + p->curcol ] =
-            0.5*atan2(2*row[xyc], row[xxc]-row[yyc]) * 180/M_PI;
-        }
-      break;
-    default:
-      error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we can "
-            "solve this problem. The value to `ocol' (%zu) is not "
-            "recognized in secondordermoment (second)", PACKAGE_BUGREPORT,
-            ocol);
+    /* Semi-major axis. */
+    case UI_KEY_SEMIMAJOR:
+    case UI_KEY_GEOSEMIMAJOR:
+      return sqrt( ( xx + yy ) / 2
+                   + sqrt( (xx - yy)/2 * (xx - yy)/2 + xy * xy ) );
+
+    /* Semi-minor axis. */
+    case UI_KEY_SEMIMINOR:
+    case UI_KEY_GEOSEMIMINOR:
+      /*printf("\nhere\n");*/
+      return sqrt( ( xx + yy )/2
+                   - sqrt( (xx - yy)/2 * (xx - yy)/2 + xy * xy ) );
+
+    /* Position angle. */
+    case UI_KEY_POSITIONANGLE:
+    case UI_KEY_GEOPOSITIONANGLE:
+      return 0.5f * atan2(2 * xy, xx - yy) * 180/M_PI;
     }
 
-  /* Set the header value */
-  sprintf(p->description, "%zu: %s %s %s.", p->curcol, target, type, name);
+
+  /* Control should not reach here! If it does, its a bug, so abort and let
+     the user know. */
+  error(EXIT_FAILURE, 0, "a bug! control has reached the end of "
+        "`columns_second_order' (which should not have happened). Please "
+        "contact us at %s, so we can address the problem", PACKAGE_BUGREPORT);
+  return NAN;
 }
 
 
 
 
 
-void
-brightnessmag(struct mkcatalogparams *p, size_t col, char *target,
-              char *scale)
-{
-  size_t i;
-  char *add;
-  double bright, *value;
-
-  /* Prepare other necessary columns */
-  if( !strcmp(MKRIVERSSUR, target) )
-    setaveriver(p);
-  if( !strcmp(MKCATCLUMP, target) && col!=CNoRiverBrightness )
-    setclumpbrightness(p);
-
-  /* Fill the output columns: */
-  for(i=0;i<p->num;++i)
-    {
+/* The magnitude error is directly derivable from the S/N:
 
-      /* Set the basic values: */
-      bright = p->info[ (i+1) * p->icols + col ];
-      value  = &p->cat[i * p->numcols + p->curcol ];
-
-
-      /* Do the job: */
-      if(!strcmp(MKCATMAG, scale))
-        *value = bright<=0.0f ? NAN : -2.5f*log10(bright)+p->zeropoint;
-      else if(!strcmp(MKCATBRIGHT, scale))
-        *value = bright;
-      else
-        error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we can "
-              "fix this issue. For some reason, the value to `scale' in"
-              "brightnessfluxmag (columns.c) is `%s', which is not "
-              "recognized", PACKAGE_BUGREPORT, scale);
-    }
+   To derive the error in measuring the magnitude from the S/N, let's take
+   `F' as the flux, `Z' is the zeropoint, `M' is the magnitude, `S' is the
+   S/N, and `D' to stand for capital delta (or error in a value) then from
 
-  /* Make final preparations for output. When dealing with the average
-     river value, set the accuracy to high, also set the units to
-     average values (per pixel). */
-  if(!strcmp(MKRIVERSSUR, target))
-    {
-      p->unitp = CATUNITAVE;
-      p->accucols[p->accucounter++]=p->curcol;
-    }
-  else
-    p->unitp = strcmp(MKCATMAG, scale) ? CATUNITBRIGHTNESS : CATUNITMAG;
+      `M = -2.5*log10(F) + Z'
 
-  /* Set the header information: */
-  add = (col==CNoRiverBrightness) ? " sky (not river) subtracted " : " ";
-  sprintf(p->description, "%zu: %s%s%s.", p->curcol, target, add, scale);
-}
+   we get the following equation after calculating the derivative with
+   respect to F.
+
+      `dM/df = -2.5 * ( 1 / ( F * ln(10) ) )'
+
+   From the Tailor series, `DM' can be written as:
 
+      `DM = dM/dF * DF'
 
+   So
 
+      `DM = |-2.5/ln(10)| * DF/F'
 
+   But `DF/F' is just the inverse of the Signal to noise ratio, or
+  `1/S'. So
 
+      `DM = 2.5 / ( S * ln(10) )'               */
+#define MAG_ERROR(P,ROW,O0C1) (2.5f/(columns_sn((P),(ROW),(O0C1)) * log(10)))
+
+
+
+
+
+
+/* All the raw first and second pass information has been collected, now
+   write them into the output columns. The list of columns here is in the
+   same order as `columns_alloc_set_out_cols', see there for the type of
+   each column. */
 void
-upperlimitcol(struct mkcatalogparams *p)
+columns_fill(struct mkcatalog_passparams *pp)
 {
-  size_t i;
-  float *std;
-  double *ptr;
+  struct mkcatalogparams *p=pp->p;
+
+  int key;
+  double tmp;
+  void *colarr;
+  gal_data_t *column;
+  double *ci, *oi=pp->oi;
+  size_t sr=pp->clumpstartindex, cind, coind;
+  size_t oind=pp->object-1; /* IDs start from 1, indexs from 0. */
+
+  /* Go over all the object columns and fill in the information. */
+  for(column=p->objectcols; column!=NULL; column=column->next)
+    {
+      /* For easy reading. */
+      key=column->status;
+      colarr=column->array;
 
-  /* For the comments: */
-  p->unitp = CATUNITMAG;
-  sprintf(p->description, "%zu: Upper limit magnitude for this %s.",
-          p->curcol, p->name);
+      /* Go over all the columns. */
+      switch(key)
+        {
+        case UI_KEY_OBJID:
+          ((int32_t *)colarr)[oind] = pp->object;
+          break;
 
+        case UI_KEY_NUMCLUMPS:
+          ((int32_t *)colarr)[oind] = pp->clumpsinobj;
+          break;
 
-  /* Correct the raw values (divide them by area) if not already
-     done. */
-  std=upperlimit(p->img, p->sky, p->objects, p->upmask, p->s0, p->s1,
-                 p->upnum, p->cp.numthreads, p->envseed, p->upsclipmultip,
-                 p->upsclipaccu);
+        case UI_KEY_AREA:
+          ((int32_t *)colarr)[oind] = oi[OCOL_NUM];
+          break;
 
+        case UI_KEY_CLUMPSAREA:
+          ((int32_t *)colarr)[oind] = oi[OCOL_C_NUM];
+          break;
 
-  /* Write the standard deviations values in the final catalog as
-     magnitudes. */
-  for(i=0;i<p->num;++i)
-    {
-      /* `ptr' is defined for a short/readable line. */
-      ptr  = &p->cat[i * p->numcols + p->curcol ];
-      *ptr =  -2.5f * log10( p->upnsigma*std[i+1] ) + p->zeropoint;
-    }
+        case UI_KEY_X:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_VX], oi[OCOL_SUMPOS] );
+          break;
 
-  free(std);
-}
+        case UI_KEY_Y:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_VY], oi[OCOL_SUMPOS] );
+          break;
 
+        case UI_KEY_GEOX:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_GX], oi[OCOL_NUM] );
+          break;
 
+        case UI_KEY_GEOY:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_GY], oi[OCOL_NUM] );
+          break;
 
+        case UI_KEY_CLUMPSX:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_C_VX],
+                                               oi[OCOL_C_SUMPOS] );
+          break;
 
+        case UI_KEY_CLUMPSY:
+          ((float *)colarr)[oind] = MKC_RATIO(oi[OCOL_C_VY],
+                                              oi[OCOL_C_SUMPOS] );
+          break;
 
-void
-skystd(struct mkcatalogparams *p, size_t col)
-{
-  size_t i;
-
-  /* For the comments: */
-  p->unitp = CATUNITAVE;
-  sprintf(p->description, "%zu: Average %s under this %s.",
-          p->curcol, ( (col==OSKY || col==CSKY)
-                       ? "sky" : "sky standard deviation" ),
-          p->name);
-
-  /* Correct the raw values (divide them by area) if not already
-     done. */
-  setskystd(p, col);
-
-  /* Fill the sky value, note that in the information array, we have
-     only calculated the sum. So here, we need to divide by the area
-     to find the average. */
-  for(i=0;i<p->num;++i)
-    p->cat[i * p->numcols + p->curcol ] = p->info[(i+1)*p->icols+col];
-
-  /* This column should be accurate: */
-  p->accucols[p->accucounter++]=p->curcol;
-}
+        case UI_KEY_CLUMPSGEOX:
+          ((float *)colarr)[oind] = MKC_RATIO(oi[OCOL_C_GX], oi[OCOL_C_NUM]);
+          break;
 
+        case UI_KEY_CLUMPSGEOY:
+          ((float *)colarr)[oind] = MKC_RATIO(oi[OCOL_C_GY], oi[OCOL_C_NUM]);
+          break;
 
+        case UI_KEY_RA:
+        case UI_KEY_DEC:
+          p->rd_vo[0][oind] = MKC_RATIO( oi[OCOL_VX], oi[OCOL_SUMPOS] );
+          p->rd_vo[1][oind] = MKC_RATIO( oi[OCOL_VY], oi[OCOL_SUMPOS] );
+          break;
 
+        case UI_KEY_GEORA:
+        case UI_KEY_GEODEC:
+          p->rd_go[0][oind] = MKC_RATIO( oi[OCOL_GX], oi[OCOL_NUM] );
+          p->rd_go[1][oind] = MKC_RATIO( oi[OCOL_GY], oi[OCOL_NUM] );
+          break;
 
+        case UI_KEY_CLUMPSRA:
+        case UI_KEY_CLUMPSDEC:
+          p->rd_vcc[0][oind] = MKC_RATIO( oi[OCOL_C_VX], oi[OCOL_C_SUMPOS] );
+          p->rd_vcc[1][oind] = MKC_RATIO( oi[OCOL_C_VY], oi[OCOL_C_SUMPOS] );
+          break;
 
-void
-sncol(struct mkcatalogparams *p, int sn0_magerr1, char *target)
-{
-  size_t i;
-  double sn, I, O, Ni, errpt, *row;
-  size_t stdcol        = p->obj0clump1 ? CSTD        : OSTD;
-  size_t areacol       = p->obj0clump1 ? CAREA       : OAREA;
-  size_t brightnesscol = p->obj0clump1 ? CBrightness : OBrightness;
-
-  /* Do the corrections:
-
-       1. If we are dealing with clumps, make sure the clump
-          brightness is corrected first.
-
-       2. Make sure the STD values are corrected in any case. */
-  setskystd(p, stdcol);
-  if(p->obj0clump1)
-    setclumpbrightness(p);
-
-  /* For the comments: */
-  p->unitp = sn0_magerr1 ? CATUNITMAG : CATUNITRATIO;
-  if(sn0_magerr1)
-    sprintf(p->description, "%zu: %s Magnitude error.", p->curcol,
-            target);
-  else
-    sprintf(p->description, "%zu: %s signal to noise ratio.", p->curcol,
-            target);
+        case UI_KEY_CLUMPSGEORA:
+        case UI_KEY_CLUMPSGEODEC:
+          p->rd_gcc[0][oind] = MKC_RATIO( oi[OCOL_C_GX], oi[OCOL_C_NUM] );
+          p->rd_gcc[1][oind] = MKC_RATIO( oi[OCOL_C_GY], oi[OCOL_C_NUM] );
+          break;
 
-  /* Calculate the signal to noise ratio. Recall that for the objects,
-     the sky value was subtracted from oinfo, but for the clumps, it
-     was not subtracted. */
-  for(i=0;i<p->num;++i)
-    {
-      /* Some convenience variables to make things readable. */
-      row = p->info + ((i+1)*p->icols);     /* Pointer to this row.       */
-      Ni  = row[ areacol ];                 /* Number-in                  */
-      I   = row[ brightnesscol ]/Ni;        /* Inner brightness (average) */
-      errpt = row[ stdcol ]*row[ stdcol ];  /* error-to-power-two         */
-
-      /* If we are on a clump and there are actually rivers (NOTE: it
-         is possible that there are no rivers, see the NoiseChisel
-         dropout paper). In short, they are actually objects with no
-         more than one clump. So, NoiseChisel parameters were set such
-         that the objects also show up in the clumps labels. */
-      if(p->obj0clump1 && row[ CRivArea ]>0.0f)
-        {
+        case UI_KEY_BRIGHTNESS:
+          ((float *)colarr)[oind] = oi[ OCOL_SUM ];
+          break;
 
-          /* Another convenience variable. */
-          O=row[ CRivAve ];                 /* Outer brightness (average)*/
-
-          /* Modify the error based on the conditions. Note that the
-             inner flux has already been subtracted from the average
-             outer flux multiplied by the clump area in
-             setclumpbrightness and was divided by the clump area
-             above. It is also in per pixel units. row[CRivAve] is
-             also in per pixel units. So simply by adding the two, we
-             get the per pixel flux within the clump before removing
-             the average river value.
-
-             If the image was already Sky subtracted, then the Sky
-             error^2 (=err) must be multiplied by 2 (we have
-             implicitly used it in both estimating the inner and outer
-             fluxes). Otherwise, it is multiplied by 0.0f, since we
-             don't care because we are not using the Sky value
-             here. */
-          errpt = ( (   I+O > 0.0f ? I+O : 0.0f )
-                    + ( O   > 0.0f ? O   : 0.0f )
-                    + errpt * (p->skysubtracted ? 2.0f : 0.0f) );
-        }
-      else
-        {
-          /* When the flux is negative (can easily happen in matched
-             photometry), then ignore the error in flux (the S/N is
-             meaningless anyway) and just keep the Sky error.
-
-             When the image was already Sky subtracted, we need two
-             errpt terms, because the error in the previous Sky
-             subtraction must also be included. */
-          errpt = ( ( I>0 ? I : 0 )
-                    + errpt * (p->skysubtracted ? 1.0f : 2.0f) );
-        }
+        case UI_KEY_CLUMPSBRIGHTNESS:
+          ((float *)colarr)[oind] = oi[ OCOL_C_SUM ];
+          break;
 
-      /* Fill in the output column. Note that magnitude error is directly
-         derivable from the S/N:
+        case UI_KEY_MAGNITUDE:
+          ((float *)colarr)[oind] = MKC_MAG(oi[ OCOL_SUM ]);
+          break;
 
-         To derive the error in measuring the magnitude from the S/N, let's
-         take `F' as the flux, `Z' is the zeropoint, `M' is the magnitude,
-         `S' is the S/N, and `D' to stand for capital delta (or error in a
-         value) then from
+        case UI_KEY_MAGNITUDEERR:
+          ((float *)colarr)[oind] = MAG_ERROR(p, oi, 0);
+          break;
 
-              `M = -2.5*log10(F) + Z'
+        case UI_KEY_CLUMPSMAGNITUDE:
+          ((float *)colarr)[oind] = MKC_MAG(oi[ OCOL_C_SUM ]);
+          break;
 
-         we get the following equation after calculating the derivative
-         with respect to F.
+        case UI_KEY_UPPERLIMIT:
+          ((float *)colarr)[oind] = oi[ OCOL_UPPERLIMIT_B ];
+          break;
 
-              `dM/df = -2.5 * ( 1 / ( F * ln(10) ) )'
+        case UI_KEY_UPPERLIMITMAG:
+          ((float *)colarr)[oind] = MKC_MAG(oi[ OCOL_UPPERLIMIT_B ]);
+          break;
 
-         From the Tailor series, `DM' can be written as:
+        case UI_KEY_SN:
+          ((float *)colarr)[oind] = columns_sn(p, oi, 0);
+          break;
 
-              `DM = dM/dF * DF'
+        case UI_KEY_SKY:
+          ((float *)colarr)[oind] = MKC_RATIO(oi[OCOL_SUMSKY], oi[OCOL_NUM]);
+          break;
 
-         So
+        case UI_KEY_STD:
+          ((float *)colarr)[oind] = MKC_RATIO(oi[OCOL_SUMSTD], oi[OCOL_NUM]);
+          break;
+
+        case UI_KEY_SEMIMAJOR:
+          ((float *)colarr)[oind] = columns_second_order(pp, oi, key, 0);
+          break;
+
+        case UI_KEY_SEMIMINOR:
+          ((float *)colarr)[oind] = columns_second_order(pp, oi, key, 0);
+          break;
+
+        case UI_KEY_AXISRATIO:
+          ((float *)colarr)[oind]
+            = ( columns_second_order(pp, oi, UI_KEY_SEMIMINOR, 0)
+                / columns_second_order(pp, oi, UI_KEY_SEMIMAJOR, 0) );
+          break;
+
+        case UI_KEY_POSITIONANGLE:
+          ((float *)colarr)[oind] = columns_second_order(pp, oi, key, 0);
+          break;
+
+        case UI_KEY_GEOSEMIMAJOR:
+          ((float *)colarr)[oind] = columns_second_order(pp, oi, key, 0);
+          break;
+
+        case UI_KEY_GEOSEMIMINOR:
+          ((float *)colarr)[oind] = columns_second_order(pp, oi, key, 0);
+          break;
 
-              `DM = |-2.5/ln(10)| * DF/F'
+        case UI_KEY_GEOAXISRATIO:
+          ((float *)colarr)[oind]
+            = ( columns_second_order(pp, oi, UI_KEY_GEOSEMIMINOR, 0)
+                / columns_second_order(pp, oi, UI_KEY_GEOSEMIMAJOR, 0) );
+          break;
 
-         But `DF/F' is just the inverse of the Signal to noise ratio, or
-         `1/S'. So
+        case UI_KEY_GEOPOSITIONANGLE:
+          ((float *)colarr)[oind] = columns_second_order(pp, oi, key, 0);
+          break;
 
-              `DM = 2.5 / ( S * ln(10) )'
-      */
-      sn = sqrt(Ni/p->cpscorr)*I / sqrt( errpt );
-      p->cat[i * p->numcols + p->curcol ] = ( sn0_magerr1
-                                              ? ( 2.5 / (sn*log(10)) )
-                                              : sn );
+        default:
+          error(EXIT_FAILURE, 0, "a bug! the output column code %d not "
+                "recognized in `mkcatalog_fill_output_columns' (for "
+                "objects). Please contact us at %s to solve the problem",
+                key, PACKAGE_BUGREPORT);
+        }
     }
 
+  /* Go over the clump columns and fill the information. */
+  for(column=p->clumpcols; column!=NULL; column=column->next)
+    for(coind=0;coind<pp->clumpsinobj;++coind)
+      {
+        /* `coind': clump-in-object-index.
+           `cind': clump-index (over all the catalog). */
+        cind   = sr + coind;
+        colarr = column->array;
+        key    = column->status;
+        ci     = &pp->ci[ coind * CCOL_NUMCOLS ];
+
+        /* Parse columns */
+        switch(key)
+          {
+          case UI_KEY_HOSTOBJID:
+            ((int32_t *)colarr)[cind]=pp->object;
+            break;
+
+          case UI_KEY_IDINHOSTOBJ:
+            ((int32_t *)colarr)[cind]=coind+1;
+            break;
+
+          case UI_KEY_AREA:
+            ((int32_t *)colarr)[cind]=ci[CCOL_NUM];
+            break;
+
+          case UI_KEY_X:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_VX],
+                                                 ci[CCOL_SUMPOS] );
+            break;
+
+          case UI_KEY_Y:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_VY],
+                                                 ci[CCOL_SUMPOS] );
+            break;
+
+          case UI_KEY_GEOX:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_GX], ci[CCOL_NUM] );
+            break;
+
+          case UI_KEY_GEOY:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_GY], ci[CCOL_NUM] );
+            break;
+
+          case UI_KEY_RA:
+          case UI_KEY_DEC:
+            p->rd_vc[0][cind] = MKC_RATIO( ci[CCOL_VX], ci[CCOL_SUMPOS] );
+            p->rd_vc[1][cind] = MKC_RATIO( ci[CCOL_VY], ci[CCOL_SUMPOS] );
+            break;
+
+          case UI_KEY_GEORA:
+          case UI_KEY_GEODEC:
+            p->rd_gc[0][cind] = MKC_RATIO( ci[CCOL_GX], ci[CCOL_NUM] );
+            p->rd_gc[1][cind] = MKC_RATIO( ci[CCOL_GY], ci[CCOL_NUM] );
+            break;
+
+          case UI_KEY_BRIGHTNESS:
+            /* Calculate the river flux over the clump area. */
+            tmp = ci[ CCOL_RIV_SUM ]/ci[ CCOL_RIV_NUM ]*ci[ CCOL_NUM ];
+
+            /* Subtract it from the clump's brightness. */
+            ((float *)colarr)[cind] = ci[ CCOL_SUM ] - tmp;
+            break;
+
+          case UI_KEY_NORIVERBRIGHTNESS:
+            ((float *)colarr)[cind] = ci[ CCOL_SUM ];
+            break;
+
+          case UI_KEY_MAGNITUDE: /* Similar: brightness for clumps */
+            tmp = ci[ CCOL_RIV_SUM ]/ci[ CCOL_RIV_NUM ]*ci[ CCOL_NUM ];
+            ((float *)colarr)[cind] = MKC_MAG(ci[ CCOL_SUM ]-tmp);
+            break;
+
+          case UI_KEY_MAGNITUDEERR:
+            ((float *)colarr)[cind] = MAG_ERROR(p, ci, 1);
+            break;
+
+          case UI_KEY_UPPERLIMIT:
+            ((float *)colarr)[cind] = ci[ CCOL_UPPERLIMIT_B ];
+            break;
+
+          case UI_KEY_UPPERLIMITMAG:
+            ((float *)colarr)[cind] = MKC_MAG(ci[ CCOL_UPPERLIMIT_B ]);
+            break;
+
+          case UI_KEY_RIVERAVE:
+            ((float *)colarr)[cind] = ( ci[ CCOL_RIV_SUM]
+                                        / ci[ CCOL_RIV_NUM] );
+            break;
+
+          case UI_KEY_RIVERNUM:
+            ((int32_t *)colarr)[cind] = ci[ CCOL_RIV_NUM ];
+            break;
+
+          case UI_KEY_SN:
+            ((float *)colarr)[cind] = columns_sn(p, ci, 1);
+            break;
+
+          case UI_KEY_SKY:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[ CCOL_SUMSKY],
+                                                 ci[ CCOL_NUM] );
+            break;
+
+          case UI_KEY_STD:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[ CCOL_SUMSTD ],
+                                                 ci[ CCOL_NUM ] );
+            break;
+
+          case UI_KEY_SEMIMAJOR:
+            ((float *)colarr)[cind] = columns_second_order(pp, ci, key, 1);
+            break;
+
+          case UI_KEY_SEMIMINOR:
+            ((float *)colarr)[cind] = columns_second_order(pp, ci, key, 1);
+            break;
+
+          case UI_KEY_AXISRATIO:
+            ((float *)colarr)[cind]
+              = ( columns_second_order(pp, ci, UI_KEY_SEMIMINOR, 1)
+                  / columns_second_order(pp, ci, UI_KEY_SEMIMAJOR, 1) );
+          break;
+
+          case UI_KEY_POSITIONANGLE:
+            ((float *)colarr)[cind] = columns_second_order(pp, ci, key, 1);
+            break;
+
+          case UI_KEY_GEOSEMIMAJOR:
+            ((float *)colarr)[cind] = columns_second_order(pp, ci, key, 1);
+            break;
+
+          case UI_KEY_GEOSEMIMINOR:
+            ((float *)colarr)[cind] = columns_second_order(pp, ci, key, 1);
+            break;
+
+          case UI_KEY_GEOAXISRATIO:
+            ((float *)colarr)[cind]
+              = ( columns_second_order(pp, ci, UI_KEY_GEOSEMIMINOR, 1)
+                  / columns_second_order(pp, ci, UI_KEY_GEOSEMIMAJOR, 1) );
+          break;
+
+          case UI_KEY_GEOPOSITIONANGLE:
+            ((float *)colarr)[cind] = columns_second_order(pp, ci, key, 1);
+            break;
+
+          default:
+            error(EXIT_FAILURE, 0, "a bug! the output column code %d not "
+                  "recognized in `mkcatalog_fill_output_columns' (for "
+                  "clumps). Please contact us at %s to solve the problem",
+                  key, PACKAGE_BUGREPORT);
+          }
+      }
 }
diff --git a/bin/mkcatalog/columns.h b/bin/mkcatalog/columns.h
index 2f4a5d3..aa6210e 100644
--- a/bin/mkcatalog/columns.h
+++ b/bin/mkcatalog/columns.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <address@hidden>
 Contributing author(s):
-Copyright (C) 2015, Free Software Foundation, Inc.
+Copyright (C) 2016, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -23,62 +23,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #ifndef COLUMNS_H
 #define COLUMNS_H
 
-/* Units: */
-#define CATDESCRIPTLENGTH         "%-60s"
-#define CATUNITRATIO              "ratio"
-#define CATUNITDEGREE             "degree"
-#define CATUNITCOUNTER            "counter"
-#define CATUNITPIXAREA            "pixel area"
-#define CATUNITMAG                "scale (log)"
-#define CATUNITPIXLENGTH          "pixel length"
-#define CATUNITBRIGHTNESS         "input data unit"
-#define CATUNITAVE                "("CATUNITBRIGHTNESS")/pixel"
-
-
-/* Fixed names */
-#define MKCATX                    "X"
-#define MKCATY                    "Y"
-#define MKCATRA                   "RA"
-#define MKCATDEC                  "Dec"
-#define MKCATCLUMP                "Clump"
-#define MKCATMAG                  "magnitude"
-#define MKCATBRIGHT               "brightness"
-#define MKCATOBJECT               "Full object"
-#define MKCATWHTC                 "weighted center"
-#define MKCATGEOC                 "geometric center"
-#define MKCATCINO                 "Clumps in object"
-#define MKRIVERSSUR               "Rivers surrounding clump, average"
-
-void
-idcol(struct mkcatalogparams *p);
-
-void
-hostobj(struct mkcatalogparams *p, int o1c0);
-
-void
-numclumps(struct mkcatalogparams *p);
-
-void
-area(struct mkcatalogparams *p, int cinobj, int isriver);
-
-void
-position(struct mkcatalogparams *p, size_t col, char *target,
-         char *type, char *axis);
-
-void
-secondordermoment(struct mkcatalogparams *p, size_t ocol, char *target);
-
-void
-brightnessmag(struct mkcatalogparams *p, size_t col, char *target,
-              char *scale);
-
-void
-upperlimitcol(struct mkcatalogparams *p);
-
 void
-skystd(struct mkcatalogparams *p, size_t col);
+columns_define_alloc(struct mkcatalogparams *p);
 
 void
-sncol(struct mkcatalogparams *p, int sn0_magerr1, char *target);
+columns_fill(struct mkcatalog_passparams *pp);
 
 #endif
diff --git a/bin/mkcatalog/main.c b/bin/mkcatalog/main.c
index 6524da9..2ebc799 100644
--- a/bin/mkcatalog/main.c
+++ b/bin/mkcatalog/main.c
@@ -25,7 +25,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <timing.h>             /* Includes time.h and sys/time.h */
+#include <gnuastro-internal/timing.h>
 
 #include "main.h"
 
@@ -43,13 +43,13 @@ main (int argc, char *argv[])
   gettimeofday(&t1, NULL);
 
   /* Read the input parameters. */
-  setparams(argc, argv, &p);
+  ui_read_check_inputs_setup(argc, argv, &p);
 
   /* Run MakeProfiles */
   mkcatalog(&p);
 
   /* Free all non-freed allocations. */
-  freeandreport(&p, &t1);
+  ui_free_report(&p, &t1);
 
   /* Return successfully.*/
   return EXIT_SUCCESS;
diff --git a/bin/mkcatalog/main.h b/bin/mkcatalog/main.h
index 3656ae8..9fbd5dc 100644
--- a/bin/mkcatalog/main.h
+++ b/bin/mkcatalog/main.h
@@ -23,371 +23,176 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #ifndef MAIN_H
 #define MAIN_H
 
+/* Include necessary headers */
 #include <gsl/gsl_rng.h>
-#include <gnuastro/fits.h>
+#include <gnuastro/data.h>
+#include <gnuastro-internal/options.h>
 
-#include <commonparams.h>
+/* Progarm names.  */
+#define PROGRAM_NAME   "MakeCatalog"  /* Program full name.       */
+#define PROGRAM_EXEC   "astmkcatalog" /* Program executable name. */
+#define PROGRAM_STRING PROGRAM_NAME" (" PACKAGE_NAME ") " PACKAGE_VERSION
 
-/* Progarm name macros: */
-#define SPACK           "astmkcatalog" /* Subpackage executable name. */
-#define SPACK_NAME      "MakeCatalog"  /* Subpackage full name.       */
-#define SPACK_STRING    SPACK_NAME" ("PACKAGE_NAME") "PACKAGE_VERSION
 
 
 
+/* Multiple of given number to stop searching for upper-limit magnitude. */
+#define MKCATALOG_UPPERLIMIT_STOP_MULTIP 50
+#define MKCATALOG_UPPERLIMIT_MINIMUM_NUM 20
 
-/* Columns in the object and clump information tables
-   ==================================================
 
-   The information tables are not the final catalog, they keep all the
-   necessary information to make the catalogs in a later step. The
-   columns of that catalog can be specified by the user, while the
-   information catalog colums are fixed.
 
-   The basic idea is that through the first and second pass through
-   the image, we fill in this information array. Then we build the
-   catalog based on what the user has requested.
+/* Intermediate/raw array elements
+   ===============================
 
-   FIRST ROW IS FLAG ROW: The object/clump IDs begin from one (1), not
-   zero (0). So the first row in the oinfo, or cinfo arrays is not
-   filled and we need to start from the second row (which as index
-   1). So we can use the first row as a flag for the appropriate
-   columns. For example, the RA and Dec need to be calculated
-   together, so once they are both calculated by RA (for example),
-   there is no more need to calculate them again for Dec, or vice
-   versa. So in this case, we set the first row of this column to 1 so
-   if it is needed again, it is not recalculated.
+   Commonly, several high-level calculations need the same low-level
+   measurements. So to avoid having to do these low-level calculations on
+   each pixel multiple tiles, each thread/object will have one array of
+   intermediate values which will be filled in the pass over the
+   pixels. After this intermediate array is filled, and we don't need to
+   pass over the pixels any more, we will use the intermediate values to
+   derive the higher-level steps.
 
-   NOTE: The X and Y columns should be immediately after each other,
-   this is necessary for converting to RA and Dec.
+   According to the C standard, the first enum variable has a value of 0
+   (int), and when none are explicitly set (with an = sign), the values of
+   the subsequent enum variables are 1 larger. We want column indexs that
+   also start with zero, but setting the values by hand manually as
+   preprocessor macros can be buggy (repeated numbers). So defining them as
+   an enum is the perfect solution. Any future column that is to be added
+   (or if any are removed), we (the developers) don't have to worry.
 
-   NOTE: the total area and brightness of clumps in the objects can be
-   found by using the clump information table in the end.
-
-   According to the C standard, the first enum variable has a value of
-   0 (int), and when none are explicitly set (with an = sign), the
-   values of the subsequent enum variables are 1 larger. We want
-   column indexs that also start with zero, but setting the values by
-   hand manually as preprocessor macros can be buggy (repeated
-   numbers). So defining them as an enum is the perfect solution. Any
-   future column that is to be added (or if any are removed), we (the
-   developers) don't have to worry.
-*/
+   --------------------- POSITIONS IN FITS standard ---------------------
+   Like the final output, positions in this intermediate array are also in
+   the FITS standard (fastest dimension is first). */
 enum objectcols
   {
-    OAREA,               /* Area of this object.                    */
-    OALLAREA,            /* Object area irrespective of threshold.  */
-    ONCLUMPS,            /* Total number of clumps in this object.  */
-    OBrightness,         /* Sum of (flux-sky) in object.            */
-    OFlxWhtX,            /* Sum of (flux-sky)*x of this object.     */
-    OFlxWhtY,            /* Sum of (flux-sky)*y of this object.     */
-    OFlxWhtXX,           /* Sum of (flux-sky)*x*x of this object.   */
-    OFlxWhtYY,           /* Sum of (flux-sky)*y*y of this object.   */
-    OFlxWhtXY,           /* Sum of (flux-sky)*x*y of this object.   */
-    OFlxWhtRA,           /* RA of (OFlxWhtX, OFlxWhtY).             */
-    OFlxWhtDec,          /* Dec of (OFlxWhtX, OFlxWhtY).            */
-    OAREAC,              /* Area of clumps in this object.          */
-    OBrightnessC,        /* Brightness  in object clumps.           */
-    OUpperBright,        /* Upper limit brightness for this object. */
-    OFlxWhtCX,           /* Sum of (flux-sky)*x on object clumps.   */
-    OFlxWhtCY,           /* Sum of (flux-sky)*y on obj. clumps.     */
-    OFlxWhtCXX,          /* Sum of (flux-sky)*x*x on object clumps. */
-    OFlxWhtCYY,          /* Sum of (flux-sky)*y*y on obj. clumps.   */
-    OFlxWhtCXY,          /* Sum of (flux-sky)*x*y on obj. clumps.   */
-    OPOSSHIFTX,          /* Shift in X to avoid rounding errors.    */
-    OPOSSHIFTY,          /* Shift in Y to avoid rounding errors.    */
-    OFlxWhtCRA,          /* RA of (OFlxWhtCX and OFlxWhtCY).        */
-    OFlxWhtCDec,         /* Dec of (OFlxWhtCX and OFlxWhtCY).       */
-    OSKY,                /* Sum of sky value on this object.        */
-    OSTD,                /* Sum of sky STD value on this object.    */
-    OPosBright,          /* Sum of positive image pixels for wht.   */
-    OPosBrightC,         /* Sum of positive image pixels for wht.   */
-    OGeoX,               /* Geometric center of object in X.        */
-    OGeoY,               /* Geometric center of object in Y.        */
-    OGeoRA,              /* RA of Geometric center of object.       */
-    OGeoDec,             /* Dec of Geometric center of object.      */
-    OGeoXX,              /* Second order geometric variable: X*X.   */
-    OGeoYY,              /* Second order geometric variable: Y*Y.   */
-    OGeoXY,              /* Second order geometric variable: X*Y.   */
-    OGeoCX,              /* Geometric center of clumps in object X. */
-    OGeoCY,              /* Geometric center of clumps in object Y. */
-    OGeoCRA,             /* Geometric center of clumps in obj. RA.  */
-    OGeoCDec,            /* Geometric center of clumps in obj. Dec. */
-    OGeoCXX,             /* Second order geometric variable: X*X.   */
-    OGeoCYY,             /* Second order geometric variable: Y*Y.   */
-    OGeoCXY,             /* Second order geometric variable: X*Y.   */
-
-    OCOLUMNS,            /* Keep this last: total number of columns.*/
+    OCOL_NUMALL,         /* Number of all pixels with this label.     */
+    OCOL_NUM,            /* Number of pixels above threshold.         */
+    OCOL_SUM,            /* Sum of (value-sky) in object.             */
+    OCOL_VX,             /* Sum of (value-sky) * x.                   */
+    OCOL_VY,             /* Sum of (value-sky) * y.                   */
+    OCOL_SX,             /* Shift along X axis.                       */
+    OCOL_SY,             /* Shift along Y axis.                       */
+    OCOL_VXX,            /* Sum of (value-sky) * x * x.               */
+    OCOL_VYY,            /* Sum of (value-sky) * y * y.               */
+    OCOL_VXY,            /* Sum of (value-sky) * x * y.               */
+    OCOL_SUMSKY,         /* Sum of sky value on this object.          */
+    OCOL_SUMSTD,         /* Sum of sky STD value on this object.      */
+    OCOL_SUMPOS,         /* Sum of positive image pixels.             */
+    OCOL_GX,             /* Geometric center of object in X.          */
+    OCOL_GY,             /* Geometric center of object in Y.          */
+    OCOL_GXX,            /* Second order geometric variable: X*X.     */
+    OCOL_GYY,            /* Second order geometric variable: Y*Y.     */
+    OCOL_GXY,            /* Second order geometric variable: X*Y.     */
+    OCOL_UPPERLIMIT_B,   /* Upper limit magnitude.                    */
+    OCOL_C_NUM,          /* Area of clumps in this object.            */
+    OCOL_C_SUM,          /* Brightness  in object clumps.             */
+    OCOL_C_VX,           /* Sum of (value-sky)*x on clumps.           */
+    OCOL_C_VY,           /* Sum of (value-sky)*y on obj. clumps.      */
+    OCOL_C_GX,           /* Geometric center of clumps in object X.   */
+    OCOL_C_GY,           /* Geometric center of clumps in object Y.   */
+    OCOL_C_SUMPOS,       /* Sum of positive image pixels for wht.     */
+
+    OCOL_NUMCOLS,        /* SHOULD BE LAST: total number of columns.  */
   };
 
 enum clumpcols
   {
-    CHOSTOID,            /* ID of object hosting this clump.        */
-    CINHOSTID,           /* ID of clump in host object.             */
-    CAREA,               /* Area of this clump.                     */
-    CALLAREA,            /* Area of clump irrespective of threshold.*/
-    CFlxWhtX,            /* Sum of flux*x of this clump.            */
-    CFlxWhtY,            /* Sum of flux*y of this clump.            */
-    CFlxWhtXX,           /* Sum of flux*x*x of this clump.          */
-    CFlxWhtYY,           /* Sum of flux*y*y of this clump.          */
-    CFlxWhtXY,           /* Sum of flux*x*y of this clump.          */
-    CPOSSHIFTX,          /* Shift in X to avoid rounding errors.    */
-    CPOSSHIFTY,          /* Shift in Y to avoid rounding errors.    */
-    CFlxWhtRA,           /* ra of (CFlxWhtX, CFlxWhtY).             */
-    CFlxWhtDec,          /* Dec of (CFlxWhtX, CFlxWhtY).            */
-    CBrightness,         /* River subtracted brightness.            */
-    CNoRiverBrightness,  /* Sky (not river) subtracted brightness.  */
-    CUpperBright,        /* Upper limit brightness for this clump.  */
-    CRivAve,             /* Average value in rivers around clump.   */
-    CRivArea,            /* Num river pixels around this clump.     */
-    CSKY,                /* Sum of sky value on this object.        */
-    CSTD,                /* Sum of sky STD value on this object.    */
-    CPosBright,          /* Sum of positive image pixels for wht.   */
-    CGeoX,               /* Geometric center of clump in X.         */
-    CGeoY,               /* Geometric center of clump in Y.         */
-    CGeoRA,              /* RA of Geometric center of clump.        */
-    CGeoDec,             /* Dec of Geometric center of clump.       */
-    CGeoXX,              /* Second order geometric moment.          */
-    CGeoYY,              /* Second order geometric moment.          */
-    CGeoXY,              /* Second order geometric moment.          */
-
-    CCOLUMNS,            /* Keep this last: total number of columns.*/
-  };
-
-
-
-
-
-/* Codes for output columns
-   ========================
-
-   The user can ask for any columns in any order, to enable that, we
-   need a fixed number ID for each column. Again (similar to the
-   argument for the information array columns), it is just important
-   that these numbers be separate and defining them by hand can cause
-   bugs. So we are defining them as enums here. */
-enum outcols
-  {
-    CATID,
-    CATHOSTOBJID,
-    CATIDINHOSTOBJ,
-    CATNUMCLUMPS,
-    CATAREA,
-    CATCLUMPSAREA,
-    CATX,
-    CATY,
-    CATGEOX,
-    CATGEOY,
-    CATCLUMPSX,
-    CATCLUMPSY,
-    CATCLUMPSGEOX,
-    CATCLUMPSGEOY,
-    CATRA,
-    CATDEC,
-    CATGEORA,
-    CATGEODEC,
-    CATCLUMPSRA,
-    CATCLUMPSDEC,
-    CATCLUMPSGEORA,
-    CATCLUMPSGEODEC,
-    CATBRIGHTNESS,
-    CATCLUMPSBRIGHTNESS,
-    CATNORIVERBRIGHTNESS,
-    CATMAGNITUDE,
-    CATMAGNITUDEERR,
-    CATCLUMPSMAGNITUDE,
-    CATUPPERLIMITMAG,
-    CATRIVERAVE,
-    CATRIVERNUM,
-    CATSN,
-    CATSKY,
-    CATSTD,
-    CATSEMIMAJOR,
-    CATSEMIMINOR,
-    CATPOSITIONANGLE,
-    CATGEOSEMIMAJOR,
-    CATGEOSEMIMINOR,
-    CATGEOPOSITIONANGLE,
+    CCOL_NUMALL,         /* Area of clump irrespective of threshold.  */
+    CCOL_NUM,            /* Area of this clump.                       */
+    CCOL_VX,             /* Sum of (value-sky) * x.                   */
+    CCOL_VY,             /* Sum of (value-sky) * y.                   */
+    CCOL_VXX,            /* Sum of flux*x*x of this clump.            */
+    CCOL_VYY,            /* Sum of flux*y*y of this clump.            */
+    CCOL_VXY,            /* Sum of flux*x*y of this clump.            */
+    CCOL_SUM,            /* River subtracted brightness.              */
+    CCOL_RIV_SUM,        /* Sum of rivers around clump.               */
+    CCOL_RIV_NUM,        /* Num river pixels around this clump.       */
+    CCOL_SUMSKY,         /* Sum of sky value on this object.          */
+    CCOL_SUMSTD,         /* Sum of sky STD value on this object.      */
+    CCOL_SUMPOS,         /* Sum of positive image pixels for wht.     */
+    CCOL_GX,             /* Geometric center of clump in X.           */
+    CCOL_GY,             /* Geometric center of clump in Y.           */
+    CCOL_GXX,            /* Second order geometric moment.            */
+    CCOL_GYY,            /* Second order geometric moment.            */
+    CCOL_GXY,            /* Second order geometric moment.            */
+    CCOL_UPPERLIMIT_B,   /* Upper limit magnitude.                    */
+
+    CCOL_NUMCOLS,        /* SHOULD BE LAST: total number of columns.  */
   };
 
 
 
 
 
-struct uiparams
-{
-  char             *inputname;  /* Name of input file.               */
-  char              *maskname;  /* Name of masked file.              */
-  char                  *mhdu;  /* HDU of mask image.                */
-  char           *objlabsname;  /* Name of object labels file.       */
-  char                *objhdu;  /* HDU of object labels image.       */
-  char         *clumplabsname;  /* Name of clump labels file .       */
-  char              *clumphdu;  /* HDU of clump labels image.        */
-  char               *skyname;  /* Sky value image file name.        */
-  char                *skyhdu;  /* Sky HDU name.                     */
-  char               *stdname;  /* Sky STD value image file name.    */
-  char                *stdhdu;  /* Sky STD HDU name.                 */
-  char            *upmaskname;  /* Name of mask for upper magnitude. */
-  char             *upmaskhdu;  /* HDU Name of mask for upper mag.   */
-
-  int             masknameset;
-  int                 mhduset;
-  int          objlabsnameset;
-  int               objhduset;
-  int        clumplabsnameset;
-  int             clumphduset;
-  int              skynameset;
-  int               skyhduset;
-  int              stdnameset;
-  int               stdhduset;
-  int              envseedset;
-
-  int            zeropointset;
-  int        skysubtractedset;
-  int            thresholdset;
-
-  int             intwidthset;
-  int           floatwidthset;
-  int            accuwidthset;
-  int       floatprecisionset;
-  int        accuprecisionset;
-  int              nsigmagset;
-
-  int           upmasknameset;
-  int            upmaskhduset;
-  int                upnumset;
-  int        upsclipmultipset;
-  int          upsclipaccuset;
-  int             upnsigmaset;
-
-  int                   idset;
-  int            hostobjidset;
-  int          idinhostobjset;
-  int            numclumpsset;
-  int                 areaset;
-  int           clumpsareaset;
-  int                    xset;
-  int                    yset;
-  int                 geoxset;
-  int                 geoyset;
-  int              clumpsxset;
-  int              clumpsyset;
-  int           clumpsgeoxset;
-  int           clumpsgeoyset;
-  int                   raset;
-  int                  decset;
-  int                georaset;
-  int               geodecset;
-  int             clumpsraset;
-  int            clumpsdecset;
-  int          clumpsgeoraset;
-  int         clumpsgeodecset;
-  int           brightnessset;
-  int     clumpsbrightnessset;
-  int    noriverbrightnessset;
-  int            magnitudeset;
-  int         magnitudeerrset;
-  int      clumpsmagnitudeset;
-  int        upperlimitmagset;
-  int             riveraveset;
-  int             rivernumset;
-  int                   snset;
-  int                  skyset;
-  int                  stdset;
-  int            semimajorset;
-  int            semiminorset;
-  int        positionangleset;
-  int         geosemimajorset;
-  int         geosemiminorset;
-  int     geopositionangleset;
-};
-
-
-
 
 
+/* Main program parameters structure */
 struct mkcatalogparams
 {
-  /* Other structures: */
-  struct uiparams          up;  /* User interface parameters.         */
-  struct gal_commonparams  cp;  /* Common parameters.                 */
-
-  /* Input: */
-  float                  *img;  /* Input image.                       */
-  long               *objects;  /* Object labels on each pixel.       */
-  long                *clumps;  /* Clump labels on each pixel.        */
-  float                  *sky;  /* Sky value on each pixel.           */
-  float                  *std;  /* Sky STD value on each pixel.       */
-  int                    nwcs;  /* Number of WCS structures.          */
-  struct wcsprm          *wcs;  /* Pointer to WCS structures.         */
-  size_t                   s0;  /* Size of input (first C axis).      */
-  size_t                   s1;  /* Size of input (second C axis).     */
-  float             zeropoint;  /* Zeropoint magnitude of input.      */
-  int           skysubtracted;  /* Input is already sky subtracted.   */
-  double              nsigmag;  /* Multiple of Sky STD to report mag. */
-  double            threshold;  /* Only pixels larger than this *STD. */
-  int                 envseed;  /* Use GSL_RNG_SEED for random seed.  */
-
-  /* Output: */
-  char              *ocatname;  /* File name of object catalog.       */
-  char              *ccatname;  /* File name of clump catalog.        */
-  int                intwidth;  /* Width of integer columns.          */
-  int              floatwidth;  /* Width of float columns.            */
-  int               accuwidth;  /* Width of accurate float columns.   */
-  int          floatprecision;  /* Floating point precision.          */
-  int           accuprecision;  /* Accurate floating point precision. */
-
-  /* Upper limit: */
-  long                *upmask;  /* Mask array, only for upper liimit. */
-  size_t                upnum;  /* Number of random samples.          */
-  float         upsclipmultip;  /* Sigma clip multiple for std calc.  */
-  float           upsclipaccu;  /* Sigma clip accuracy for std calc.  */
-  float              upnsigma;  /* Multiple of sigma for upper-limit. */
-
-  /* Operating mode: */
-
-  /* Internal: */
-  time_t              rawtime;  /* Starting time of the program.      */
-  double               *oinfo;  /* Information for all the clumps.    */
-  double               *cinfo;  /* Information for all the clumps.    */
-  size_t           numobjects;  /* Total number of objects.           */
-  size_t            numclumps;  /* Total number of clumps.            */
-  struct gal_linkedlist_sll *allcolsll; /* All the input columns.     */
-  size_t             *allcols;  /* Array keeping all the input cols.  */
-  size_t             allncols;  /* Total number of input columns.     */
-  size_t             *objcols;  /* Array of objcolsll.                */
-  size_t           *clumpcols;  /* Array of clumpcolsll.              */
-  size_t             objncols;  /* Num. columns in objects catalog.   */
-  size_t           clumpncols;  /* Num. columns in clumps catalog.    */
-  double              *objcat;  /* Output object catalog.             */
-  double            *clumpcat;  /* Output clump catalog.              */
-  size_t            objcurcol;  /* Current column in object catalog.  */
-  size_t          clumpcurcol;  /* Current column in clump catalog.   */
-  float                minstd;  /* For correction for counts/sec.     */
-  float                medstd;  /* Median STD value (5 sigma mag).    */
-  double              cpscorr;  /* Correction for counts/sec.         */
-  double                detsn;  /* Detection S/N threshold.           */
-  double              clumpsn;  /* Clumps S/N threshold.              */
-
-  /* For going through the rows: */
-  size_t               curcol;  /* Current column in the catalog.     */
-  size_t           intcounter;  /* Counter of the integer columns.    */
-  size_t          accucounter;  /* Counter of the integer columns.    */
-  int              obj0clump1;  /* Is this an object or clump catalog.*/
-  char                  *name;  /* `Object' or `Clump'.               */
-  double                 *cat;  /* Either objcat or clumpcat.         */
-  char              *filename;  /* Either ocatname or ccatname.       */
-  size_t                  num;  /* Either numobjects or numclumps.    */
-  size_t              numcols;  /* Either objncols or clumpncols.     */
-  double                *info;  /* Pointer to either oinfo or cinfo.  */
-  size_t                icols;  /* Either OCOLUMNS or CCOLUMNS.       */
-  char                 *unitp;  /* Pointer to units array.            */
-  size_t            xshiftcol;  /* Column to correct/shift positions. */
-  size_t            yshiftcol;  /* Column to correct/shift positions. */
-  char             line[1500];  /* Comment line.                      */
-  char       description[500];  /* The description of each row.       */
-  int                *intcols;  /* Indexs of integer columns.         */
-  int               *accucols;  /* Indexs of accurate columns.        */
+  /* From command-line */
+  struct gal_options_common_params cp; /* Common parameters.            */
+  struct gal_linkedlist_ill  *columnids; /* The desired column codes.   */
+  char             *inputname;  /* Input filename.                      */
+  char           *objectsfile;  /* File name of objects file.           */
+  char            *objectshdu;  /* HDU of objects image.                */
+  char            *clumpsfile;  /* File name of objects file.           */
+  char             *clumpshdu;  /* HDU of objects image.                */
+  char               *skyfile;  /* File name of sky file.               */
+  char                *skyhdu;  /* HDU of sky image.                    */
+  char               *stdfile;  /* File name of sky STD file.           */
+  char                *stdhdu;  /* HDU of sky STD image.                */
+
+  float             zeropoint;  /* Zero-point magnitude of object.      */
+  uint8_t       skysubtracted;  /* If image is already sky subtracted.  */
+  float             threshold;  /* Only use values above this threshold.*/
+  float               nsigmag;  /* Multiple of STD to report magnitude. */
+
+  char            *upmaskfile;  /* Name of upper limit mask file.       */
+  char             *upmaskhdu;  /* HDU of upper limit mask file.        */
+  size_t                upnum;  /* Number of upper-limit random samples.*/
+  uint8_t             envseed;  /* Use the environment for random seed. */
+  double       upsigmaclip[2];  /* Sigma clip to measure upper limit.   */
+  float              upnsigma;  /* Multiple of sigma to define up-lim.  */
+
+  /* Internal. */
+  time_t              rawtime;  /* Starting time of the program.        */
+  gal_data_t           *input;  /* Input.                               */
+  gal_data_t         *objects;  /* Object labels.                       */
+  gal_data_t          *clumps;  /* Clump labels.                        */
+  gal_data_t             *sky;  /* Sky.                                 */
+  gal_data_t             *std;  /* Sky standard deviation.              */
+  gal_data_t          *upmask;  /* Upper limit magnitude mask.          */
+  float                minstd;  /* Minimum Standard deviation value.    */
+  float                medstd;  /* Median standard deviation value.     */
+  float               cpscorr;  /* Counts-per-second correction.        */
+  float                 detsn;  /* Minimum detection S/N threshold.     */
+  size_t           numobjects;  /* Number of object labels in image.    */
+  float               clumpsn;  /* Clump S/N threshold.                 */
+  size_t            numclumps;  /* Number of clumps in image.           */
+  gal_data_t      *objectcols;  /* Output columns for the objects.      */
+  gal_data_t       *clumpcols;  /* Output columns for the clumps.       */
+  gal_data_t           *tiles;  /* Tiles to cover each object.          */
+  char            *objectsout;  /* Output objects catalog.              */
+  char             *clumpsout;  /* Output clumps catalog.               */
+  uint8_t             *oiflag;  /* Intermediate flags for objects.      */
+  uint8_t             *ciflag;  /* Intermediate flags for clumps.       */
+  pthread_mutex_t       mutex;  /* Mutex to change the total numbers.   */
+  size_t      clumprowsfilled;  /* No. filled clump rows at this moment.*/
+  gsl_rng                *rng;  /* Main random number generator.        */
+  unsigned long          seed;  /* Random number generator seed.        */
+  const char         *rngname;  /* Name of random number generator.     */
+
+  double              **rd_vo;  /* Object RA-Dec flux weighted X, Y.    */
+  double              **rd_vc;  /* Clump RA-Dec flux weighted X, Y.     */
+  double              **rd_go;  /* Object RA-Dec geometric X,Y.         */
+  double              **rd_gc;  /* Clump RA-Dec geometric X, Y.         */
+  double             **rd_vcc;  /* All clumps RA-Dec flx. wht. X, Y.    */
+  double             **rd_gcc;  /* All clumps RA-Dec geometric X, Y.    */
+
+  uint8_t            hasblank;  /* Dataset has blank values.            */
+  uint8_t              hasmag;  /* Catalog has magnitude columns.       */
+  uint8_t          upperlimit;  /* Calculate upper limit magnitude.     */
 };
 
 #endif
diff --git a/bin/mkcatalog/mkcatalog.c b/bin/mkcatalog/mkcatalog.c
index 4da48e0..0344beb 100644
--- a/bin/mkcatalog/mkcatalog.c
+++ b/bin/mkcatalog/mkcatalog.c
@@ -34,345 +34,370 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/wcs.h>
 #include <gnuastro/data.h>
 #include <gnuastro/fits.h>
-#include <gnuastro/txtarray.h>
+#include <gnuastro/threads.h>
+#include <gnuastro/dimension.h>
 
-#include <timing.h>
-#include <neighbors.h>
+#include <gnuastro-internal/timing.h>
 
 #include "main.h"
+#include "mkcatalog.h"
 
+#include "ui.h"
 #include "columns.h"
-#include "mkcatalog.h"
+#include "upperlimit.h"
 
 
 
 
 
 /*********************************************************************/
-/*****************     Fill information tables     *******************/
+/*************      Definitions and initialization     ***************/
 /*********************************************************************/
+/* Both passes are going to need their starting pointers set, so we'll do
+   that here. */
+static void
+mkcatalog_initialize_params(struct mkcatalog_passparams *pp)
+{
+  struct mkcatalogparams *p=pp->p;
 
-/* Macro to see if the label is indexable (belongs to an object or
-   not). See the explanation in bin/noisechisel/label.h. */
-#if GAL_DATA_BLANK_LONG<0
-#define ISINDEXABLEOBJLABEL (objects[i]>0)
-#define ISINDEXABLECLPLABEL (clumps[i]>0)
-#else
-#define ISINDEXABLEOBJLABEL (objects[i] && objects[i]!=GAL_DATA_BLANK_LONG)
-#define ISINDEXABLECLPLABEL (clumps[i] && clumps[i]!=GAL_DATA_BLANK_LONG)
-#endif
+  /* Initialize the number of clumps in this object. */
+  pp->clumpsinobj=0;
 
 
+  /* Initialize the intermediate values. */
+  memset(pp->oi, 0, OCOL_NUMCOLS * sizeof *pp->oi);
 
 
+  /* Set the shifts in every dimension to avoid round-off errors in large
+     numbers for the non-linear calculations. We are using the first pixel
+     of each object's tile as the shift parameter to keep the mean
+     (average) reasonably near to the standard deviation. Otherwise, when
+     the object is far out in the image (large x and y positions), then
+     roundoff errors are going to decrease the accuracy of the second order
+     calculations. */
+  gal_dimension_index_to_coord( ( (float *)(pp->tile->array)
+                                  - (float *)(pp->tile->block->array) ),
+                                p->input->ndim, p->input->dsize, pp->shift);
 
-/* In the first pass, the most basic properties (mainly about the
-   objects) are found. The primary reason is that we still don't know
-   how many objects there are in order to be able to put the clump
-   information in the proper place. This could maybe be fixed with a
-   linked list in one pass, but that would drastically bring down the
-   speed. */
-void
-firstpass(struct mkcatalogparams *p)
-{
-  float imgss;
-  size_t i, is1=p->s1;
-  double x, y, sx, sy, *thisobj;
-  long *objects=p->objects, *clumps=p->clumps;
-
-  /* Go over the pixels and fill the information array. */
-  for(i=0;i<p->s0*p->s1;++i)
-    if(ISINDEXABLEOBJLABEL)
-      {
-        /* thisobj is a pointer to the start of the row in the object
-           information array (oinfo). It is mainly used to keep things
-           short, simple, less bugy and most importantly: elegant. */
-        imgss = p->img[i] - p->sky[i];
-        thisobj = p->oinfo + objects[i]*OCOLUMNS;
-
-        /* Set the shifts to avoid round-off errors in large numbers
-           for the non-linear calculations. We are using the first
-           pixel of each object as the shift parameter to keep the
-           mean reasonably near to the standard deviation. Otherwise,
-           when the object is far out in the image (large x and y
-           positions), then roundoff errors are going to decrease the
-           accuracy of the second order calculations.
-
-           When the object and clumps columns were allocated, the
-           shift columns were set to NaN so we know when to set them
-           for each object/clump. For later parallelization, a mutex
-           can be set up within the `if' statement below and another
-           check can be done within the mutex, this way, only if
-           separate threads get to the start of an object in their
-           respective mesh, for an object that doesn't have a shift
-           already assigned to it, at the same time (highly unlikely)
-           will the process be slightly slowed down. Otherwise, there
-           will be no speed penalty. */
-        if(isnan(thisobj[ OPOSSHIFTX ]))
-          {
-            thisobj[ OPOSSHIFTX ] = i%is1+1;
-            thisobj[ OPOSSHIFTY ] = i/is1+1;
-          }
-
-        /* Set the positional variables: */
-        x = i%is1+1;
-        y = i/is1+1;
-        sx = x - thisobj[ OPOSSHIFTX ];
-        sy = y - thisobj[ OPOSSHIFTY ];
-
-        /* Properties that are independent of threshod: */
-        ++thisobj[OALLAREA];
-        thisobj[ OSKY ]        += p->sky[i];
-        thisobj[ OSTD ]        += p->std[i];
-
-        /* Only if the pixel is above the desired threshold.
-
-           REASON: The reason this condition is given like this that
-           we don't want to do multiple checks. The basic idea is
-           this: when the user doesn't want any thresholds applied,
-           then p->threshold=NAN and any conditional that involves a
-           NaN will fail, so its logical negation will be positive and
-           the calculations below will be done. However, if the user
-           does specify a threhold and the pixel is above the
-           threshold, then (imgss<p->threshold*p->std[i]) will be
-           false and its logigal negation will be positive, so the
-           pixel will be included.*/
-        if(!(imgss<p->threshold*p->std[i]))
-          {
-            ++thisobj[OAREA];
-            thisobj[ OGeoX ]       += x;
-            thisobj[ OGeoY ]       += y;
-            thisobj[ OGeoXX ]      += sx * sx;
-            thisobj[ OGeoYY ]      += sy * sy;
-            thisobj[ OGeoXY ]      += sx * sy;
-            thisobj[ OBrightness ] += imgss;
-            if(imgss>0)
-              {
-                thisobj[ OPosBright ]  += imgss;
-                thisobj[ OFlxWhtX   ]  += imgss * x;
-                thisobj[ OFlxWhtY   ]  += imgss * y;
-                thisobj[ OFlxWhtXX  ]  += imgss * sx * sx;
-                thisobj[ OFlxWhtYY  ]  += imgss * sy * sy;
-                thisobj[ OFlxWhtXY  ]  += imgss * sx * sy;
-              }
-          }
-
-        if(clumps && clumps[i]>0)
-          {
-            /* The largest clump ID over each object is the number of
-               clumps that object has. */
-            thisobj[ ONCLUMPS ] = ( clumps[i] > thisobj[ONCLUMPS]
-                                    ? clumps[i] : thisobj[ONCLUMPS] );
-
-            /* Only if the pixel is above the desired threshold, see
-               explanation under similar condition above. */
-            if(!(imgss<p->threshold*p->std[i]))
-              {
-                /* Save the information. */
-                ++thisobj [ OAREAC ];
-                thisobj[ OBrightnessC ]  += imgss;
-                thisobj[ OGeoCX  ]       += x;
-                thisobj[ OGeoCY  ]       += y;
-                thisobj[ OGeoCXX ]       += sx * sx;
-                thisobj[ OGeoCYY ]       += sy * sy;
-                thisobj[ OGeoCXY ]       += sx * sy;
-                if(imgss>0)
-                  {
-                    thisobj[ OPosBrightC ]  += imgss;
-                    thisobj[ OFlxWhtCX   ]  += imgss * x;
-                    thisobj[ OFlxWhtCY   ]  += imgss * y;
-                    thisobj[ OFlxWhtCXX  ]  += imgss * sx * sx;
-                    thisobj[ OFlxWhtCYY  ]  += imgss * sy * sy;
-                    thisobj[ OFlxWhtCXY  ]  += imgss * sx * sy;
-                  }
-              }
-          }
-      }
+
+  /* Set the starting and ending indexs of this tile/object.  */
+  pp->st_i   = gal_tile_start_end_ind_inclusive(pp->tile, p->input,
+                                                pp->start_end_inc);
+  pp->st_sky = (float *)(p->sky->array)       + pp->start_end_inc[0];
+  pp->st_std = (float *)(p->std->array)       + pp->start_end_inc[0];
+  pp->st_o   = (int32_t *)(p->objects->array) + pp->start_end_inc[0];
+  pp->st_c   = ( p->clumps
+                 ? (int32_t *)(p->clumps->array)  + pp->start_end_inc[0]
+                 : NULL );
 }
 
 
 
 
 
-/* In the second pass, we have the number of clumps so we can find the
-   total values for the clumps. */
-void
-clumppass(struct mkcatalogparams *p)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*********************************************************************/
+/*************         First and second passes         ***************/
+/*********************************************************************/
+static void
+mkcatalog_first_pass(struct mkcatalog_passparams *pp)
 {
-  float imgss;
-  double x, y, sx, sy;
-  long wngb[2*WNGBSIZE];
-  size_t ii, *n, *nf, numngb, ngb[8];
-  double *thisclump, *cinfo=p->cinfo;
-  long *objects=p->objects, *clumps=p->clumps;
-  float *img=p->img, *sky=p->sky, *std=p->std;
-  size_t i, j, *ind, *ofcrow, row=0, is0=p->s0, is1=p->s1;
-
-
-
-  /* The job of ofcrow (object-first-clump-row) is to give the row
-     number of the first clump within an object in the clumps
-     information table. This value can be added with the clumpid in
-     order to give the row number in the clumps information table. */
-  errno=0; ofcrow=malloc((p->numobjects+1)*sizeof *ofcrow);
-  if(ofcrow==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for ofcrow in seconpass "
-          "(mkcatalog.c)", (p->numobjects+1)*sizeof *ofcrow);
-
-
-  /* Fill ofcrow using the known number of clumps in each object. Note
-     that while ofcrow counts from zero, the clump[i] values (which
-     are added with this later) are added with one. So the clump
-     information will start from the second row (with index 1) of the
-     clumps array, not the first (with index 0). */
-  for(i=1;i<=p->numobjects;++i)
-    if(p->oinfo[i*OCOLUMNS+ONCLUMPS]>0.0f)
-      {
-        ofcrow[i]=row;
-        row+=p->oinfo[i*OCOLUMNS+ONCLUMPS];
-      }
-
-  /* Go over all the pixels in the image and fill in the clump
-     information: */
-  for(i=0;i<p->s0*p->s1;++i)
+  struct mkcatalogparams *p=pp->p;
+  size_t ndim=p->input->ndim, *dsize=p->input->dsize;
+
+  double *oi=pp->oi;
+  int32_t *O, *C=NULL;
+  size_t d, increment=0, num_increment=1;
+  float ss, *I, *II, *SK, *ST, *input=p->input->array;
+  size_t *c=gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+  size_t *sc=gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+
+
+  /* Parse each contiguous patch of memory covered by this object. */
+  while( pp->start_end_inc[0] + increment <= pp->start_end_inc[1] )
     {
-      /* We are on a clump, save its properties. */
-      if(ISINDEXABLECLPLABEL)
+      /* Set the contiguous range to parse, we will check the count
+         over the `I' pointer and just increment the rest. */
+      O  = pp->st_o   + increment;
+      SK = pp->st_sky + increment;
+      ST = pp->st_std + increment;
+      if(p->clumps) C = pp->st_c + increment;
+      II = ( I = pp->st_i + increment ) + pp->tile->dsize[ndim-1];
+
+      /* Parse the tile. */
+      do
         {
-          /* This pointer really simplifies things below! */
-          thisclump = ( cinfo
-                        + ( ofcrow[objects[i]] + clumps[i] )
-                        * CCOLUMNS );
-
-          /* Set the shifts for this object if not already set, see
-             the explanations in the firstpass function. */
-          if(isnan(thisclump[ CPOSSHIFTX ]))
+          /* If this pixel belongs to the requested object and isn't
+             NAN, then do the processing. `hasblank' is constant, so
+             when the input doesn't have any blank values, the `isnan'
+             will never be checked. */
+          if( *O==pp->object && !( p->hasblank && isnan(*I) ) )
             {
-              thisclump[ CPOSSHIFTX ] = i%is1+1;
-              thisclump[ CPOSSHIFTY ] = i/is1+1;
-            }
 
-          /* Set the positional variables: */
-          x = i%is1+1;
-          y = i/is1+1;
-          sx = x - thisclump[ CPOSSHIFTX ];
-          sy = y - thisclump[ CPOSSHIFTY ];
-
-          /* Calculations that are independent of the threshold. The
-             CALLAREA is the full area of the clump irrespective of
-             the threshold, this is needed to correctly implement the
-             average river flux (which is defined by the. */
-          ++thisclump[ CALLAREA ];
-          thisclump[ CSKY ]  += sky[i];
-          thisclump[ CSTD ]  += std[i];
-
-          /* Only if the pixel is above the desired threshold, see
-             explanation under similar condition above. */
-          if(!(img[i]-sky[i]<p->threshold*p->std[i]))
-            {
-              /* Sky subtracted brightness */
-              imgss=img[i]-sky[i];
-
-              /* Fill in this clump information. IMPORTANT NOTE: The
-                 Sky is not subtracted from the clump brightness or
-                 river, because later, we will subtract the river flux
-                 from the clump brightness and therefore we don't need
-                 to know the Sky for the clump brightness. */
-              ++thisclump[ CAREA ];
-              thisclump[ CGeoX ]              += x;
-              thisclump[ CGeoY ]              += y;
-              thisclump[ CGeoXX ]             += sx * sx;
-              thisclump[ CGeoYY ]             += sy * sy;
-              thisclump[ CGeoXY ]             += sx * sy;
-              thisclump[ CINHOSTID ]           = clumps[i];
-              thisclump[ CHOSTOID ]            = objects[i];
-              thisclump[ CBrightness ]        += img[i];
-              thisclump[ CNoRiverBrightness ] += imgss;
-              if( imgss > 0.0f )
+              /* Total area (independent of threshold). */
+              ++oi[ OCOL_NUMALL ];
+
+              /* Sky subtracted value. */
+              ss = *I - *SK;
+
+              /* Get the number of clumps in this object: the largest clump
+                 ID over each object. */
+              if( p->clumps && *C>0 )
+                pp->clumpsinobj = *C > pp->clumpsinobj ? *C : pp->clumpsinobj;
+
+              /* Only continue if the pixel is above the threshold.
+
+                 ABOUT THE CHECK: The reason this condition is given
+                 like this is that the `threshold' value is optional
+                 and we don't want to do multiple checks. The basic
+                 idea is this: when the user doesn't want any
+                 thresholds applied, then `p->threshold==NAN' and any
+                 conditional that involves a NaN will fail, so its
+                 logical negation will be positive and the calculations
+                 below will be done. However, if the user does specify
+                 a threhold and the pixel is above the threshold, then
+                 (`ss < p->threshold * *ST') will be false and its
+                 logical negation will be positive, so the pixel will
+                 be included. */
+              if( !( ss < p->threshold * *ST ) )
                 {
-                  thisclump[ CPosBright ]   += imgss;
-                  thisclump[ CFlxWhtX ]     += imgss * x;
-                  thisclump[ CFlxWhtY ]     += imgss * y;
-                  thisclump[ CFlxWhtXX ]    += imgss * sx * sx;
-                  thisclump[ CFlxWhtYY ]    += imgss * sy * sy;
-                  thisclump[ CFlxWhtXY ]    += imgss * sx * sy;
+                  /* Sum of the Sky and its STD value. */
+                  oi[ OCOL_SUMSKY ] += *SK;
+                  oi[ OCOL_SUMSTD ] += *ST;
+
+                  /* Get the coordinates of this point. */
+                  gal_dimension_index_to_coord(I-input, ndim, dsize, c);
+
+                  /* Calculate the shifted coordinates for second order
+                     calculations. The coordinate is incremented
+                     because from now on, the positions are in the FITS
+                     standard (starting from one).
+
+                     IMPORTANT NOTE: this is a postfix increment, so after
+                     the expression (difference) is evaluated, the
+                     coordinate is going to change. This is necessary
+                     because `shift' is also starting from zero.    */
+                  for(d=0;d<ndim;++d) sc[d] = c[d] - pp->shift[d];
+
+                  /* Geometric positions and total number. */
+                  ++oi[ OCOL_NUM ];
+                  oi[ OCOL_SUM   ] += ss;
+                  oi[ OCOL_GX    ] += c[1]+1;
+                  oi[ OCOL_GY    ] += c[0]+1;
+                  oi[ OCOL_GXX   ] += sc[1] * sc[1];
+                  oi[ OCOL_GYY   ] += sc[0] * sc[0];
+                  oi[ OCOL_GXY   ] += sc[1] * sc[0];
+                  if(p->clumps && *C>0)
+                    {
+                      ++oi[ OCOL_C_NUM ];
+                      oi[ OCOL_C_SUM   ] += ss;
+                      oi[ OCOL_C_GX    ] += c[1]+1;
+                      oi[ OCOL_C_GY    ] += c[0]+1;
+                    }
+
+                  /* For flux weighted centers, we can only use positive
+                     values, so do those measurements here. */
+                  if( ss > 0.0f )
+                    {
+                      oi[ OCOL_SUMPOS ] += ss;
+                      oi[ OCOL_VX     ] += ss * c[1]+1;
+                      oi[ OCOL_VY     ] += ss * c[0]+1;
+                      oi[ OCOL_VXX    ] += ss * sc[1] * sc[1];
+                      oi[ OCOL_VYY    ] += ss * sc[0] * sc[0];
+                      oi[ OCOL_VXY    ] += ss * sc[1] * sc[0];
+                      if(p->clumps && *C>0)
+                        {
+                          oi[ OCOL_C_SUMPOS ] += ss;
+                          oi[ OCOL_C_VX     ] += ss * (c[1]+1);
+                          oi[ OCOL_C_VY     ] += ss * (c[0]+1);
+                        }
+                    }
                 }
             }
 
+          /* Increment the other pointers. */
+          ++O; ++SK; ++ST; if(p->clumps) ++C;
         }
+      while(++I<II);
+
+      /* Increment to the next contiguous region of this tile. */
+      increment += ( gal_tile_block_increment(p->input, dsize,
+                                              num_increment++, NULL) );
+    }
 
-      /* We are on a detected region but not a clump (with a negative
-         label). This region can be used to find properties like the
-         river fluxs in the vicinity of clumps. */
-      else if (clumps[i]!=GAL_DATA_BLANK_LONG)
-
-        /* We want to check the river pixels in each detection that
-           has a clump. Recall that each detection can host more than
-           one object. Since all the detection's pixels are given to
-           one object, you can check if we are on an object or not
-           with the clumps[i]<0 test.
-
-           The process that keeps the labels of the clumps and their
-           host object that have already been used for each river
-           pixel in wngb wngb is inherited from the getclumpinfo
-           function in NoiseChisel's clump.c.
-
-           There is one big difference. When NoiseChisel was
-           identifying the clumps, there were no `object's. The clumps
-           were all within one detection. But here, the clumps can be
-           separated by a one pixel thick river, but belong to
-           different objects. So wngb has to keep two value for each
-           neighboring clump: the object it belongs to and the clump
-           within that object.
-        */
-        if(clumps[i]<0 && p->oinfo[objects[i]*OCOLUMNS+ONCLUMPS] > 0.0f)
-          {
-            /* Make the preparations: */
-            ii=0;
-            ind=&i;
-            GAL_NEIGHBORS_FILL_8_ALLIMG;
-            nf=(n=ngb)+numngb;
-            memset(wngb, 0, sizeof(wngb));
-
-            /* Go over the neighbors and add the flux of this river
-               pixel to a clump's information if it has not been added
-               already. */
-            do
-              if(clumps[*n]>0)
+  /* Clean up. */
+  free(c);
+  free(sc);
+}
+
+
+
+
+
+/* Do the second pass  */
+static void
+mkcatalog_second_pass(struct mkcatalog_passparams *pp)
+{
+  struct mkcatalogparams *p=pp->p;
+  size_t ndim=p->input->ndim, *dsize=p->input->dsize;
+
+  double *ci;
+  int32_t *O, *C=NULL, nlab, *ngblabs;
+  size_t i, ii, d, increment=0, num_increment=1;
+  size_t nngb=gal_dimension_num_neighbors(ndim);
+  size_t *dinc=gal_dimension_increment(ndim, dsize);
+  float ss, *I, *II, *SK, *ST, *input=p->input->array;
+  size_t *c=gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+  size_t *sc=gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+  int32_t *objects=p->objects->array, *clumps=p->clumps->array;
+
+  /* Allocate array to keep the neighbor labels. */
+  ngblabs=gal_data_malloc_array(GAL_TYPE_INT32, nngb);
+
+  /* Parse each contiguous patch of memory covered by this object. */
+  while( pp->start_end_inc[0] + increment <= pp->start_end_inc[1] )
+    {
+      /* Set the contiguous range to parse, we will check the count
+         over the `I' pointer and just increment the rest. */
+      O  = pp->st_o   + increment;
+      SK = pp->st_sky + increment;
+      ST = pp->st_std + increment;
+      if(p->clumps) C = pp->st_c + increment;
+      II = ( I = pp->st_i + increment ) + pp->tile->dsize[ndim-1];
+
+      /* Parse the next contiguous region of this tile. */
+      do
+        {
+          /* If this pixel belongs to the requested object, is a clumps and
+             isn't NAN, then do the processing. `hasblank' is constant, so
+             when the input doesn't have any blank values, the `isnan' will
+             never be checked. */
+          if( *O==pp->object && !( p->hasblank && isnan(*I) ) )
+            {
+              /* We are on a clump. */
+              if(p->clumps && *C>0)
                 {
-                  /* Go over wngb to see if this river pixel's value
-                     has been added to a segment or not. */
-                  for(j=0;j<ii;++j)
-                    if(wngb[j*2]==objects[*n] && wngb[j*2+1]==clumps[*n])
-                      /* It is already present. break out. */
-                      break;
-
-                  /* First time we are seeing this clump for this
-                     river pixel. */
-                  if(j==ii)
+                  /* Pointer to make things easier. Note that the clump
+                     counters start from 1, but the array indexs from 0.*/
+                  ci=&pp->ci[ (*C-1) * CCOL_NUMCOLS ];
+
+                  /* Total area (independent of threshold). */
+                  ++ci[ CCOL_NUMALL ];
+
+                  /* Sky subtracted value. */
+                  ss = *I - *SK;
+
+                  /* Only use pixels above the threshold, see explanations in
+                     first pass for an explanation. */
+                  if( !( ss < p->threshold * *ST ) )
                     {
-                      /* Note that the object label has to come from
-                         the object label on the neighbor, not the
-                         object label of the river. This river pixel
-                         might be immediately between two clumps on
-                         separate objects, so it will read it
-                         correctly for one clump and incorrectly for
-                         the next. */
-                      cinfo[ ( ofcrow[objects[*n]] + clumps[*n] )
-                             * CCOLUMNS + CRivAve ] += img[i];
-                      ++cinfo[ ( ofcrow[objects[*n]] + clumps[*n] )
-                               * CCOLUMNS + CRivArea ];
-                      wngb[ii*2]=objects[*n];
-                      wngb[ii*2+1]=clumps[*n];
-                      ++ii;
+                      /* Sum of the Sky and its STD value. */
+                      ci[ CCOL_SUMSKY ] += *SK;
+                      ci[ CCOL_SUMSTD ] += *ST;
+
+                      /* Get the coordinates of this point. */
+                      gal_dimension_index_to_coord(I-input, ndim, dsize, c);
+
+                      /* Shifted coordinates for second order moments, see
+                         explanations in the first pass.*/
+                      for(d=0;d<ndim;++d) sc[d] = c[d]++ - pp->shift[d];
+
+                      /* Fill in the necessary information. */
+                      ++ci[ CCOL_NUM ];
+                      ci[ CCOL_SUM   ] += ss;
+                      ci[ CCOL_GX    ] += c[1];
+                      ci[ CCOL_GY    ] += c[0];
+                      ci[ CCOL_GXX   ] += sc[1] * sc[1];
+                      ci[ CCOL_GYY   ] += sc[0] * sc[0];
+                      ci[ CCOL_GXY   ] += sc[1] * sc[0];
+                      if( ss > 0.0f )
+                        {
+                          ci[ CCOL_SUMPOS ] += ss;
+                          ci[ CCOL_VX     ] += ss * c[1];
+                          ci[ CCOL_VY     ] += ss * c[0];
+                          ci[ CCOL_VXX    ] += ss * sc[1] * sc[1];
+                          ci[ CCOL_VYY    ] += ss * sc[0] * sc[0];
+                          ci[ CCOL_VXY    ] += ss * sc[1] * sc[0];
+                        }
                     }
                 }
-            while(++n<nf);
-          }
+
+              /* This pixel is on the diffuse region, check to see if it is
+                 touching a clump or not, but only if this object actually
+                 has any clumps. */
+              else if(pp->clumpsinobj)
+                {
+                  /* We are on a diffuse (possibly a river) pixel. So the
+                     value of this pixel has to be added to any of the
+                     clumps in touches. But since it might touch a labeled
+                     region more than once, we use `ngblabs' to keep track
+                     of which label we have already added its value
+                     to. `ii' is the number of different labels this river
+                     pixel has already been considered for. `ngblabs' will
+                     keep the list labels. */
+                  ii=0;
+                  memset(ngblabs, 0, nngb*sizeof *ngblabs);
+
+                  /* Go over the neighbors and see if this pixel is
+                     touching a clump or not. */
+                  GAL_DIMENSION_NEIGHBOR_OP(I-input, ndim, dsize, ndim, dinc,
+                     {
+                       /* Neighbor's label (mainly for easy reading). */
+                       nlab=clumps[nind];
+
+                       /* We only want neighbors that are a clump and part
+                          of this object and part of the same object. */
+                       if( nlab>0 && objects[nind]==pp->object)
+                         {
+                           /* Go over all already checked labels and make
+                              sure this clump hasn't already been
+                              considered. */
+                           for(i=0;i<ii;++i) if(ngblabs[i]==nlab) break;
+
+                           /* It hasn't been considered yet: */
+                           if(i==ii)
+                             {
+                               ngblabs[ii++] = nlab;
+
+                               ++(pp->ci)[ (nlab-1) * CCOL_NUMCOLS
+                                           + CCOL_RIV_NUM ];
+                               pp->ci[ (nlab-1) * CCOL_NUMCOLS
+                                       + CCOL_RIV_SUM ] += *I-*SK;
+                             }
+                         }
+                     });
+                }
+            }
+
+          /* Increment the other pointers. */
+          ++O; ++SK; ++ST; if(p->clumps) ++C;
+        }
+      while(++I<II);
+
+      /* Increment to the next contiguous region of this tile. */
+      increment += ( gal_tile_block_increment(p->input, dsize,
+                                              num_increment++, NULL) );
     }
 
-  /* Clean up: */
-  free(ofcrow);
+  /* Clean up. */
+  free(c);
+  free(sc);
+  free(dinc);
+  free(ngblabs);
 }
 
 
@@ -393,371 +418,438 @@ clumppass(struct mkcatalogparams *p)
 
 
 
+
 /*********************************************************************/
-/*****************           Make output           *******************/
+/*****************       High-level funcitons      *******************/
 /*********************************************************************/
-void
-makeoutput(struct mkcatalogparams *p)
+static void
+mkcatalog_clump_starting_index(struct mkcatalog_passparams *pp)
 {
-  double sn, pixarea;
-  size_t col, *cols, tmpcol;
-  char comment[COMMENTSIZE], tline[100], *target;
-  int prec[2]={p->floatprecision, p->accuprecision};
-  int space[3]={p->intwidth, p->floatwidth, p->accuwidth};
+  struct mkcatalogparams *p=pp->p;
+
+  /* Lock the mutex if we are working on more than one thread. NOTE: it is
+     very important to keep the number of operations within the mutex to a
+     minimum so other threads don't get delayed. */
+  if(p->cp.numthreads>1)
+    pthread_mutex_lock(&p->mutex);
+
+  /* Put the current total number of rows filled into the output, then
+     increment the total number by the number of clumps. */
+  pp->clumpstartindex = p->clumprowsfilled;
+  p->clumprowsfilled += pp->clumpsinobj;
+
+  /* Unlock the mutex (if it was locked). */
+  if(p->cp.numthreads>1)
+    pthread_mutex_unlock(&p->mutex);
+}
+
 
 
-  /* Calculate the pixel area in arcseconds^2: */
-  pixarea=gal_wcs_pixel_area_arcsec2(p->wcs);
 
 
-  /* First make the objects catalog, then the clumps catalog. */
-  for(p->obj0clump1=0;p->obj0clump1<2;++p->obj0clump1)
+/* Each thread will call this function once. It will go over all the
+   objects that are assigned to it. */
+static void *
+mkcatalog_single_object(void *in_prm)
+{
+  struct gal_threads_params *tprm=(struct gal_threads_params *)in_prm;
+  struct mkcatalogparams *p=(struct mkcatalogparams *)(tprm->params);
+  size_t ndim=p->input->ndim;
+
+  size_t i;
+  struct mkcatalog_passparams pp;
+
+  /* Initialize the mkcatalog_passparams elements. */
+  pp.p               = p;
+  pp.clumpstartindex = 0;
+  pp.rng             = p->rng ? gsl_rng_clone(p->rng) : NULL;
+  pp.shift           = gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+  pp.oi              = gal_data_malloc_array(GAL_TYPE_FLOAT64, OCOL_NUMCOLS);
+
+  /* If we have upper-limit mode, then allocate the container to keep the
+     values to calculate the standard deviation. */
+  pp.up_vals = p->upperlimit ? gal_data_alloc(NULL, GAL_TYPE_FLOAT32, 1,
+                                              &p->upnum, NULL, 0,
+                                              p->cp.minmapsize, NULL, NULL,
+                                              NULL) : NULL;
+
+  /* Fill the desired columns for all the objects given to this thread. */
+  for(i=0; tprm->indexs[i]!=GAL_BLANK_SIZE_T; ++i)
     {
+      /* For easy reading, Note that the object IDs start from one while
+         the array positions start from 0. */
+      pp.ci=NULL;
+      pp.object = tprm->indexs[i] + 1;
+      pp.tile   = &p->tiles[ tprm->indexs[i] ];
 
-      /* If no clumps image was provided, then ignore the clumps
-         catalog. */
-      if(p->obj0clump1==1 && p->clumps==NULL)
-        continue;
+      /* Initialize the parameters for this object/tile. */
+      mkcatalog_initialize_params(&pp);
 
+      /* Get the first pass information. */
+      mkcatalog_first_pass(&pp);
 
-      /* Do the preparations for this round: */
-      p->intcounter=p->accucounter=p->curcol=0;
-      p->name      = p->obj0clump1 ? "clump" : "object";
-      p->icols     = p->obj0clump1 ? CCOLUMNS : OCOLUMNS;
-      p->info      = p->obj0clump1 ? p->cinfo : p->oinfo;
-      p->cat       = p->obj0clump1 ? p->clumpcat : p->objcat;
-      target       = p->obj0clump1 ? MKCATCLUMP : MKCATOBJECT;
-      p->filename  = p->obj0clump1 ? p->ccatname : p->ocatname;
-      cols         = p->obj0clump1 ? p->clumpcols : p->objcols;
-      p->numcols   = p->obj0clump1 ? p->clumpncols : p->objncols;
-      p->num       = p->obj0clump1 ? p->numclumps : p->numobjects;
+      /* Currently the second pass is only necessary when there is a clumps
+         image. */
+      if(p->clumps)
+        {
+          /* Allocate space for the properties of each clump. */
+          pp.ci = gal_data_calloc_array(GAL_TYPE_FLOAT64,
+                                        pp.clumpsinobj * CCOL_NUMCOLS);
 
+          /* Get the starting row of this object's clumps in the final
+             catalog. This index is also necessary for the unique random
+             number generator seeds of each clump. */
+          mkcatalog_clump_starting_index(&pp);
 
+          /* Get the second pass information. */
+          mkcatalog_second_pass(&pp);
+        }
 
-      /* Allocate the integer and accuracy arrays: */
-      errno=0; p->intcols=malloc(p->numcols*sizeof *p->intcols);
-      if(p->intcols==NULL)
-        error(EXIT_FAILURE, errno, "%zu bytes for intcols in makeoutput "
-              "(mkcatalog.c)", p->numcols*sizeof *p->intcols);
-      errno=0; p->accucols=malloc(p->numcols*sizeof *p->accucols);
-      if(p->accucols==NULL)
-        error(EXIT_FAILURE, errno, "%zu bytes for accucols in makeoutput "
-              "(mkcatalog.c)", p->numcols*sizeof *p->accucols);
+      /* Calculate the upper limit magnitude (if necessary). */
+      if(p->upperlimit) upperlimit_calculate(&pp);
 
+      /* Write the pass information into the columns. */
+      columns_fill(&pp);
 
+      /* Clean up for this object. */
+      if(pp.ci) free(pp.ci);
+    }
 
-      /* Write the top of the comments: */
-      sprintf(comment, "# %s %s catalog.\n", SPACK_STRING, p->name);
-      sprintf(p->line, "# %s started on %s", SPACK_NAME, ctime(&p->rawtime));
-      strcat(comment, p->line);
-      if(gal_git_describe())
-        {
-          sprintf(p->line, "# From commit %s\n", gal_git_describe());
-          strcat(comment, p->line);
-        }
+  /* Clean up. */
+  free(pp.oi);
+  free(pp.shift);
+  gal_data_free(pp.up_vals);
+  if(pp.rng) gsl_rng_free(pp.rng);
 
+  /* Wait until all the threads finish and return. */
+  if(tprm->b) pthread_barrier_wait(tprm->b);
+  return NULL;
+}
 
-      /* Write the input files: */
-      strcat(comment, "#\n# Input files and information:\n"
-             "# ----------------------------\n");
-      sprintf(p->line, "# Input   %s (hdu: %s)\n", p->up.inputname,
-              p->cp.hdu);
-      strcat(comment, p->line);
-      if(p->up.masknameset)
-        {
-          sprintf(p->line, "# Mask   %s (hdu: %s)\n", p->up.maskname,
-                  p->up.mhdu);
-          strcat(comment, p->line);
-        }
-      sprintf(p->line, "# Objects %s (hdu: %s)\n", p->up.objlabsname,
-              p->up.objhdu);
-      strcat(comment, p->line);
-      if(p->up.clumplabsname)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*********************************************************************/
+/********         Processing after threads finish        *************/
+/*********************************************************************/
+/* Convert internal image coordinates to WCS for table.
+
+   Note that from the beginning (during the passing steps), we saved FITS
+   coordinates. Also note that we are doing the conversion in place. */
+static void
+mkcatalog_wcs_conversion(struct mkcatalogparams *p)
+{
+  double *c, *d, *df;
+  gal_data_t *column;
+
+  /* Flux weighted center positions for clumps and objects. */
+  if(p->rd_vo)
+    {
+      gal_wcs_img_to_world(p->input->wcs, p->rd_vo[0], p->rd_vo[1],
+                           &p->rd_vo[0], &p->rd_vo[1], p->numobjects);
+      if(p->rd_vc)
+        gal_wcs_img_to_world(p->input->wcs, p->rd_vc[0], p->rd_vc[1],
+                             &p->rd_vc[0], &p->rd_vc[1], p->numclumps);
+    }
+
+  /* Geometric center positions for clumps and objects. */
+  if(p->rd_go)
+    {
+      gal_wcs_img_to_world(p->input->wcs, p->rd_go[0], p->rd_go[1],
+                           &p->rd_go[0], &p->rd_go[1], p->numobjects);
+      if(p->rd_gc)
+        gal_wcs_img_to_world(p->input->wcs, p->rd_gc[0], p->rd_gc[1],
+                             &p->rd_gc[0], &p->rd_gc[1], p->numclumps);
+    }
+
+  /* All clumps flux weighted center. */
+  if(p->rd_vcc)
+    gal_wcs_img_to_world(p->input->wcs, p->rd_vcc[0], p->rd_vcc[1],
+                         &p->rd_vcc[0], &p->rd_vcc[1], p->numobjects);
+
+  /* All clumps geometric center. */
+  if(p->rd_gcc)
+    gal_wcs_img_to_world(p->input->wcs, p->rd_gcc[0], p->rd_gcc[1],
+                         &p->rd_gcc[0], &p->rd_gcc[1], p->numobjects);
+
+
+  /* Go over all the object columns and fill in the values. */
+  for(column=p->objectcols; column!=NULL; column=column->next)
+    {
+      /* Definitions */
+      c=NULL;
+
+      /* Set `c' for the columns that must be corrected. Note that this
+         `switch' statement doesn't need any `default', because there are
+         probably columns that don't need any correction. */
+      switch(column->status)
         {
-          sprintf(p->line, "# Clumps  %s (hdu: %s)\n", p->up.clumplabsname,
-                  p->up.clumphdu);
-          strcat(comment, p->line);
+        case UI_KEY_RA:           c=p->rd_vo[0];   break;
+        case UI_KEY_DEC:          c=p->rd_vo[1];   break;
+        case UI_KEY_GEORA:        c=p->rd_go[0];   break;
+        case UI_KEY_GEODEC:       c=p->rd_go[1];   break;
+        case UI_KEY_CLUMPSRA:     c=p->rd_vcc[0];  break;
+        case UI_KEY_CLUMPSDEC:    c=p->rd_vcc[1];  break;
+        case UI_KEY_CLUMPSGEORA:  c=p->rd_gcc[0];  break;
+        case UI_KEY_CLUMPSGEODEC: c=p->rd_gcc[1];  break;
         }
-      sprintf(p->line, "# Sky     %s (hdu: %s)\n", p->up.skyname,
-              p->up.skyhdu);
-      strcat(comment, p->line);
-      sprintf(p->line, "# Sky STD %s (hdu: %s)\n", p->up.stdname,
-              p->up.stdhdu);
-      strcat(comment, p->line);
-      if(p->obj0clump1==0 && p->upmask)
+
+      /* Copy the elements. */
+      if(c) { df=(d=column->array)+column->size; do *d=*c++; while(++d<df); }
+    }
+
+
+  /* Go over all the clump columns and fill in the values. */
+  for(column=p->clumpcols; column!=NULL; column=column->next)
+    {
+      /* Definitions */
+      c=NULL;
+
+      /* Set `c' for the columns that must be corrected. Note that this
+         `switch' statement doesn't need any `default', because there are
+         probably columns that don't need any correction. */
+      switch(column->status)
         {
-          sprintf(p->line, "# Upper limit magnitude mask %s (hdu: %s)\n",
-                  p->up.upmaskname, p->up.upmaskhdu);
-          strcat(comment, p->line);
+        case UI_KEY_RA:           c=p->rd_vc[0];   break;
+        case UI_KEY_DEC:          c=p->rd_vc[1];   break;
+        case UI_KEY_GEORA:        c=p->rd_gc[0];   break;
+        case UI_KEY_GEODEC:       c=p->rd_gc[1];   break;
         }
 
+      /* Copy the elements. */
+      if(c) { df=(d=column->array)+column->size; do *d=*c++; while(++d<df); }
+    }
+}
 
 
 
-      /* If a magnitude is also desired, print the zero point
-         magnitude and the 5sigma magnitude: */
-      sprintf(p->line, "# "CATDESCRIPTLENGTH"%.3f\n",
-              "Zero point magnitude:", p->zeropoint);
-      strcat(comment, p->line);
-      sprintf(tline, "Pixel %g sigma surface brightness (magnitude)",
-              p->nsigmag);
-      sprintf(p->line, "# "CATDESCRIPTLENGTH"%.3f\n",
-              tline, -2.5f*log10(p->nsigmag*p->medstd)+p->zeropoint );
-      strcat(comment, p->line);
 
-      sn = p->obj0clump1 ? p->clumpsn : p->detsn;
-      if(!isnan(sn))
-        {
-          sprintf(tline, "%s limiting Signal to noise ratio: ",
-                  p->obj0clump1 ? "Clump" : "Detection");
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%.3f\n", tline, sn);
-          strcat(comment, p->line);
-          if(p->obj0clump1==0)
-            strcat(comment, "# (NOTE: limits above are for detections, not "
-                   "objects)\n");
-        }
 
+/* Write the similar information. */
+static struct gal_linkedlist_stll *
+mkcatalog_outputs_same_start(struct mkcatalogparams *p, int o0c1,
+                             char *ObjClump)
+{
+  char *str;
+  float snlim;
+  struct gal_linkedlist_stll *comments=NULL;
+  char *skyfile=p->skyfile ? p->skyfile : p->inputname;
+  char *stdfile=p->stdfile ? p->stdfile : p->inputname;
+  char *clumpsfile=p->clumpsfile ? p->clumpsfile : p->inputname;
+  char *objectsfile=p->objectsfile ? p->objectsfile : p->inputname;
+
+  asprintf(&str, "%s %s catalog.", PROGRAM_STRING, o0c1 ? "object" : "clump");
+  gal_linkedlist_add_to_stll(&comments, str, 0);
+
+  /* If in a Git controlled directory and output isn't a FITS file (in
+     FITS, this will be automatically included). */
+  if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT && gal_git_describe())
+    {
+      asprintf(&str, "# Directory commit %s\n", gal_git_describe());
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
 
-      /* If cpscorr was used, report it: */
-      sprintf(p->line, "# "CATDESCRIPTLENGTH"%.3f\n",
-              "Counts-per-second correction:", 1/p->cpscorr);
-      strcat(comment, p->line);
+  asprintf(&str, "%s started on %s", PROGRAM_NAME, ctime(&p->rawtime));
+  gal_linkedlist_add_to_stll(&comments, str, 0);
 
 
+  if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT)
+    {
+      asprintf(&str, "--------- Input files ---------");
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
 
-      /* Report the area of each pixel in stradians: */
-      sprintf(p->line, "# "CATDESCRIPTLENGTH"%g\n",
-              "Pixel area (arcsec^2)", pixarea);
-      strcat(comment, p->line);
+  asprintf(&str, "Values:  %s (hdu: %s).", p->inputname, p->cp.hdu);
+  gal_linkedlist_add_to_stll(&comments, str, 0);
+
+  asprintf(&str, "Objects: %s (hdu: %s).", objectsfile, p->objectshdu);
+  gal_linkedlist_add_to_stll(&comments, str, 0);
+
+  if(p->clumps)
+    {
+      asprintf(&str, "Clumps:  %s (hdu: %s).", clumpsfile, p->clumpshdu);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  asprintf(&str, "Sky:     %s (hdu: %s).", skyfile, p->skyhdu);
+  gal_linkedlist_add_to_stll(&comments, str, 0);
+
+  asprintf(&str, "Sky STD: %s (hdu: %s).", stdfile, p->stdhdu);
+  gal_linkedlist_add_to_stll(&comments, str, 0);
+
+  if(p->upmaskfile)
+    {
+      asprintf(&str, "Upperlimit mask: %s (hdu: %s).", p->upmaskfile,
+               p->upmaskhdu);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT)
+    {
+      asprintf(&str, "--------- Supplimentary information ---------");
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  if(p->input->wcs)
+    {
+      asprintf(&str, "Pixel area (arcsec^2): %g",
+               gal_wcs_pixel_area_arcsec2(p->input->wcs));
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  if(p->hasmag)
+    {
+      asprintf(&str, "Zeropoint magnitude: %.4f", p->zeropoint);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  if( !isnan(p->zeropoint) )
+    {
+      asprintf(&str, "Pixel %g sigma surface brightness (magnitude): %.3f",
+               p->nsigmag, -2.5f*log10(p->nsigmag*p->medstd)+p->zeropoint);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  snlim = o0c1 ? p->clumpsn : p->detsn;
+  if( !isnan(snlim) )
+    {
+      asprintf(&str, "%s limiting signal-to-noise ratio: %.3f", ObjClump,
+               snlim);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  if(o0c1==0)
+    {
+      asprintf(&str, "(NOTE: S/N limit above is for pseudo-detections, "
+               "not objects.)");
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  if(p->cpscorr>1.0f)
+    {
+      asprintf(&str, "Counts-per-second correction: %.3f", p->cpscorr);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  if( !isnan(p->threshold) )
+    {
+      asprintf(&str, "**IMPORTANT** Pixel threshold (multiple of local "
+               "std): %.3f", p->threshold);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
 
-      /* if a threshold was used then report it: */
-      if(!isnan(p->threshold))
-        {
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%g\n", "**IMPORTANT** "
-                  "Pixel threshold (multiple of local std)",
-                  p->threshold);
-          strcat(comment, p->line);
-        }
 
-      /* If an upper limit was output then report its parameters: */
-      if(p->obj0clump1==0 && p->up.upperlimitmagset)
+  if(p->upperlimit)
+    {
+      if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT)
         {
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%zu\n", "Number of upper "
-                  "limit magnitude samples", p->upnum);
-          strcat(comment, p->line);
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%zu\n", "Number of threads "
-                  "used for upper limit magnitude",
-                  p->cp.numthreads);
-          strcat(comment, p->line);
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%s\n", "Random number "
-                  "generator type for upper limit magnitude",
-                  gsl_rng_default->name);
-          strcat(comment, p->line);
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%zu\n", "Random number "
-                  "generator seed for upper limit magnitude",
-                  gsl_rng_default_seed);
-          strcat(comment, p->line);
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%.3f\n", "STD multiple for "
-                  "upper limit magnitude sigma-clip", p->upsclipmultip);
-          strcat(comment, p->line);
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%.3f\n", "STD accuracy "
-                  "to stop upper limit magnitude sigma-clip", p->upsclipaccu);
-          strcat(comment, p->line);
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"%.3f\n", "Multiple of "
-                  "sigma for final upper limit magnitude", p->upnsigma);
-          strcat(comment, p->line);
+          asprintf(&str, "--------- Upper-limit measurement ---------");
+          gal_linkedlist_add_to_stll(&comments, str, 0);
         }
 
+      asprintf(&str, "Number of random samples: %zu", p->upnum);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
 
-      /* Prepare for printing the columns: */
-      strcat(comment, "#\n# Columns:\n# --------\n");
+      asprintf(&str, "Random number generator name: %s", p->rngname);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
 
+      asprintf(&str, "Random number generator seed: %lu", p->seed);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
 
-      /* Fill the catalog array, in the end set the last elements in
-         intcols and accucols to -1, so gal_txtarray_array_to_txt knows
-         when to stop. */
-      for(p->curcol=0;p->curcol<p->numcols;++p->curcol)
-        {
-          col=cols[p->curcol];
-          switch(col)
-            {
+      asprintf(&str, "Multiple of STD used for sigma-clipping: %.3f",
+               p->upsigmaclip[0]);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
 
-            case CATID:
-              idcol(p);
-              break;
-
-            case CATHOSTOBJID:
-              hostobj(p, 1);
-              break;
-
-            case CATIDINHOSTOBJ:
-              hostobj(p, 0);
-              break;
-
-            case CATNUMCLUMPS:
-              numclumps(p);
-              break;
-
-            case CATAREA:
-              area(p, 0, 0);
-              break;
-
-            case CATCLUMPSAREA:
-              area(p, 1, 0);
-              break;
-
-
-            case CATX:
-              tmpcol = p->obj0clump1 ? CFlxWhtX : OFlxWhtX;
-              position(p, tmpcol, target, MKCATWHTC, MKCATX);
-              break;
-
-            case CATY:
-              tmpcol = p->obj0clump1 ? CFlxWhtY : OFlxWhtY;
-              position(p, tmpcol, target, MKCATWHTC, MKCATY);
-              break;
-
-            case CATGEOX:
-              tmpcol = p->obj0clump1 ? CGeoX : OGeoX;
-              position(p, tmpcol, target, MKCATGEOC, MKCATX);
-              break;
-
-            case CATGEOY:
-              tmpcol = p->obj0clump1 ? CGeoY : OGeoY;
-              position(p, tmpcol, target, MKCATGEOC, MKCATY);
-              break;
-
-            case CATCLUMPSX:
-              position(p, OFlxWhtCX, MKCATCINO, MKCATWHTC, MKCATX);
-              break;
-
-            case CATCLUMPSY:
-              position(p, OFlxWhtCY, MKCATCINO, MKCATWHTC, MKCATY);
-              break;
-
-            case CATCLUMPSGEOX:
-              position(p, OGeoCX, MKCATCINO, MKCATGEOC, MKCATX);
-              break;
-
-            case CATCLUMPSGEOY:
-              position(p, OGeoCY, MKCATCINO, MKCATGEOC, MKCATY);
-              break;
-
-            case CATRA:
-              tmpcol = p->obj0clump1 ? CFlxWhtRA : OFlxWhtRA;
-              position(p, tmpcol, target, MKCATWHTC, MKCATRA);
-              break;
-
-            case CATDEC:
-              tmpcol = p->obj0clump1 ? CFlxWhtDec : OFlxWhtDec;
-              position(p, tmpcol, target, MKCATWHTC, MKCATDEC);
-              break;
-
-            case CATGEORA:
-              tmpcol = p->obj0clump1 ? CGeoRA : OGeoRA;
-              position(p, tmpcol, target, MKCATGEOC, MKCATRA);
-              break;
-
-            case CATGEODEC:
-              tmpcol = p->obj0clump1 ? CGeoDec : OGeoDec;
-              position(p, tmpcol, target, MKCATGEOC, MKCATDEC);
-              break;
-
-            case CATCLUMPSRA:
-              position(p, OFlxWhtCRA, MKCATCINO, MKCATWHTC, MKCATRA);
-              break;
-
-            case CATCLUMPSDEC:
-              position(p, OFlxWhtCDec, MKCATCINO, MKCATWHTC, MKCATDEC);
-              break;
-
-            case CATCLUMPSGEORA:
-              position(p, OGeoCRA, MKCATCINO, MKCATGEOC, MKCATRA);
-              break;
-
-            case CATCLUMPSGEODEC:
-              position(p, OGeoCDec, MKCATCINO, MKCATGEOC, MKCATDEC);
-              break;
-
-            case CATBRIGHTNESS:
-              tmpcol = p->obj0clump1 ? CBrightness : OBrightness;
-              brightnessmag(p, tmpcol, target, MKCATBRIGHT);
-              break;
-
-            case CATCLUMPSBRIGHTNESS:
-              brightnessmag(p, OBrightnessC, MKCATCINO, MKCATBRIGHT);
-              break;
-
-            case CATNORIVERBRIGHTNESS:
-              brightnessmag(p, CNoRiverBrightness, target, MKCATBRIGHT);
-              break;
-
-            case CATMAGNITUDE:
-              tmpcol = p->obj0clump1 ? CBrightness : OBrightness;
-              brightnessmag(p, tmpcol, target, MKCATMAG);
-              break;
-
-            case CATMAGNITUDEERR:
-              sncol(p, 1, target);
-              break;
-
-            case CATCLUMPSMAGNITUDE:
-              brightnessmag(p, OBrightnessC, MKCATCINO, MKCATMAG);
-              break;
-
-            case CATUPPERLIMITMAG:
-              upperlimitcol(p);
-              break;
-
-            case CATRIVERAVE:
-              brightnessmag(p, CRivAve, MKRIVERSSUR, MKCATBRIGHT);
-              break;
-
-            case CATRIVERNUM:
-              area(p, 0, 1);
-              break;
-
-            case CATSN:
-              sncol(p, 0, target);
-              break;
-
-            case CATSKY:
-              skystd(p, p->obj0clump1 ? CSKY : OSKY);
-              break;
-
-            case CATSTD:
-              skystd(p, p->obj0clump1 ? CSTD : OSTD);
-              break;
-
-            case CATSEMIMAJOR: case CATSEMIMINOR: case CATPOSITIONANGLE:
-            case CATGEOSEMIMAJOR: case CATGEOSEMIMINOR:
-            case CATGEOPOSITIONANGLE:
-              secondordermoment(p, col, target);
-              break;
-
-            default:
-              error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                    "can fix the problem. The value to cols[%zu] (%zu), is "
-                    "not recognized in makeoutput (mkcatalog.c)",
-                    PACKAGE_BUGREPORT, p->curcol, cols[p->curcol]);
-            }
+      if(p->upsigmaclip[1]>=1.0f)
+        asprintf(&str, "Number of clips for sigma-clipping: %.0f",
+                 p->upsigmaclip[1]);
+      else
+        asprintf(&str, "Tolerance level to sigma-clipping: %.3f",
+                 p->upsigmaclip[1]);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
 
-          sprintf(p->line, "# "CATDESCRIPTLENGTH"[%s]\n",
-                  p->description, p->unitp);
-          strcat(comment, p->line);
-        }
-      p->intcols[p->intcounter]=p->accucols[p->accucounter]=-1;
+      asprintf(&str, "Multiple of sigma-clipped STD for upper-limit: %.3f",
+               p->upnsigma);
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
 
 
 
-      /* Write the catalog to file: */
-      gal_txtarray_array_to_txt(p->cat, p->num, p->numcols, comment,
-                                p->intcols, p->accucols, space, prec,
-                                'f', p->filename);
+  if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT)
+    {
+      asprintf(&str, "-------------------------------------------\n");
+      gal_linkedlist_add_to_stll(&comments, str, 0);
 
-      /* Clean up: */
-      free(p->intcols);
-      free(p->accucols);
+      asprintf(&str, "--------- Table columns ---------");
+      gal_linkedlist_add_to_stll(&comments, str, 0);
+    }
+
+  /* Return the comments. */
+  return comments;
+}
+
+
+
+
+
+/* Write the produced columns into the output */
+static void
+mkcatalog_write_outputs(struct mkcatalogparams *p)
+{
+  /*char *str;*/
+  struct gal_linkedlist_stll *comments;
+
+
+  /* OBJECT CATALOG
+     ============== */
+  comments=mkcatalog_outputs_same_start(p, 0, "Detection");
+
+
+  /* Write objects catalog
+     ---------------------
+
+     Reverse the comments list (so it is printed in the same order here),
+     write the objects catalog and free the comments. */
+  gal_linkedlist_reverse_stll(&comments);
+  gal_table_write(p->objectcols, comments, p->cp.tableformat, p->objectsout,
+                  p->cp.dontdelete);
+  gal_linkedlist_free_stll(comments, 1);
+
+
+
+  /* OBJECT CATALOG
+     ============== */
+  if(p->clumps)
+    {
+      comments=mkcatalog_outputs_same_start(p, 1, "Clumps");
+
+
+
+      /* Write objects catalog
+         ---------------------
+
+         Reverse the comments list (so it is printed in the same order here),
+         write the objects catalog and free the comments. */
+      gal_linkedlist_reverse_stll(&comments);
+      gal_table_write(p->clumpcols, comments, p->cp.tableformat,
+                      p->clumpsout, p->cp.dontdelete);
+      gal_linkedlist_free_stll(comments, 1);
     }
 }
 
@@ -781,19 +873,30 @@ makeoutput(struct mkcatalogparams *p)
 
 
 /*********************************************************************/
-/*****************          Main function          *******************/
+/*****************       Top-level function        *******************/
 /*********************************************************************/
 void
 mkcatalog(struct mkcatalogparams *p)
 {
-  /* Run through the data for the first time: */
-  firstpass(p);
-  if(p->clumps) clumppass(p);
+  /* When more than one thread is to be used, initialize the mutex: we need
+     it to assign a column to the clumps in the final catalog. */
+  if( p->cp.numthreads > 1 ) pthread_mutex_init(&p->mutex, NULL);
+
+
+  /* Do the processing on each thread. */
+  gal_threads_spin_off(mkcatalog_single_object, p, p->numobjects,
+                       p->cp.numthreads);
+
+
+  /* Post-thread processing, for example to convert image coordinates to RA
+     and Dec. */
+  mkcatalog_wcs_conversion(p);
+
+
+  /* Write the filled columns into the output. */
+  mkcatalog_write_outputs(p);
 
-  /* Write the output: */
-  makeoutput(p);
 
-  /* Clean up: */
-  free(p->oinfo);
-  free(p->cinfo);
+  /* Destroy the mutex. */
+  if( p->cp.numthreads>1 ) pthread_mutex_destroy(&p->mutex);
 }
diff --git a/bin/mkcatalog/mkcatalog.h b/bin/mkcatalog/mkcatalog.h
index 13bf4c2..bdae1cb 100644
--- a/bin/mkcatalog/mkcatalog.h
+++ b/bin/mkcatalog/mkcatalog.h
@@ -23,12 +23,25 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #ifndef MKCATALOG_H
 #define MKCATALOG_H
 
-#define WNGBSIZE        20      /* wngb: indexs of river neighbors. */
-#define COMMENTSIZE  10000
-
-#define COLINFOINT       1
-#define COLINFOFLOAT     2
-#define COLINFOACCU      3
+struct mkcatalog_passparams
+{
+  struct mkcatalogparams *p;    /* Main MakeCatalog paramers.           */
+  double                *oi;    /* Intermediate values for objects.     */
+  double                *ci;    /* Intermediate values for clumps.      */
+  int32_t            object;    /* Object that is currently working on. */
+  size_t        clumpsinobj;    /* The number of clumps in this object. */
+  gal_data_t          *tile;    /* The tile to pass-over.               */
+  float               *st_i;    /* Starting pointer for input image.    */
+  int32_t             *st_o;    /* Starting pointer for objects image.  */
+  int32_t             *st_c;    /* Starting pointer for objects image.  */
+  float             *st_sky;    /* Starting pointer for input image.    */
+  float             *st_std;    /* Starting pointer for input image.    */
+  size_t   start_end_inc[2];    /* Starting and ending indexs.          */
+  size_t             *shift;    /* Shift coordinates for coordinates.   */
+  gsl_rng              *rng;    /* Random number generator.             */
+  size_t    clumpstartindex;    /* Clump starting row in final catalog. */
+  gal_data_t       *up_vals;    /* Container for upper-limit values.    */
+};
 
 void
 mkcatalog(struct mkcatalogparams *p);
diff --git a/bin/mkcatalog/ui.c b/bin/mkcatalog/ui.c
index 29d2f96..1cad0d1 100644
--- a/bin/mkcatalog/ui.c
+++ b/bin/mkcatalog/ui.c
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <address@hidden>
 Contributing author(s):
-Copyright (C) 2015, Free Software Foundation, Inc.
+Copyright (C) 2016, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -22,39 +22,66 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 **********************************************************************/
 #include <config.h>
 
-#include <math.h>
-#include <stdio.h>
+#include <argp.h>
 #include <errno.h>
 #include <error.h>
-#include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
-#include <fitsio.h>
 
+#include <gnuastro/wcs.h>
 #include <gnuastro/fits.h>
-#include <gnuastro/txtarray.h>
+#include <gnuastro/blank.h>
+#include <gnuastro/threads.h>
+#include <gnuastro/dimension.h>
+#include <gnuastro/arithmetic.h>
 #include <gnuastro/statistics.h>
+#include <gnuastro/linkedlist.h>
 
-#include <nproc.h>               /* From Gnulib.                   */
-#include <timing.h>              /* Includes time.h and sys/time.h */
-#include <checkset.h>
-#include <commonargs.h>
-#include <configfiles.h>
+#include <gnuastro-internal/timing.h>
+#include <gnuastro-internal/options.h>
+#include <gnuastro-internal/checkset.h>
+#include <gnuastro-internal/fixedstringmacros.h>
 
 #include "main.h"
+#include "mkcatalog.h"
 
-#include "ui.h"                  /* Needs main.h                   */
-#include "args.h"                /* Needs main.h, includes argp.h. */
+#include "ui.h"
+#include "columns.h"
+#include "authors-cite.h"
 
 
-/* Set the file names of the places where the default parameters are
-   put. */
-#define CONFIG_FILE SPACK CONF_POSTFIX
-#define SYSCONFIG_FILE SYSCONFIG_DIR "/" CONFIG_FILE
-#define USERCONFIG_FILEEND USERCONFIG_DIR CONFIG_FILE
-#define CURDIRCONFIG_FILE CURDIRCONFIG_DIR CONFIG_FILE
 
 
 
+/**************************************************************/
+/*********      Argp necessary global entities     ************/
+/**************************************************************/
+/* Definition parameters for the Argp: */
+const char *
+argp_program_version = PROGRAM_STRING "\n"
+                       GAL_STRINGS_COPYRIGHT
+                       "\n\nWritten/developed by "PROGRAM_AUTHORS;
+
+const char *
+argp_program_bug_address = PACKAGE_BUGREPORT;
+
+static char
+args_doc[] = "ASTRdata";
+
+const char
+doc[] = GAL_STRINGS_TOP_HELP_INFO PROGRAM_NAME" will create a catalog from "
+  "an input, labeled, and noise images.\n"
+  GAL_STRINGS_MORE_HELP_INFO
+  /* After the list of options: */
+  "\v"
+  PACKAGE_NAME" home page: "PACKAGE_URL;
+
+/* Option groups particular to this program. */
+enum program_args_groups
+{
+  ARGS_GROUP_UPPERLIMIT = GAL_OPTIONS_GROUP_AFTER_COMMON,
+  ARGS_GROUP_COLUMNS,
+};
 
 
 
@@ -62,855 +89,166 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/**************************************************************/
-/**************       Options and parameters    ***************/
-/**************************************************************/
-void
-readconfig(char *filename, struct mkcatalogparams *p)
-{
-  int yes;
-  FILE *fp;
-  size_t lineno=0, len=200;
-  char *line, *name, *value;
-  struct uiparams *up=&p->up;
-  struct gal_commonparams *cp=&p->cp;
-  char key='a';        /* Not used, just a place holder. */
-
-  /* When the file doesn't exist or can't be opened, it is ignored. It
-     might be intentional, so there is no error. If a parameter is
-     missing, it will be reported after all defaults are read. */
-  fp=fopen(filename, "r");
-  if (fp==NULL) return;
-
-
-  /* Allocate some space for `line` with `len` elements so it can
-     easily be freed later on. The value of `len` is arbitarary at
-     this point, during the run, getline will change it along with the
-     pointer to line. */
-  errno=0;
-  line=malloc(len*sizeof *line);
-  if(line==NULL)
-    error(EXIT_FAILURE, errno, "ui.c: %zu bytes in readdefaults",
-          len * sizeof *line);
 
-  /* Read the tokens in the file:  */
-  while(getline(&line, &len, fp) != -1)
-    {
-      /* Prepare the "name" and "value" strings, also set lineno. */
-      GAL_CONFIGFILES_START_READING_LINE;
-
-
-
-
-      /* Inputs: */
-      if(strcmp(name, "hdu")==0)
-        gal_checkset_allocate_copy_set(value, &cp->hdu, &cp->hduset);
-      else if (strcmp(name, "mask")==0)
-        gal_checkset_allocate_copy_set(value, &up->maskname,
-                                       &up->masknameset);
-      else if (strcmp(name, "mhdu")==0)
-        gal_checkset_allocate_copy_set(value, &up->mhdu, &up->mhduset);
-      else if (strcmp(name, "objlabs")==0)
-        gal_checkset_allocate_copy_set(value, &up->objlabsname,
-                                       &up->objlabsnameset);
-      else if (strcmp(name, "objhdu")==0)
-        gal_checkset_allocate_copy_set(value, &up->objhdu,
-                                       &up->objhduset);
-      else if (strcmp(name, "clumplabs")==0)
-        gal_checkset_allocate_copy_set(value, &up->clumplabsname,
-                                       &up->clumplabsnameset);
-      else if (strcmp(name, "clumphdu")==0)
-        gal_checkset_allocate_copy_set(value, &up->clumphdu,
-                                       &up->clumphduset);
-      else if (strcmp(name, "skyfilename")==0)
-        gal_checkset_allocate_copy_set(value, &up->skyname,
-                                       &up->skynameset);
-      else if (strcmp(name, "skyhdu")==0)
-        gal_checkset_allocate_copy_set(value, &up->skyhdu,
-                                       &up->skyhduset);
-      else if (strcmp(name, "stdfilename")==0)
-        gal_checkset_allocate_copy_set(value, &up->stdname,
-                                       &up->stdnameset);
-      else if (strcmp(name, "stdhdu")==0)
-        gal_checkset_allocate_copy_set(value, &up->stdhdu,
-                                       &up->stdhduset);
-      else if (strcmp(name, "zeropoint")==0)
-        {
-          if(up->zeropointset) continue;
-          gal_checkset_any_float(value, &p->zeropoint, name, key, SPACK,
-                                 filename, lineno);
-          up->zeropointset=1;
-        }
-      else if (strcmp(name, "skysubtracted")==0)
-        {
-          if(up->skysubtractedset) continue;
-          gal_checkset_int_zero_or_one(value, &p->skysubtracted, name,
-                                       key, SPACK, filename, lineno);
-          up->skysubtractedset=1;
-        }
-      else if(strcmp(name, "threshold")==0)
-        {
-          if(up->thresholdset) continue;
-          gal_checkset_any_double(value, &p->threshold, name, key,
-                                  SPACK, filename, lineno);
-          up->thresholdset=1;
-        }
 
 
 
-      /* Outputs */
-      else if(strcmp(name, "output")==0)
-        gal_checkset_allocate_copy_set(value, &cp->output,
-                                       &cp->outputset);
-      else if(strcmp(name, "nsigmag")==0)
-        {
-          if(up->nsigmagset) continue;
-          gal_checkset_any_double(value, &p->nsigmag, name, key, SPACK,
-                                  filename, lineno);
-          up->nsigmagset=1;
-        }
-      else if(strcmp(name, "intwidth")==0)
-        {
-          if(up->intwidthset) continue;
-          gal_checkset_int_l_zero(value, &p->intwidth, name, key, SPACK,
-                                  filename, lineno);
-          up->intwidthset=1;
-        }
-      else if(strcmp(name, "floatwidth")==0)
-        {
-          if(up->floatwidthset) continue;
-          gal_checkset_int_l_zero(value, &p->floatwidth, name, key,
-                                  SPACK, filename, lineno);
-          up->floatwidthset=1;
-        }
-      else if(strcmp(name, "accuwidth")==0)
-        {
-          if(up->accuwidthset) continue;
-          gal_checkset_int_l_zero(value, &p->accuwidth, name, key,
-                                  SPACK, filename, lineno);
-          up->accuwidthset=1;
-        }
-      else if(strcmp(name, "floatprecision")==0)
-        {
-          if(up->floatprecisionset) continue;
-          gal_checkset_int_l_zero(value, &p->floatprecision, name,
-                                  key, SPACK, filename, lineno);
-          up->floatprecisionset=1;
-        }
-      else if(strcmp(name, "accuprecision")==0)
-        {
-          if(up->accuprecisionset) continue;
-          gal_checkset_int_l_zero(value, &p->accuprecision, name,
-                                  key, SPACK, filename, lineno);
-          up->accuprecisionset=1;
-        }
 
 
-      /* Upper limit magnitude */
-      else if (strcmp(name, "upmaskname")==0)
-        gal_checkset_allocate_copy_set(value, &up->upmaskname,
-                                       &up->upmasknameset);
-      else if (strcmp(name, "upmaskhdu")==0)
-        gal_checkset_allocate_copy_set(value, &up->upmaskhdu,
-                                       &up->upmaskhduset);
-      else if(strcmp(name, "upnum")==0)
-        {
-          if(up->upnumset) continue;
-          gal_checkset_sizet_l_zero(value, &p->upnum, name,
-                                  key, SPACK, filename, lineno);
-          up->upnumset=1;
-        }
-      else if(strcmp(name, "envseed")==0)
-        {
-          if(up->envseedset) continue;
-          gal_checkset_int_zero_or_one(value, &p->envseed, name, key,
-                                       SPACK, filename, lineno);
-          up->envseedset=1;
-        }
-      else if(strcmp(name, "upsclipmultip")==0)
-        {
-          if(up->upsclipmultipset) continue;
-          gal_checkset_float_l_0(value, &p->upsclipmultip, name,
-                                 key, SPACK, filename, lineno);
-          up->upsclipmultipset=1;
-        }
-      else if(strcmp(name, "upsclipaccu")==0)
-        {
-          if(up->upsclipaccuset) continue;
-          gal_checkset_float_l_0_s_1(value, &p->upsclipaccu, name,
-                                    key, SPACK, filename, lineno);
-          up->upsclipaccuset=1;
-        }
-      else if(strcmp(name, "upnsigma")==0)
-        {
-          if(up->upnsigmaset) continue;
-          gal_checkset_float_l_0(value, &p->upnsigma, name,
-                                 key, SPACK, filename, lineno);
-          up->upnsigmaset=1;
-        }
 
 
 
-      /* Catalog columns */
-      else if(strcmp(name, "id")==0)
-        {
-          if(up->idset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATID);
-          up->idset=1;
-        }
-      else if(strcmp(name, "hostobjid")==0)
-        {
-          if(up->hostobjidset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATHOSTOBJID);
-          up->hostobjidset=1;
-        }
-      else if(strcmp(name, "idinhostobj")==0)
-        {
-          if(up->idinhostobjset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATIDINHOSTOBJ);
-          up->idinhostobjset=1;
-        }
-      else if(strcmp(name, "numclumps")==0)
-        {
-          if(up->numclumpsset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATNUMCLUMPS);
-          up->numclumpsset=1;
-        }
-      else if(strcmp(name, "area")==0)
-        {
-          if(up->areaset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATAREA);
-          up->areaset=1;
-        }
-      else if(strcmp(name, "clumpsarea")==0)
-        {
-          if(up->clumpsareaset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSAREA);
-          up->clumpsareaset=1;
-        }
-      else if(strcmp(name, "x")==0)
-        {
-          if(up->xset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATX);
-          up->xset=1;
-        }
-      else if(strcmp(name, "y")==0)
-        {
-          if(up->yset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATY);
-          up->yset=1;
-        }
-      else if(strcmp(name, "geox")==0)
-        {
-          if(up->geoxset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOX);
-          up->geoxset=1;
-        }
-      else if(strcmp(name, "geoy")==0)
-        {
-          if(up->geoyset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOY);
-          up->geoyset=1;
-        }
-      else if(strcmp(name, "clumpsx")==0)
-        {
-          if(up->clumpsxset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSX);
-          up->clumpsxset=1;
-        }
-      else if(strcmp(name, "clumpsy")==0)
-        {
-          if(up->clumpsyset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSY);
-          up->clumpsyset=1;
-        }
-      else if(strcmp(name, "clumpsgeox")==0)
-        {
-          if(up->clumpsgeoxset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSGEOX);
-          up->clumpsgeoxset=1;
-        }
-      else if(strcmp(name, "clumpsgeoy")==0)
-        {
-          if(up->clumpsgeoyset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSGEOY);
-          up->clumpsgeoyset=1;
-        }
-      else if(strcmp(name, "ra")==0)
-        {
-          if(up->raset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATRA);
-          up->raset=1;
-        }
-      else if(strcmp(name, "dec")==0)
-        {
-          if(up->decset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATDEC);
-          up->decset=1;
-        }
-      else if(strcmp(name, "geora")==0)
-        {
-          if(up->georaset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATGEORA);
-          up->georaset=1;
-        }
-      else if(strcmp(name, "geodec")==0)
-        {
-          if(up->geodecset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATGEODEC);
-          up->geodecset=1;
-        }
-      else if(strcmp(name, "clumpsra")==0)
-        {
-          if(up->clumpsraset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSRA);
-          up->clumpsraset=1;
-        }
-      else if(strcmp(name, "clumpsdec")==0)
-        {
-          if(up->clumpsdecset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSDEC);
-          up->clumpsdecset=1;
-        }
-      else if(strcmp(name, "clumpsgeora")==0)
-        {
-          if(up->clumpsgeoraset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSGEORA);
-          up->clumpsgeoraset=1;
-        }
-      else if(strcmp(name, "clumpsgeodec")==0)
-        {
-          if(up->clumpsgeodecset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSGEODEC);
-          up->clumpsgeodecset=1;
-        }
-      else if(strcmp(name, "brightness")==0)
-        {
-          if(up->brightnessset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATBRIGHTNESS);
-          up->brightnessset=1;
-        }
-      else if(strcmp(name, "clumpsbrightness")==0)
-        {
-          if(up->clumpsbrightnessset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSBRIGHTNESS);
-          p->up.clumpsbrightnessset=1;
-        }
-      else if(strcmp(name, "noriverbrightness")==0)
-        {
-          if(up->noriverbrightnessset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATNORIVERBRIGHTNESS);
-          p->up.noriverbrightnessset=1;
-        }
-      else if(strcmp(name, "magnitude")==0)
-        {
-          if(up->magnitudeset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATMAGNITUDE);
-          up->magnitudeset=1;
-        }
-      else if(strcmp(name, "magnitudeerr")==0)
-        {
-          if(up->magnitudeerrset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATMAGNITUDEERR);
-          up->magnitudeerrset=1;
-        }
-      else if(strcmp(name, "clumpsmagnitude")==0)
-        {
-          if(up->clumpsmagnitudeset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATCLUMPSMAGNITUDE);
-          up->clumpsmagnitudeset=1;
-        }
-      else if(strcmp(name, "upperlimitmag")==0)
-        {
-          if(up->upperlimitmagset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATUPPERLIMITMAG);
-          up->upperlimitmagset=1;
-        }
-      else if(strcmp(name, "riverave")==0)
-        {
-          if(up->riveraveset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATRIVERAVE);
-          up->riveraveset=1;
-        }
-      else if(strcmp(name, "rivernum")==0)
-        {
-          if(up->rivernumset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATRIVERNUM);
-          up->rivernumset=1;
-        }
-      else if(strcmp(name, "sn")==0)
-        {
-          if(up->snset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATSN);
-          up->snset=1;
-        }
-      else if(strcmp(name, "sky")==0)
-        {
-          if(up->skyset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATSKY);
-          up->skyset=1;
-        }
-      else if(strcmp(name, "std")==0)
-        {
-          if(up->stdset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATSTD);
-          up->stdset=1;
-        }
-      else if(strcmp(name, "semimajor")==0)
-        {
-          if(up->semimajorset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATSEMIMAJOR);
-          up->semimajorset=1;
-        }
-      else if(strcmp(name, "semiminor")==0)
-        {
-          if(up->semiminorset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATSEMIMINOR);
-          up->semiminorset=1;
-        }
-      else if(strcmp(name, "positionangle")==0)
-        {
-          if(up->positionangleset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATPOSITIONANGLE);
-          up->positionangleset=1;
-        }
-      else if(strcmp(name, "geosemimajor")==0)
-        {
-          if(up->geosemimajorset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOSEMIMAJOR);
-          up->geosemimajorset=1;
-        }
-      else if(strcmp(name, "geosemiminor")==0)
+
+/**************************************************************/
+/*********    Initialize & Parse command-line    **************/
+/**************************************************************/
+static void
+ui_initialize_options(struct mkcatalogparams *p,
+                      struct argp_option *program_options,
+                      struct argp_option *gal_commonopts_options)
+{
+  size_t i;
+  struct gal_options_common_params *cp=&p->cp;
+
+
+  /* Set the necessary common parameters structure. */
+  cp->program_struct     = p;
+  cp->program_name       = PROGRAM_NAME;
+  cp->program_exec       = PROGRAM_EXEC;
+  cp->program_bibtex     = PROGRAM_BIBTEX;
+  cp->program_authors    = PROGRAM_AUTHORS;
+  cp->poptions           = program_options;
+  cp->numthreads         = gal_threads_number();
+  cp->coptions           = gal_commonopts_options;
+
+  /* Specific to this program. */
+  p->nsigmag             = NAN;
+  p->upnsigma            = NAN;
+  p->zeropoint           = NAN;
+  p->threshold           = NAN;
+  p->upsigmaclip[0]      = NAN;
+  p->upsigmaclip[1]      = NAN;
+
+
+  /* Modify common options. */
+  for(i=0; !gal_options_is_last(&cp->coptions[i]); ++i)
+    {
+      /* Select individually. */
+      switch(cp->coptions[i].key)
         {
-          if(up->geosemiminorset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOSEMIMINOR);
-          up->geosemiminorset=1;
+        case GAL_OPTIONS_KEY_TYPE:
+        case GAL_OPTIONS_KEY_SEARCHIN:
+        case GAL_OPTIONS_KEY_IGNORECASE:
+        case GAL_OPTIONS_KEY_MINMAPSIZE:
+          cp->coptions[i].flags=OPTION_HIDDEN;
+          cp->coptions[i].mandatory=GAL_OPTIONS_NOT_MANDATORY;
+          break;
+
+        case GAL_OPTIONS_KEY_TABLEFORMAT:
+          cp->coptions[i].mandatory=GAL_OPTIONS_MANDATORY;
+          break;
         }
-      else if(strcmp(name, "geopositionangle")==0)
+
+      /* Select by group. */
+      switch(cp->coptions[i].group)
         {
-          if(up->geopositionangleset) continue;
-          gal_checkset_int_zero_or_one(value, &yes, name, key, SPACK,
-                                       filename, lineno);
-          if(!yes) continue;
-          gal_linkedlist_add_to_sll(&p->allcolsll, CATGEOPOSITIONANGLE);
-          up->geopositionangleset=1;
+        case GAL_OPTIONS_GROUP_TESSELLATION:
+          cp->coptions[i].doc=NULL; /* Necessary to remove title. */
+          cp->coptions[i].flags=OPTION_HIDDEN;
+          break;
         }
+    }
+}
+
 
 
 
-      /* Operating modes */
-      /* Read options common to all programs */
-      GAL_CONFIGFILES_READ_COMMONOPTIONS_FROM_CONF
 
+/* Parse a single option: */
+error_t
+parse_opt(int key, char *arg, struct argp_state *state)
+{
+  struct mkcatalogparams *p = state->input;
+
+  /* Pass `gal_options_common_params' into the child parser.  */
+  state->child_inputs[0] = &p->cp;
+
+  /* In case the user incorrectly uses the equal sign (for example
+     with a short format or with space in the long format, then `arg`
+     start with (if the short version was called) or be (if the long
+     version was called with a space) the equal sign. So, here we
+     check if the first character of arg is the equal sign, then the
+     user is warned and the program is stopped: */
+  if(arg && arg[0]=='=')
+    argp_error(state, "incorrect use of the equal sign (`=`). For short "
+               "options, `=` should not be used and for long options, "
+               "there should be no space between the option, equal sign "
+               "and value");
+
+  /* Set the key to this option. */
+  switch(key)
+    {
 
+    /* Read the non-option tokens (arguments): */
+    case ARGP_KEY_ARG:
+      if(p->inputname)
+        argp_error(state, "only one argument (input file) should be given");
       else
-        error_at_line(EXIT_FAILURE, 0, filename, lineno,
-                      "`%s` not recognized.\n", name);
+        p->inputname=arg;
+      break;
+
+
+    /* This is an option, set its value. */
+    default:
+      return gal_options_set_from_key(key, arg, p->cp.poptions, &p->cp);
     }
 
-  free(line);
-  fclose(fp);
+  return 0;
 }
 
 
 
 
 
-void
-printvalues(FILE *fp, struct mkcatalogparams *p)
+/* Read the user's desired columns. Because the types of these options are
+   `GAL_TYPE_INVALID', this function will not be called when printing the
+   full list of parameters and their values. */
+void *
+ui_column_codes_ll(struct argp_option *option, char *arg,
+                   char *filename, size_t lineno, void *params)
 {
-  int i;
-  struct uiparams *up=&p->up;
-  struct gal_commonparams *cp=&p->cp;
-
-
-  /* Print all the options that are set. Separate each group with a
-     commented line explaining the options in that group. */
-  fprintf(fp, "\n# Input image:\n");
-  if(cp->hduset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("hdu", cp->hdu);
-  if(up->masknameset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("mask", up->maskname);
-  if(up->mhduset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("mhdu", up->mhdu);
-  if(up->objlabsnameset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("objlabs",
-                                               up->objlabsname);
-  if(up->objhduset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("objhdu", up->objhdu);
-  if(up->clumplabsnameset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("clumplabs",
-                                               up->clumplabsname);
-  if(up->clumphduset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("clumphdu",
-                                               up->clumphdu);
-  if(up->skynameset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("skyfilename",
-                                               up->skyname);
-  if(up->skyhduset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("skyhdu", up->skyhdu);
-  if(up->stdnameset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("stdfilename",
-                                               up->stdname);
-  if(up->stdhduset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("stdhdu", up->stdhdu);
-  if(up->zeropointset)
-    fprintf(fp, CONF_SHOWFMT"%.3f\n", "zeropoint", p->zeropoint);
-  if(up->skysubtractedset)
-    fprintf(fp, CONF_SHOWFMT"%d\n", "skysubtracted", p->skysubtracted);
-  if(up->thresholdset)
-    fprintf(fp, CONF_SHOWFMT"%.3f\n", "threshold", p->threshold);
-
-  /* Output: */
-  fprintf(fp, "\n# Output:\n");
-  if(cp->outputset)
-    GAL_CHECKSET_PRINT_STRING_MAYBE_WITH_SPACE("output", cp->output);
-  if(up->nsigmagset)
-    fprintf(fp, CONF_SHOWFMT"%g\n", "nsigmag", p->nsigmag);
-  if(up->intwidthset)
-    fprintf(fp, CONF_SHOWFMT"%d\n", "intwidth", p->intwidth);
-  if(up->floatwidthset)
-    fprintf(fp, CONF_SHOWFMT"%d\n", "floatwidth", p->floatwidth);
-  if(up->accuwidthset)
-    fprintf(fp, CONF_SHOWFMT"%d\n", "accuwidth", p->accuwidth);
-  if(up->floatprecisionset)
-    fprintf(fp, CONF_SHOWFMT"%d\n", "floatprecision", p->floatprecision);
-  if(up->accuprecisionset)
-    fprintf(fp, CONF_SHOWFMT"%d\n", "accuprecision", p->accuprecision);
-
-  /* Upper limit magnitude */
-  fprintf(fp, "\n# Upper limit magnitude:\n");
-  if(up->upmasknameset)
-    fprintf(fp, CONF_SHOWFMT"%s\n", "upmaskname", up->upmaskname);
-  if(up->upmaskhduset)
-    fprintf(fp, CONF_SHOWFMT"%s\n", "upmaskhdu", up->upmaskhdu);
-  if(up->upnumset)
-    fprintf(fp, CONF_SHOWFMT"%zu\n", "upnum", p->upnum);
-  if(up->envseedset)
-    fprintf(fp, CONF_SHOWFMT"%d\n", "envseed", p->envseed);
-  if(up->upsclipmultipset)
-    fprintf(fp, CONF_SHOWFMT"%.3f\n", "upsclipmultip", p->upsclipmultip);
-  if(up->upsclipaccuset)
-    fprintf(fp, CONF_SHOWFMT"%.3f\n", "upsclipaccu", p->upsclipaccu);
-  if(up->upnsigmaset)
-    fprintf(fp, CONF_SHOWFMT"%.3f\n", "upnsigma", p->upnsigma);
-
-
-  /* Catalog columns, since order is important. Notice that they have
-     to be printed in opposite order (because of the way they are read
-     through a simple linked list). */
-  fprintf(fp, "\n# Catalog columns:\n");
-  for(i=p->allncols-1;i>=0;--i)
-    switch(p->allcols[i])
-      {
-      case CATID:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "id", 1);
-        break;
-      case CATHOSTOBJID:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "hostobjid", 1);
-        break;
-      case CATIDINHOSTOBJ:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "idinhostobj", 1);
-        break;
-      case CATNUMCLUMPS:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "numclumps", 1);
-        break;
-      case CATAREA:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "area", 1);
-        break;
-      case CATCLUMPSAREA:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsarea", 1);
-        break;
-      case CATX:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "x", 1);
-        break;
-      case CATY:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "y", 1);
-        break;
-      case CATGEOX:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "x", 1);
-        break;
-      case CATGEOY:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "y", 1);
-        break;
-      case CATCLUMPSX:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsx", 1);
-        break;
-      case CATCLUMPSY:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsy", 1);
-        break;
-      case CATCLUMPSGEOX:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsgeox", 1);
-        break;
-      case CATCLUMPSGEOY:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsgeoy", 1);
-        break;
-      case CATRA:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "ra", 1);
-        break;
-      case CATDEC:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "dec", 1);
-        break;
-      case CATGEORA:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "geora", 1);
-        break;
-      case CATGEODEC:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "geodec", 1);
-        break;
-      case CATCLUMPSRA:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsra", 1);
-        break;
-      case CATCLUMPSDEC:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsdec", 1);
-        break;
-      case CATCLUMPSGEORA:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsgeora", 1);
-        break;
-      case CATCLUMPSGEODEC:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsgeodec", 1);
-        break;
-      case CATBRIGHTNESS:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "brightness", 1);
-        break;
-      case CATCLUMPSBRIGHTNESS:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsbrightness", 1);
-        break;
-      case CATUPPERLIMITMAG:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "upperlimitmag", 1);
-        break;
-      case CATNORIVERBRIGHTNESS:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "noriverbrightness", 1);
-        break;
-      case CATMAGNITUDE:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "magnitude", 1);
-        break;
-      case CATMAGNITUDEERR:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "magnitudeerr", 1);
-        break;
-      case CATCLUMPSMAGNITUDE:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "clumpsmagnitude", 1);
-        break;
-      case CATRIVERAVE:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "riverave", 1);
-        break;
-      case CATRIVERNUM:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "rivernum", 1);
-        break;
-      case CATSN:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "sn", 1);
-        break;
-      case CATSKY:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "sky", 1);
-        break;
-      case CATSTD:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "std", 1);
-        break;
-      case CATSEMIMAJOR:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "semimajor", 1);
-        break;
-      case CATSEMIMINOR:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "semiminor", 1);
-        break;
-      case CATPOSITIONANGLE:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "positionangle", 1);
-        break;
-      case CATGEOSEMIMAJOR:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "geosemimajor", 1);
-        break;
-      case CATGEOSEMIMINOR:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "geosemiminor", 1);
-        break;
-      case CATGEOPOSITIONANGLE:
-        fprintf(fp, CONF_SHOWFMT"%d\n", "geopositionangle", 1);
-        break;
-      default:
-        error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we can "
-              "fix the problem. For some reason p->allcols[%d] is given "
-              "the value %zu which is not recognized in printvalues "
-              "(ui.c)", PACKAGE_BUGREPORT, i, p->allcols[i]);
-      }
-
-  /* For the operating mode, first put the macro to print the common
-     options, then the (possible options particular to this
-     program). */
-  fprintf(fp, "\n# Operating mode:\n");
-  GAL_CONFIGFILES_PRINT_COMMONOPTIONS;
-}
-
-
-
+  struct mkcatalogparams *p=(struct mkcatalogparams *)params;
 
+  /* These options don't take arguments on the command-line but in the
+     configuration files they can take values of 0 or 1. In the latter
+     case, the column shouldn't be added if the value is 0. */
+  if(arg)
+    {
+      if( arg[0]=='0' && arg[1]=='\0' ) return NULL;
+      else if ( !(arg[0]=='1' && arg[1]=='\0')  )
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' is not a "
+                      "valid value to the `%s' option: (\"%s\").\n\n`%s' is "
+                      "an on/off option specifying if you want this column "
+                      "in the output catalog, it doesn't take any "
+                      "arguments. In a configuration file, it can only take "
+                      "a value of `0' (to be ignored) or `1'", arg,
+                      option->name, option->doc, option->name);
+    }
 
 
-/* Note that numthreads will be used automatically based on the
-   configure time. */
-void
-checkifset(struct mkcatalogparams *p)
-{
-  struct uiparams *up=&p->up;
-  struct gal_commonparams *cp=&p->cp;
-
-  int intro=0;
-  if(cp->hduset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("hdu");
-  if(up->objhduset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("objhdu");
-  if(up->clumphduset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("clumphdu");
-  if(up->skyhduset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("skyhdu");
-  if(up->stdhduset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("stdhdu");
-  if(up->zeropointset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("zeropoint");
-  if(up->skysubtractedset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("skysubtracted");
-
-  /* Output: */
-  if(up->nsigmagset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("nsigmag");
-  if(up->intwidthset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("intwidth");
-  if(up->floatwidthset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("floatwidth");
-  if(up->accuwidthset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("accuwidth");
-  if(up->floatprecisionset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("floatprecision");
-  if(up->accuprecisionset==0)
-    GAL_CONFIGFILES_REPORT_NOTSET("accuprecision");
-
-  /* Upper limit magnitude. */
-  if(p->up.upperlimitmagset)
+  /* The user wants this column, so add it to the list. Note that the `ids'
+     column means three columns. */
+  if(option->key==UI_KEY_IDS)
     {
-      if(p->up.upnumset==0)
-        GAL_CONFIGFILES_REPORT_NOTSET("upnum");
-      if(p->up.upsclipmultipset==0)
-        GAL_CONFIGFILES_REPORT_NOTSET("upsclipmultip");
-      if(p->up.upsclipaccuset==0)
-        GAL_CONFIGFILES_REPORT_NOTSET("upsclipaccu");
-      if(p->up.upnsigmaset==0)
-        GAL_CONFIGFILES_REPORT_NOTSET("upnsigma");
+      gal_linkedlist_add_to_ill(&p->columnids, UI_KEY_OBJID);
+      gal_linkedlist_add_to_ill(&p->columnids, UI_KEY_HOSTOBJID);
+      gal_linkedlist_add_to_ill(&p->columnids, UI_KEY_IDINHOSTOBJ);
     }
+  else
+    gal_linkedlist_add_to_ill(&p->columnids, option->key);
 
-  GAL_CONFIGFILES_END_OF_NOTSET_REPORT;
+  /* Return NULL */
+  return NULL;
 }
 
 
@@ -935,77 +273,33 @@ checkifset(struct mkcatalogparams *p)
 /**************************************************************/
 /***************       Sanity Check         *******************/
 /**************************************************************/
-void
-sanitycheck(struct mkcatalogparams *p)
+/* Read and check ONLY the options. When arguments are involved, do the
+   check in `ui_check_options_and_arguments'.
+static void
+ui_read_check_only_options(struct mkcatalogparams *p)
 {
-  struct uiparams *up=&p->up;
-  struct gal_fits_key keys[1];
-  struct gal_commonparams *cp=&p->cp;
-
-  /* Make sure the input file exists. */
-  gal_checkset_check_file(up->inputname);
-
-  /* Set the names of the files. */
-  gal_fits_file_or_ext_name(up->inputname, p->cp.hdu, up->masknameset,
-                            &up->maskname, up->mhdu, up->mhduset,
-                            "mask");
-  gal_fits_file_or_ext_name(up->inputname, p->cp.hdu,
-                            up->objlabsnameset, &up->objlabsname,
-                            up->objhdu, up->objhduset,
-                            "object labels");
-  gal_fits_file_or_ext_name(up->inputname, p->cp.hdu, up->skynameset,
-                            &up->skyname, up->skyhdu, up->skyhduset,
-                            "sky value image");
-  gal_fits_file_or_ext_name(up->inputname, p->cp.hdu, up->stdnameset,
-                            &up->stdname, up->stdhdu, up->stdhduset,
-                            "sky standard deviation");
-
-  /* The WCLUMPS (with-clumps) keyword in the object HDU says that there is
-     also a clumps image accompaning the object image. If it exists and its
-     value is `yes' (not case sensitive), then set the image name to the
-     proper string. Otherwise, set the image name to NULL, so future
-     functions can check. */
-  keys[0].str[0]='\0';
-  keys[0].datatype=TSTRING;
-  keys[0].keyname="WCLUMPS";
-  gal_fits_read_keywords(up->objlabsname, up->objhdu, keys, 1);
-  if(strcasecmp(keys[0].str, "yes"))
-    up->clumplabsname=NULL;
-  else
-    gal_fits_file_or_ext_name(up->inputname, p->cp.hdu,
-                              up->clumplabsnameset, &up->clumplabsname,
-                              up->clumphdu, up->clumphduset,
-                              "clump labels");
-
-  /* When the RA and Dec are needed, make sure that the X and Y
-     columns and the RA and Dec columns in the information array are
-     immediately after each other and in this order. This is not set
-     by the user, but by the programmer. Nevertheless, it is a very
-     important issue to check. Since the enum values are constants,
-     the compiler will know immediately and stop.
-
-     NOTE: the information array is separate from the output array
-  */
-  if(up->raset || up->decset)
+}
+*/
+
+
+
+
+static void
+ui_check_options_and_arguments(struct mkcatalogparams *p)
+{
+  /* Make sure an input file name was given and if it was a FITS file, that
+     a HDU is also given. */
+  if(p->inputname)
     {
-      if( OFlxWhtX!=OFlxWhtY-1 || OFlxWhtRA!=OFlxWhtDec-1 )
-        error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we can "
-              "fix the problem. X(%d) and Y(%d), or Ra(%d) and Dec(%d) "
-              "columns in the information array are not immediately "
-              "after each other", PACKAGE_BUGREPORT, OFlxWhtX, OFlxWhtY,
-              OFlxWhtRA, OFlxWhtDec);
-    }
+      if( gal_fits_name_is_fits(p->inputname) && p->cp.hdu==NULL )
+        error(EXIT_FAILURE, 0, "no HDU specified. When the input is a FITS "
+              "file, a HDU must also be specified, you can use the `--hdu' "
+              "(`-h') option and give it the HDU number (starting from "
+              "zero), extension name, or anything acceptable by CFITSIO");
 
-  /* Set the output names, based on if the output is set or not. */
-  gal_checkset_automatic_output(cp->outputset ? cp->output : up->inputname,
-                                up->clumplabsname ? "_o.txt" : ".txt",
-                                cp->outputset ? 0 : p->cp.removedirinfo,
-                                p->cp.dontdelete, &p->ocatname);
-  if(up->clumplabsname)
-    gal_checkset_automatic_output(cp->outputset ? cp->output : up->inputname,
-                                  "_c.txt",
-                                  cp->outputset ? 0 : p->cp.removedirinfo,
-                                  p->cp.dontdelete, &p->ccatname);
+    }
+  else
+    error(EXIT_FAILURE, 0, "no input file is specified");
 }
 
 
@@ -1026,421 +320,493 @@ sanitycheck(struct mkcatalogparams *p)
 
 
 
+
 /**************************************************************/
 /***************       Preparations         *******************/
 /**************************************************************/
-void
-checksetlong(struct mkcatalogparams *p, char *filename, char *hdu,
-             long **array)
+static void
+ui_preparations_read_inputs(struct mkcatalogparams *p)
 {
-  size_t s0, s1;
-  int bitpix, anyblank;
-
-  /* Read the file: */
-  gal_fits_file_to_long(filename, hdu, array, &bitpix,
-                        &anyblank, &s0, &s1);
-
-  /* Make sure it has an integer type. */
-  if(bitpix==FLOAT_IMG || bitpix==DOUBLE_IMG)
-    error(EXIT_FAILURE, 0, "the labels image can be any integer type "
-          "(BITPIX). However, %s (hdu: %s) is a %s precision floating "
-          "point image", filename, hdu,
-          bitpix==FLOAT_IMG ? "single" : "double");
-
-  /* Make sure it is the same size as the input image. */
-  if(s0!=p->s0 || s1!=p->s1)
-    error(EXIT_FAILURE, 0, "%s (hdu: %s) is %zu x %zu pixels while the "
-          "%s (hdu: %s) is %zu x %zu. The images should have the same "
-          "size", filename, hdu, s1, s0, p->up.inputname,
-          p->cp.hdu, p->s1, p->s0);
-}
+  size_t one;
+  int readclumps=0, readwcs=0;
+  struct gal_linkedlist_ill *tmp;
+  char *namestypes, **strarr=NULL;
+  gal_data_t *zero, *key=gal_data_array_calloc(1);
+  char *skyfile=p->skyfile ? p->skyfile : p->inputname;
+  char *stdfile=p->stdfile ? p->stdfile : p->inputname;
+  char *clumpsfile=p->clumpsfile ? p->clumpsfile : p->inputname;
+  char *objectsfile=p->objectsfile ? p->objectsfile : p->inputname;
 
 
+  /* Read the input image. */
+  p->input=gal_fits_img_read_to_type(p->inputname, p->cp.hdu,
+                                     GAL_TYPE_FLOAT32, p->cp.minmapsize);
 
 
+  /* Currently MakeCatalog is only implemented for 2D images. */
+  if(p->input->ndim!=2)
+    error(EXIT_FAILURE, 0, "%s (%s) has %zu dimensions, MakeCatalog "
+          "currently only supports 2D inputs", p->inputname, p->cp.hdu,
+          p->input->ndim);
 
-void
-checksetfloat(struct mkcatalogparams *p, char *filename, char *hdu,
-              float **array)
-{
-  size_t s0, s1;
-  int bitpix, anyblank;
-
-  /* Read the array: */
-  gal_fits_file_to_float(filename, NULL, hdu, NULL, array, &bitpix,
-                              &anyblank, &s0, &s1);
-
-  /* Make sure it has no blank pixels.
-  if(anyblank)
-    error(EXIT_FAILURE, 0, "the Sky and Sky standard deviation images "
-          "should not have any blank values. %s (hdu: %s) has blank "
-          "pixels", filename, hdu);
-  */
 
-  /* Make sure it has the same size as the image. */
-  if(s0!=p->s0 || s1!=p->s1)
-    error(EXIT_FAILURE, 0, "%s (hdu: %s) is %zu x %zu pixels while the "
-          "%s (hdu: %s) is %zu x %zu. The images should have the same "
-          "size", filename, hdu, s1, s0, p->up.inputname,
-          p->cp.hdu, p->s1, p->s0);
-}
+  /* See if the input has blank pixels and set the flags appropriately. */
+  p->hasblank = gal_blank_present(p->input, 1);
 
 
+  /* Read the object label image and check its size. */
+  p->objects = gal_fits_img_read(objectsfile, p->objectshdu,
+                                 p->cp.minmapsize);
+  if( gal_data_dsize_is_different(p->input, p->objects) )
+    error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+          "different dimension/size", objectsfile, p->objectshdu,
+          p->inputname, p->cp.hdu);
 
 
+  /* Read the Sky image and check its size. */
+  p->sky=gal_fits_img_read_to_type(skyfile, p->skyhdu, GAL_TYPE_FLOAT32,
+                                   p->cp.minmapsize);
+  if( gal_data_dsize_is_different(p->input, p->sky) )
+    error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+          "different dimension/size", skyfile, p->skyhdu, p->inputname,
+          p->cp.hdu);
 
-/* Read the necessary keywords and if they aren't present do the
-   appropriate action. */
-void
-readkeywords(struct mkcatalogparams *p)
-{
-  long numobjects;
-  size_t size=p->s0*p->s1;
-  struct uiparams *up=&p->up;
-  struct gal_fits_key keys[2];
 
-  /* Read keywords from the standard deviation image:  */
-  keys[0].keyname="MINSTD";   keys[0].datatype=TFLOAT;
-  keys[1].keyname="MEDSTD";   keys[1].datatype=TFLOAT;
-  gal_fits_read_keywords(up->stdname, up->stdhdu, keys, 2);
+  /* Read the Sky standard deviation image and check its size. */
+  p->std=gal_fits_img_read_to_type(stdfile, p->stdhdu, GAL_TYPE_FLOAT32,
+                                   p->cp.minmapsize);
+  if( gal_data_dsize_is_different(p->input, p->std) )
+    error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+          "different dimension/size", stdfile, p->stdhdu, p->inputname,
+          p->cp.hdu);
 
-  /* The minimum standard deviation value. */
-  if(keys[0].status)
-    gal_statistics_float_min(p->std, size, &p->minstd);
+
+  /* If an upper-limit mask image was given, read it. */
+  if(p->upmaskfile)
+    {
+      /* Read the mask image. */
+      p->upmask = gal_fits_img_read(p->upmaskfile, p->upmaskhdu,
+                                    p->cp.minmapsize);
+      if( gal_data_dsize_is_different(p->input, p->upmask) )
+        error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+              "different dimension/size", p->upmaskfile, p->upmaskhdu,
+          p->inputname, p->cp.hdu);
+
+      /* If it isn't an integer type, report an error, otherwise, convert
+         it to a uint8_t: with a 1 for all non-zero pixels and 0 for zero
+         pixels. */
+      zero=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &one, NULL, 1, -1,
+                          NULL, NULL, NULL);
+      p->upmask=gal_arithmetic(GAL_ARITHMETIC_OP_NE,
+                               ( GAL_ARITHMETIC_INPLACE | GAL_ARITHMETIC_FREE
+                                 | GAL_ARITHMETIC_NUMOK ), p->upmask, zero);
+    }
+
+
+  /* Check to see if the objects extension has a `WCLUMPS' keyword and that
+     its value is 'yes', `y', or `1'. */
+  key->name="WCLUMPS";
+  key->type=GAL_TYPE_STRING;
+  gal_fits_key_read(objectsfile, p->objectshdu, key, 0, 0);
+  if(key->status==KEY_NO_EXIST) readclumps=0;
   else
-    p->minstd=keys[0].f;
-  p->cpscorr = p->minstd>1 ? 1.0f : p->minstd;
+    {
+      if(key->status)
+        gal_fits_io_error(key->status, "CFITSIO error while reading "
+                          "WCLUMPS keyword");
+      else
+        {
+          strarr=key->array;
+          if( !strcasecmp(strarr[0], "yes") || !strcasecmp(strarr[0], "y")
+              || !strcmp(strarr[0], "1") ) readclumps=1;
+        }
+    }
+
 
-  /* Median standard deviation value (only necessary in catalog
-     comments). */
+  /* Read the clumps array if necessary. */
+  if(readclumps)
+    {
+      /* Make sure the user did indeed give the clumps HDU. */
+      if(p->clumpshdu==NULL)
+        error(EXIT_FAILURE, 0, "no `--clumpshdu' given! The WCLUMPS keyword "
+              "in %s (hdu: %s) has a value of `%s', so MakeCatalog expects "
+              "a clump image.\n\nYou can use the optional `--clumpsfile' "
+              "option to give the filename and the mandatory `--clumpshdu' "
+              "to specify the extension. If `--clumpsfile' is not given, "
+              "MakeCatalog will look into the input file for the given "
+              "extension. Alternatively, you can modify/remove this "
+              "keyword using Gnuastro's Fits program, please run `$ info "
+              "astfits' for more information (press `SPACE' to go down and "
+              "`q' to return to the command-line).", objectsfile,
+              p->objectshdu, strarr[0]);
+
+      /* Read the clumps image and check its size. */
+      p->clumps = gal_fits_img_read(clumpsfile, p->clumpshdu,
+                                    p->cp.minmapsize);
+      if( gal_data_dsize_is_different(p->input, p->std) )
+        error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+              "different dimension/size", clumpsfile, p->clumpshdu,
+              p->inputname, p->cp.hdu);
+    }
+
+
+  /* Make sure the object and clump label images have an integer type, then
+     to be safe that they have the correct integer type, run
+     `gal_data_copy_to_new_type' on them. */
+  if( p->objects->type==GAL_TYPE_FLOAT32
+      || p->objects->type==GAL_TYPE_FLOAT64
+      || (p->clumps && ( p->clumps->type==GAL_TYPE_FLOAT32
+                         || p->clumps->type==GAL_TYPE_FLOAT64 ) ) )
+    {
+      if(p->clumps)
+        asprintf(&namestypes, "However, `%s' (hdu: %s) and `%s' (hdu: %s) "
+                 "have types of `%s' and `%s' respectively", objectsfile,
+                 p->objectshdu, clumpsfile, p->clumpshdu,
+                 gal_type_to_string(p->objects->type, 1),
+                 gal_type_to_string(p->clumps->type, 1) );
+      else
+        asprintf(&namestypes, "However, %s (hdu: %s) has a type of %s",
+                 objectsfile, p->objectshdu,
+                 gal_type_to_string(p->objects->type, 1));
+      error(EXIT_FAILURE, 0, "labeled images (for objects or clumps) must "
+            "have an integer datatype. %s.\n\n"
+            "If you are sure the images contain only integer values but "
+            "are just stored in a floating point container, you can "
+            "put them in an integer container with Gnuastro's Arithmetic "
+            "program using a command like below:\n\n"
+            "    $ astarithmetic img.fits int32", namestypes);
+    }
+  p->objects=gal_data_copy_to_new_type_free(p->objects, GAL_TYPE_INT32);
+  if(p->clumps)
+    p->clumps=gal_data_copy_to_new_type_free(p->clumps, GAL_TYPE_INT32);
+
+
+  /* If any WCS related parameter is requested then read the input's WCS
+     structure. */
+  for(tmp=p->columnids; tmp!=NULL; tmp=tmp->next)
+    if(readwcs==0)
+      switch(tmp->v)
+        {
+        case UI_KEY_RA:
+        case UI_KEY_DEC:
+        case UI_KEY_GEORA:
+        case UI_KEY_GEODEC:
+        case UI_KEY_CLUMPSRA:
+        case UI_KEY_CLUMPSDEC:
+        case UI_KEY_CLUMPSGEORA:
+        case UI_KEY_CLUMPSGEODEC:
+          readwcs=1;
+          break;
+        }
+  if(readwcs)
+    gal_wcs_read(p->inputname, p->cp.hdu, 0, 0, &p->input->nwcs,
+                 &p->input->wcs);
+
+
+  /* Clean up. */
+  key->name=NULL;
+  gal_data_array_free(key, 1, 1);
+}
+
+
+
+
+
+/* The input images can have extensions to speed up the processing. */
+static void
+ui_preparations_read_keywords(struct mkcatalogparams *p)
+{
+  gal_data_t *tmp;
+  gal_data_t *keys=gal_data_array_calloc(2);
+  char *stdfile=p->stdfile ? p->stdfile : p->inputname;
+  char *clumpsfile=p->clumpsfile ? p->clumpsfile : p->inputname;
+  char *objectsfile=p->objectsfile ? p->objectsfile : p->inputname;
+
+  /* Read the keywords from the standard deviation image. */
+  keys[0].next=&keys[1];
+  keys[0].name="MINSTD";                    keys[1].name="MEDSTD";
+  keys[0].type=GAL_TYPE_FLOAT32;            keys[1].type=GAL_TYPE_FLOAT32;
+  keys[0].array=&p->minstd;                 keys[1].array=&p->medstd;
+  gal_fits_key_read(stdfile, p->stdhdu, keys, 0, 0);
+
+  /* If the two keywords couldn't be read, calculate them. */
+  if(keys[0].status)
+    {
+      /* Calculate the minimum STD. */
+      tmp=gal_statistics_minimum(p->std);
+      p->minstd=*((float *)(tmp->array));
+      gal_data_free(tmp);
+    }
   if(keys[1].status)
     {
-      p->medstd=gal_statistics_median(p->std, size);
-      fprintf(stderr, "Warning: Could not find the MEDSTD keyword in %s "
+      /* Calculate the median STD. */
+      tmp=gal_statistics_median(p->std, 0);
+      p->medstd=*((float *)(tmp->array));
+      gal_data_free(tmp);
+
+      /* Alert the user if it wasn't calculated from a header keyword. */
+      fprintf(stderr, "---------------\n"
+              "Warning: Could not find the `MEDSTD' keyword in `%s' "
               "(hdu: %s). The median standard deviation is thus found on "
               "the (interpolated) standard deviation image. NoiseChisel "
-              "finds the median before interpolation, so the reported value "
-              "in the final catalog will not be accurate (will depend on "
-              "how many meshs were blank and their spatial position "
-              "relative to the non-blank ones.", up->stdname, up->stdhdu);
+              "finds the median before interpolation which is more "
+              "accurate. Ho the reported value in the final catalog will "
+              "not be accurate: it will depend on how many tiles were "
+              "blank and their spatial position relative to the non-blank "
+              "ones.\n"
+              "---------------\n", stdfile, p->stdhdu);
     }
-  else
-    p->medstd=keys[1].f;
-
-  /* Read the keywords from the objects image:  */
-  keys[0].keyname="DETSN";     keys[0].datatype=TDOUBLE;
-  keys[1].keyname="NOBJS";     keys[1].datatype=TLONG;
-  gal_fits_read_keywords(up->objlabsname, up->objhdu, keys, 2);
+  p->cpscorr = p->minstd>1 ? 1.0f : p->minstd;
 
-  /* If DETSN is not given, there is no way we can calculate it here, so we
-     will just set it to NaN to check and not report in the end. */
-  p->detsn = keys[0].status ? NAN : keys[0].d;
 
-  /* Read the total number of objects. */
+  /* Read the keywords from the objects image. */
+  keys[0].name="DETSN";                     keys[1].name="NUMLABS";
+  keys[0].type=GAL_TYPE_FLOAT32;            keys[1].type=GAL_TYPE_SIZE_T;
+  keys[0].array=&p->detsn;                  keys[1].array=&p->numobjects;
+  gal_fits_key_read(objectsfile, p->objectshdu, keys, 0, 0);
+  if(keys[0].status) p->detsn=NAN;
   if(keys[1].status)
     {
-      gal_statistics_long_non_blank_max(p->objects, size, &numobjects,
-                                        GAL_DATA_BLANK_LONG);
-      p->numobjects=numobjects;
+      tmp=gal_statistics_maximum(p->objects);
+      p->numobjects=*((int32_t *)(tmp->array)); /* objects is in int32_t. */
+      gal_data_free(tmp);
     }
-  else
-    p->numobjects=keys[1].l;
-
-  /* Read the clumps information if it is necessary.
-
-     Note that unlike finding the number of objects, finding the number of
-     clumps is not an easy process, since the clumps of each object start
-     with a label of one. So if the number of clumps is not given, we have
-     to abort. For now, is up to the program that built the clumps to give
-     the total number, later we can take a procedure to find them (for
-     example first only taking the positive labels (that are clumps) and
-     making a binary image, then running the connected component algorithm
-     to find the number of clumps in the image.*/
-  if(up->clumplabsname)
+
+
+  /* Read the keywords from the clumps image if necessary. */
+  if(p->clumps)
     {
-      keys[0].keyname="CLUMPSN";
-      keys[1].keyname="NCLUMPS";
-      gal_fits_read_keywords(up->clumplabsname, up->clumphdu, keys, 2);
-      p->clumpsn = keys[0].status ? NAN : keys[0].d;
+      keys[0].name="CLUMPSN";               keys[1].name="NUMLABS";
+      keys[0].type=GAL_TYPE_FLOAT32;        keys[1].type=GAL_TYPE_SIZE_T;
+      keys[0].array=&p->clumpsn;            keys[1].array=&p->numclumps;
+      gal_fits_key_read(clumpsfile, p->clumpshdu, keys, 0, 0);
+      if(keys[0].status) p->clumpsn=NAN;
       if(keys[1].status)
         error(EXIT_FAILURE, 0, "couldn't find NCLUMPS in the header of "
-              "%s (hdu: %s).", up->clumplabsname, up->clumphdu);
-      else
-        p->numclumps=keys[1].l;
+              "%s (hdu: %s).", p->clumpsfile, p->clumpshdu);
     }
 
-}
 
+  /* Clean up. */
+  keys[0].name=keys[1].name=NULL;
+  keys[0].array=keys[1].array=NULL;
+  gal_data_array_free(keys, 2, 1);
+}
 
 
 
 
 
+/* To make the catalog processing more scalable (and later allow for
+   over-lappping regions), we will define a tile for each object. */
 void
-preparearrays(struct mkcatalogparams *p)
+ui_one_tile_per_object(struct mkcatalogparams *p)
 {
-  size_t i;
-  int bitpix, anyblank;
-
-
-  /* Prepare the columns and allocate the p->objcols and p->clumpcols
-     arrays to keep the macros of what output they should keep. */
-  gal_linkedlist_sll_to_array(p->allcolsll, &p->allcols, &p->allncols, 0);
-  if(p->allncols==0)
-    error(EXIT_FAILURE, 0, "no columns specified for output");
-  errno=0; p->objcols=malloc(p->allncols*sizeof *p->objcols);
-  if(p->objcols==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for p->objcols in "
-          "preprarearrays (ui.c)", p->allncols*sizeof *p->objcols);
-  errno=0; p->clumpcols=malloc(p->allncols*sizeof *p->clumpcols);
-  if(p->clumpcols==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for p->clumpcols in "
-          "preprarearrays (ui.c)", p->allncols*sizeof *p->clumpcols);
-
-
-  /* Put all the input columns into the object or clumps columns and
-     count how many columns each should have. */
-  p->objncols=p->clumpncols=0;
-  for(i=0;i<p->allncols;++i)
-    {
-      switch(p->allcols[i])
-        {
-        case CATID:
-          p->objcols[p->objncols++]=p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATHOSTOBJID:
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATIDINHOSTOBJ:
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATNUMCLUMPS:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATAREA:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSAREA:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATX:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATY:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATGEOX:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATGEOY:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSX:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSY:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSGEOX:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSGEOY:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATRA:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATDEC:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATGEORA:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATGEODEC:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSRA:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSDEC:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSGEORA:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSGEODEC:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATBRIGHTNESS:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSBRIGHTNESS:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATUPPERLIMITMAG:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATNORIVERBRIGHTNESS:
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATMAGNITUDE:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATMAGNITUDEERR:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATCLUMPSMAGNITUDE:
-          p->objcols[p->objncols++] = p->allcols[i];
-          break;
-        case CATRIVERAVE:
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATRIVERNUM:
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATSN:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATSKY:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATSTD:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATSEMIMAJOR:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATSEMIMINOR:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATPOSITIONANGLE:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATGEOSEMIMAJOR:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATGEOSEMIMINOR:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        case CATGEOPOSITIONANGLE:
-          p->objcols[p->objncols++] = p->allcols[i];
-          p->clumpcols[p->clumpncols++] = p->allcols[i];
-          break;
-        default:
-          error(EXIT_FAILURE, 0, "a bug! Please contact us at %s so we "
-                "can fix the problem. For some reason p->allcols[%zu] "
-                "is given the value %zu which is not recognized in "
-                "preparearrays (ui.c)", PACKAGE_BUGREPORT, i,
-                p->allcols[i]);
-        }
-    }
+  size_t ndim=p->input->ndim;
+
+  int32_t *l, *lf, *start;
+  size_t i, d, *min, *max, width=2*ndim;
+  size_t *coord=gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+  size_t *minmax=gal_data_malloc_array(GAL_TYPE_SIZE_T,
+                                       width*p->numobjects);
+
+
+  /* Initialize the minimum and maximum position for each tile/object. So,
+     we'll initialize the minimum coordinates to the maximum possible
+     `size_t' value (in `GAL_BLANK_SIZE_T') and the maximums to zero. */
+  for(i=0;i<p->numobjects;++i)
+    for(d=0;d<ndim;++d)
+      {
+        minmax[ i * width +        d ] = GAL_BLANK_SIZE_T; /* Minimum. */
+        minmax[ i * width + ndim + d ] = 0;                /* Maximum. */
+      }
+
+  /* Go over the objects label image and correct the minimum and maximum
+     coordinates. */
+  start=p->objects->array;
+  lf=(l=p->objects->array)+p->objects->size;
+  do
+    if(*l>0)
+      {
+        /* Get the coordinates of this pixel. */
+        gal_dimension_index_to_coord(l-start, ndim, p->objects->dsize, coord);
+
+        /* Check to see this coordinate is the smallest/largest found so
+           far for this label. Note that labels start from 1, while indexs
+           here start from zero. */
+        min = &minmax[ (*l-1) * width        ];
+        max = &minmax[ (*l-1) * width + ndim ];
+        for(d=0;d<ndim;++d)
+          {
+            if( coord[d] < min[d] ) min[d] = coord[d];
+            if( coord[d] > max[d] ) max[d] = coord[d];
+          }
+      }
+  while(++l<lf);
+
+  /* For a check.
+  for(i=0;i<p->numobjects;++i)
+    printf("%zu: (%zu, %zu) --> (%zu, %zu)\n", i+1, minmax[i*width],
+           minmax[i*width+1], minmax[i*width+2], minmax[i*width+3]);
+  */
+
+  /* Make the tiles. */
+  p->tiles=gal_tile_series_from_minmax(p->input, minmax, p->numobjects);
+
+  /* Clean up. */
+  free(coord);
+  free(minmax);
+}
+
+
+
+
+
+/* When both catalogs need to be made, we need a separator, the output
+   names will either be built based on the input name or output name (if
+   given). In both cases, the operations are the same, just the base name
+   differs. So to keep things clean, we have defined this function. */
+static void
+ui_preparations_both_names(struct mkcatalogparams *p, char *basename)
+{
+  char *end, suffix[50];
+
+  /* Set the objects name */
+  end="_o";
+  sprintf(suffix, "%s.%s", end,
+          p->cp.tableformat==GAL_TABLE_FORMAT_TXT ? "txt" : "fits");
+  p->objectsout=gal_checkset_automatic_output(&p->cp, p->inputname,
+                                              suffix);
+
+  /* Set the clumps name */
+  end="_c";
+  sprintf(suffix, "%s.%s", end,
+          p->cp.tableformat==GAL_TABLE_FORMAT_TXT ? "txt" : "fits");
+  p->clumpsout=gal_checkset_automatic_output(&p->cp, p->inputname,
+                                             suffix);
+}
+
+
+
 
 
-  /* Read the input image. Note that after this step, everything
-     depends on having an input filename. If the user just wants to
-     check the parameters, there is no input file name. */
-  if(p->up.inputname)
+/* Set the output name. */
+static void
+ui_preparations_outnames(struct mkcatalogparams *p)
+{
+  char *suffix;
+
+  /* Set the output filename */
+  if(p->cp.output)
     {
-      gal_fits_file_to_float(p->up.inputname, p->up.maskname, p->cp.hdu,
-                                  p->up.mhdu, &p->img, &bitpix, &anyblank,
-                                  &p->s0, &p->s1);
-      gal_fits_read_wcs(p->up.inputname, p->cp.hdu, 0, 0, &p->nwcs,
-                        &p->wcs);
-
-
-      /* Read and check the object, sky and skystd HDUs. Note that the
-         clumps image is only used when the objects image says a clumps
-         image exists. */
-      checksetfloat(p, p->up.skyname, p->up.skyhdu, &p->sky);
-      checksetfloat(p, p->up.stdname, p->up.stdhdu, &p->std);
-      checksetlong(p, p->up.objlabsname, p->up.objhdu, &p->objects);
-      if(p->up.clumplabsname)
-        checksetlong(p, p->up.clumplabsname, p->up.clumphdu, &p->clumps);
-      else p->clumps=NULL;
-      if(p->up.upmasknameset)
-        checksetlong(p, p->up.upmaskname, p->up.upmaskhdu, &p->upmask);
-      else p->upmask=NULL;
-
-
-      /* Read the necessary keywords. */
-      readkeywords(p);
-
-      /* Allocate the catalog arrays: */
-      if(p->objncols>0 && p->numobjects>0)
-        {
-          errno=0;
-          p->objcat=malloc(p->objncols*p->numobjects*sizeof *p->objcat);
-          if(p->objcat==NULL)
-            error(EXIT_FAILURE, errno, "%zu bytes for p->objcat in "
-                  "preprarearrays (ui.c)",
-                  p->objncols*p->numobjects*sizeof *p->objcat);
-        }
-      else p->objcat=NULL;
-      if(p->clumpncols>0 && p->numclumps>0)
+      /* Make sure the given output filename corresponds to the
+         `tableformat' option. */
+      gal_table_check_fits_format(p->cp.output, p->cp.tableformat);
+
+      /* If a clumps image has been read, we have two outputs. */
+      if(p->clumps)  ui_preparations_both_names(p, p->cp.output);
+      else           p->objectsout=p->cp.output;
+    }
+  else
+    {
+      /* Both clumps and object catalogs are necessary. */
+      if(p->clumps)  ui_preparations_both_names(p, p->inputname);
+
+      /* We only need one objects catalog. */
+      else
         {
-          errno=0;
-          p->clumpcat=malloc(p->clumpncols*p->numclumps
-                             * sizeof *p->clumpcat);
-          if(p->clumpcat==NULL)
-            error(EXIT_FAILURE, errno, "%zu bytes for p->clumpcat in "
-                  "preprarearrays (ui.c)",
-                  p->clumpncols*p->numclumps*sizeof *p->clumpcat);
+          suffix = ( p->cp.tableformat==GAL_TABLE_FORMAT_TXT
+                     ? ".txt" : ".fits" );
+          p->objectsout=gal_checkset_automatic_output(&p->cp, p->inputname,
+                                                      suffix);
         }
-      else p->clumpcat=NULL;
     }
+}
+
+
 
 
-  /* Allocate two arrays to keep all the basic information about each
-     object and clump. Note that there should be one row more than the
-     total number of objects or clumps. This is because we want each
-     label to be its row number and we don't have any object label of
-     zero.*/
-  errno=0; p->oinfo=calloc(OCOLUMNS*(p->numobjects+1), sizeof *p->oinfo);
-  if(p->oinfo==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for p->oinfo in mkcatalog "
-          "(mkcatalog.c)", OCOLUMNS*(p->numobjects+1)*sizeof *p->oinfo);
-  errno=0; p->cinfo=calloc(CCOLUMNS*(p->numclumps+1), sizeof *p->cinfo);
-  if(p->cinfo==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for p->cinfo in mkcatalog "
-          "(mkcatalog.c)", CCOLUMNS*(p->numclumps+1)*sizeof *p->cinfo);
-
-  /* Set the shift values to NAN for all the objects and clumps: */
-  for(i=1;i<=p->numobjects;++i)
-    p->oinfo[i*OCOLUMNS+OPOSSHIFTX]=p->oinfo[i*OCOLUMNS+OPOSSHIFTY]=NAN;
-  for(i=1;i<=p->numclumps;++i)
-    p->cinfo[i*CCOLUMNS+CPOSSHIFTX]=p->cinfo[i*CCOLUMNS+CPOSSHIFTY]=NAN;
-
-  /* Allocate the random number generator: */
-  if(p->up.upperlimitmagset)
-    {
-      gsl_rng_env_setup();
-      if(p->envseed==0)
-        gsl_rng_default_seed=gal_timing_time_based_rng_seed();
-    }
 
-  /* Clean up: */
-  gal_linkedlist_free_sll(p->allcolsll);
+/* Sanity checks and preparations for the upper-limit magnitude. */
+static void
+ui_preparations_upperlimit(struct mkcatalogparams *p)
+{
+  /* Check the number of random samples. */
+  if( p->upnum < MKCATALOG_UPPERLIMIT_MINIMUM_NUM )
+    error(EXIT_FAILURE, 0, "%zu not acceptable as `--upnum'. The minimum "
+          "acceptable number of random samples for the upper limit "
+          "magnitude is %d", p->upnum, MKCATALOG_UPPERLIMIT_MINIMUM_NUM);
+
+  /* Check if sigma-clipping parameters have been given. */
+  if( isnan(p->upsigmaclip[0]) )
+    error(EXIT_FAILURE, 0, "`--upsigmaclip' is mandatory for measuring "
+          "the upper-limit magnitude. It takes two numbers separated by "
+          "a comma. The first is the multiple of sigma and the second is "
+          "the aborting criteria: <1: tolerance level, >1: number of "
+          "clips");
+
+  /* Check if the sigma multiple is given. */
+  if( isnan(p->upnsigma) )
+    error(EXIT_FAILURE, 0, "`--upnsigma' is mandatory for measuring the "
+          "upperlimit magnitude. Its value is the multiple of final sigma "
+          "that is reported as the upper-limit");
+
+  /* Set the random number generator. */
+  gsl_rng_env_setup();
+  p->rng=gsl_rng_alloc(gsl_rng_default);
+  p->seed = ( p->envseed
+              ? gsl_rng_default_seed
+              : gal_timing_time_based_rng_seed() );
+  if(p->envseed) gsl_rng_set(p->rng, p->seed);
+  p->rngname=gsl_rng_name(p->rng);
+}
+
+
+
+
+
+
+
+
+
+void
+ui_preparations(struct mkcatalogparams *p)
+{
+  /* If no columns are requested, then inform the user. */
+  if(p->columnids==NULL)
+    error(EXIT_FAILURE, 0, "no columns requested, please run again with "
+          "`--help' for the full list of columns you can ask for");
+
+
+  /* Read the inputs. */
+  ui_preparations_read_inputs(p);
+
+
+  /* Set the output filename(s). */
+  ui_preparations_outnames(p);
+
+
+  /* Read the helper keywords from the inputs and if they aren't present
+     then calculate the necessary parameters. */
+  ui_preparations_read_keywords(p);
+
+
+  /* Allocate the output columns to fill up with the program. */
+  columns_define_alloc(p);
+
+
+  /* Make the tiles that cover each object. */
+  ui_one_tile_per_object(p);
+
+
+  /* Allocate the reference random number generator and seed values. It
+     will be cloned once for every thread. If the user hasn't called
+     `envseed', then we want it to be different for every run, so we need
+     to re-set the seed. */
+  if(p->upperlimit) ui_preparations_upperlimit(p);
+
+  if( p->hasmag && isnan(p->zeropoint) )
+    error(EXIT_FAILURE, 0, "no zeropoint specified");
 }
 
 
@@ -1464,68 +830,86 @@ preparearrays(struct mkcatalogparams *p)
 /**************************************************************/
 /************         Set the parameters          *************/
 /**************************************************************/
+
 void
-setparams(int argc, char *argv[], struct mkcatalogparams *p)
+ui_read_check_inputs_setup(int argc, char *argv[], struct mkcatalogparams *p)
 {
-  struct gal_commonparams *cp=&p->cp;
+  struct gal_options_common_params *cp=&p->cp;
+  char *skyfile, *stdfile, *clumpsfile, *objectsfile;
 
-  /* Set the non-zero initial values, the structure was initialized to
-     have a zero value for all elements. */
-  cp->spack         = SPACK;
-  cp->verb          = 1;
-  cp->numthreads    = num_processors(NPROC_CURRENT);
-  cp->removedirinfo = 1;
 
-  p->threshold      = NAN;
+  /* Include the parameters necessary for argp from this program (`args.h')
+     and for the common options to all Gnuastro (`commonopts.h'). We want
+     to directly put the pointers to the fields in `p' and `cp', so we are
+     simply including the header here to not have to use long macros in
+     those headers which make them hard to read and modify. This also helps
+     in having a clean environment: everything in those headers is only
+     available within the scope of this function. */
+#include <gnuastro-internal/commonopts.h>
+#include "args.h"
 
-  /* Read the arguments. */
+
+  /* Initialize the options and necessary information.  */
+  ui_initialize_options(p, program_options, gal_commonopts_options);
+
+
+  /* Read the command-line options and arguments. */
   errno=0;
   if(argp_parse(&thisargp, argc, argv, 0, 0, p))
     error(EXIT_FAILURE, errno, "parsing arguments");
 
-  /* Add the user default values and save them if asked. */
-  GAL_CONFIGFILES_CHECK_SET_CONFIG;
 
-  /* Check if all the required parameters are set. */
-  checkifset(p);
+  /* Read the configuration files and set the common values. */
+  gal_options_read_config_set(&p->cp);
 
-  /* Do a sanity check. Note that if the user just wants to see the
-     parameters and hasn't given any file name, sanity check is
-     useless, because in MakeProfiles, sanitycheck just checks the
-     file names. So we first have to check if an input */
-  if(p->up.inputname)
-    sanitycheck(p);
 
-  /* Make the array of input images. */
-  preparearrays(p);
+  /* Read the options into the program's structure, and check them and
+     their relations prior to printing.
+  ui_read_check_only_options(p);
+  */
+
+  /* Print the option values if asked. Note that this needs to be done
+     after the option checks so un-sane values are not printed in the
+     output state. */
+  gal_options_print_state(&p->cp);
 
-  /* Print the values for each parameter. */
-  if(cp->printparams)
-    GAL_CONFIGFILES_REPORT_PARAMETERS_SET;
 
-  /* Everything is ready, notify the user of the program starting. */
-  if(cp->verb)
+  /* Check that the options and arguments fit well with each other. Note
+     that arguments don't go in a configuration file. So this test should
+     be done after (possibly) printing the option values. */
+  ui_check_options_and_arguments(p);
+
+
+  /* Read/allocate all the necessary starting arrays. */
+  ui_preparations(p);
+
+
+  /* Inform the user. */
+  if(!p->cp.quiet)
     {
-      printf(SPACK_NAME" started on %s", ctime(&p->rawtime));
-      printf("  - Input   %s (hdu: %s)\n", p->up.inputname, p->cp.hdu);
-      if(p->up.masknameset)
-        printf("  - Mask   %s (hdu: %s)\n", p->up.maskname, p->up.mhdu);
-      printf("  - Objects %s (hdu: %s)\n", p->up.objlabsname,
-             p->up.objhdu);
-      if(p->up.clumplabsname)
-        printf("  - Clumps  %s (hdu: %s)\n", p->up.clumplabsname,
-               p->up.clumphdu);
-      printf("  - Sky     %s (hdu: %s)\n", p->up.skyname, p->up.skyhdu);
-      printf("  - Sky STD %s (hdu: %s)\n", p->up.stdname, p->up.stdhdu);
-      if(p->up.upperlimitmagset)
-        {
-          if(p->up.upmasknameset)
-            printf("  - Upper limit mask: %s (hdu: %s)\n", p->up.inputname,
-                   p->cp.hdu);
-          printf("  - RNG type for upper-limit magnitude: %s\n",
-                 gsl_rng_default->name);
-          printf("  - RNG seed for upper-limit magnitude: %ld\n",
-                 gsl_rng_default_seed);
+      /* Set the names for easy reading. */
+      skyfile     = p->skyfile     ? p->skyfile     : p->inputname;
+      stdfile     = p->stdfile     ? p->stdfile     : p->inputname;
+      clumpsfile  = p->clumpsfile  ? p->clumpsfile  : p->inputname;
+      objectsfile = p->objectsfile ? p->objectsfile : p->inputname;
+
+      /* Write the information. */
+      printf(PROGRAM_NAME" started on %s", ctime(&p->rawtime));
+      printf("  - Using %zu CPU thread%s\n", p->cp.numthreads,
+             p->cp.numthreads==1 ? "." : "s.");
+      printf("  - Input:   %s (hdu: %s)\n", p->inputname, p->cp.hdu);
+      printf("  - Objects: %s (hdu: %s)\n", objectsfile,  p->objectshdu);
+      if(p->clumps)
+        printf("  - Clumps:  %s (hdu: %s)\n", clumpsfile, p->clumpshdu);
+      printf("  - Sky:     %s (hdu: %s)\n", skyfile, p->skyhdu);
+      printf("  - Sky STD: %s (hdu: %s)\n", stdfile, p->stdhdu);
+      if(p->upmaskfile)
+        printf("  - Upper limit magnitude mask: %s (hdu: %s)\n",
+               p->upmaskfile, p->cp.hdu);
+      if(p->upperlimit)
+        {
+          printf("  - Random number generator name: %s\n", p->rngname);
+          printf("  - Random number generator seed: %lu\n", p->seed);
         }
     }
 }
@@ -1553,36 +937,58 @@ setparams(int argc, char *argv[], struct mkcatalogparams 
*p)
 /************      Free allocated, report         *************/
 /**************************************************************/
 void
-freeandreport(struct mkcatalogparams *p, struct timeval *t1)
+ui_free_report(struct mkcatalogparams *p, struct timeval *t1)
 {
-  /* Free all the allocated spaces: */
-  free(p->sky);
-  free(p->std);
-  free(p->objcat);
+  size_t d;
+
+  /* The temporary arrays for RA and Dec. */
+  if(p->rd_vo || p->rd_vc || p->rd_go || p->rd_gc || p->rd_vcc || p->rd_gcc)
+    {
+      /* First free the separate dimensions in each value. */
+      for(d=0;d<p->input->ndim;++d)
+        {
+          if(p->rd_vo)  free(p->rd_vo[d]);
+          if(p->rd_vc)  free(p->rd_vc[d]);
+          if(p->rd_go)  free(p->rd_go[d]);
+          if(p->rd_gc)  free(p->rd_gc[d]);
+          if(p->rd_vcc) free(p->rd_vcc[d]);
+          if(p->rd_gcc) free(p->rd_gcc[d]);
+        }
+
+      /* Then free the container arrays. */
+      if(p->rd_vo)  free(p->rd_vo);
+      if(p->rd_vc)  free(p->rd_vc);
+      if(p->rd_go)  free(p->rd_go);
+      if(p->rd_gc)  free(p->rd_gc);
+      if(p->rd_vcc) free(p->rd_vcc);
+      if(p->rd_gcc) free(p->rd_gcc);
+    }
+
+  /* If a random number generator was allocated, free it. */
+  if(p->rng) gsl_rng_free(p->rng);
+
+  /* Free the allocated arrays: */
+  free(p->skyhdu);
+  free(p->stdhdu);
   free(p->cp.hdu);
-  free(p->clumps);
-  free(p->allcols);
-  free(p->objects);
-  free(p->objcols);
-  free(p->clumpcat);
-  free(p->ocatname);
-  free(p->ccatname);
-  free(p->clumpcols);
-  free(p->up.objhdu);
+  free(p->oiflag);
+  free(p->ciflag);
+  free(p->skyfile);
+  free(p->stdfile);
   free(p->cp.output);
-  free(p->up.skyhdu);
-  free(p->up.stdhdu);
-  free(p->up.clumphdu);
-  if(p->upmask) free(p->upmask);
-  if(p->up.mhduset) free(p->up.mhdu);
-  if(p->wcs) wcsvfree(&p->nwcs, &p->wcs);
-  if(p->up.skynameset) free(p->up.skyname);
-  if(p->up.stdnameset) free(p->up.stdname);
-  if(p->up.masknameset) free(p->up.maskname);
-  if(p->up.objlabsnameset) free(p->up.objlabsname);
-  if(p->up.clumplabsnameset) free(p->up.clumplabsname);
+  free(p->clumpshdu);
+  free(p->objectshdu);
+  free(p->clumpsfile);
+  free(p->objectsfile);
+  gal_data_free(p->sky);
+  gal_data_free(p->std);
+  gal_data_free(p->input);
+  gal_data_free(p->upmask);
+  gal_data_free(p->clumps);
+  gal_data_free(p->objects);
+  gal_data_array_free(p->tiles, p->numobjects, 0);
 
   /* Print the final message. */
-  if(p->cp.verb)
-    gal_timing_report(t1, SPACK_NAME" finished in", 0);
+  if(!p->cp.quiet)
+    gal_timing_report(t1, PROGRAM_NAME" finished in: ", 0);
 }
diff --git a/bin/mkcatalog/ui.h b/bin/mkcatalog/ui.h
index 9a1fd5f..a5f72e9 100644
--- a/bin/mkcatalog/ui.h
+++ b/bin/mkcatalog/ui.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <address@hidden>
 Contributing author(s):
-Copyright (C) 2015, Free Software Foundation, Inc.
+Copyright (C) 2016, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -20,13 +20,98 @@ General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
 **********************************************************************/
-#ifndef IMCROPUI_H
-#define IMCROPUI_H
+#ifndef UI_H
+#define UI_H
+
+
+
+
+
+/* Available letters for short options:
+
+   f g k l u v w x y
+   H J L W X Y
+*/
+enum option_keys_enum
+{
+  /* With short-option version. */
+  UI_KEY_OBJECTSFILE     = 'O',         /* General settings. */
+  UI_KEY_CLUMPSFILE      = 'C',
+  UI_KEY_SKYFILE         = 's',
+  UI_KEY_STDFILE         = 't',
+  UI_KEY_ZEROPOINT       = 'z',
+  UI_KEY_SKYSUBTRACTED   = 'E',
+  UI_KEY_THRESHOLD       = 'R',
+  UI_KEY_ENVSEED         = 'e',
+
+  UI_KEY_IDS             = 'i',         /* Catalog columns. */
+  UI_KEY_HOSTOBJID       = 'j',
+  UI_KEY_NUMCLUMPS       = 'c',
+  UI_KEY_AREA            = 'a',
+  UI_KEY_X               = 'x',
+  UI_KEY_Y               = 'y',
+  UI_KEY_RA              = 'r',
+  UI_KEY_DEC             = 'd',
+  UI_KEY_BRIGHTNESS      = 'b',
+  UI_KEY_MAGNITUDE       = 'm',
+  UI_KEY_MAGNITUDEERR    = 'G',
+  UI_KEY_UPPERLIMITMAG   = 'u',
+  UI_KEY_SN              = 'n',
+  UI_KEY_SEMIMAJOR       = 'A',
+  UI_KEY_SEMIMINOR       = 'B',
+  UI_KEY_AXISRATIO       = 'Q',
+  UI_KEY_POSITIONANGLE   = 'p',
+
+  /* Only with long version (start with a value 1000, the rest will be set
+     automatically). */
+  UI_KEY_OBJECTSHDU      = 1000,        /* General settings. */
+  UI_KEY_CLUMPSHDU,
+  UI_KEY_SKYHDU,
+  UI_KEY_STDHDU,
+  UI_KEY_NSIGMAG,
+  UI_KEY_UPMASKFILE,
+  UI_KEY_UPMASKHDU,
+  UI_KEY_UPNUM,
+  UI_KEY_UPSIGMACLIP,
+  UI_KEY_UPNSIGMA,
+
+  UI_KEY_OBJID,                         /* Catalog columns. */
+  UI_KEY_IDINHOSTOBJ,
+  UI_KEY_CLUMPSAREA,
+  UI_KEY_GEOX,
+  UI_KEY_GEOY,
+  UI_KEY_CLUMPSX,
+  UI_KEY_CLUMPSY,
+  UI_KEY_CLUMPSGEOX,
+  UI_KEY_CLUMPSGEOY,
+  UI_KEY_GEORA,
+  UI_KEY_GEODEC,
+  UI_KEY_CLUMPSRA,
+  UI_KEY_CLUMPSDEC,
+  UI_KEY_CLUMPSGEORA,
+  UI_KEY_CLUMPSGEODEC,
+  UI_KEY_CLUMPSBRIGHTNESS,
+  UI_KEY_NORIVERBRIGHTNESS,
+  UI_KEY_CLUMPSMAGNITUDE,
+  UI_KEY_UPPERLIMIT,
+  UI_KEY_RIVERAVE,
+  UI_KEY_RIVERNUM,
+  UI_KEY_SKY,
+  UI_KEY_STD,
+  UI_KEY_GEOSEMIMAJOR,
+  UI_KEY_GEOSEMIMINOR,
+  UI_KEY_GEOAXISRATIO,
+  UI_KEY_GEOPOSITIONANGLE,
+};
+
+
+
+
 
 void
-setparams(int argc, char *argv[], struct mkcatalogparams *p);
+ui_read_check_inputs_setup(int argc, char *argv[], struct mkcatalogparams *p);
 
 void
-freeandreport(struct mkcatalogparams *p, struct timeval *t1);
+ui_free_report(struct mkcatalogparams *p, struct timeval *t1);
 
 #endif
diff --git a/bin/mkcatalog/upperlimit.c b/bin/mkcatalog/upperlimit.c
index d6d3703..095ff1a 100644
--- a/bin/mkcatalog/upperlimit.c
+++ b/bin/mkcatalog/upperlimit.c
@@ -22,292 +22,103 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 **********************************************************************/
 #include <config.h>
 
-#include <math.h>
 #include <stdio.h>
 #include <errno.h>
 #include <error.h>
+#include <float.h>
 #include <stdlib.h>
-#include <sys/time.h>
 
-#include <gsl/gsl_rng.h>
-#include <gnuastro/data.h>
-#include <gnuastro/fits.h>
+#include <gnuastro/tile.h>
 #include <gnuastro/threads.h>
-#include <gnuastro/txtarray.h>
+#include <gnuastro/dimension.h>
 #include <gnuastro/statistics.h>
-#include <gsl/gsl_statistics_double.h>
-
-#include <timing.h>
-
-#include "upperlimit.h"
 
+#include "main.h"
+#include "mkcatalog.h"
 
 
 
 /*********************************************************************/
-/************           Structures and macros         ****************/
+/*******************       Tiles for clumps       ********************/
 /*********************************************************************/
-/* These structures and macros are defined in the C file since they are
-   only necessary in the processing here, they do not need to be given to
-   the user through the header, it will just complicate their
-   name-space. */
-struct upperlimitparams
-{
-  size_t                  s0;   /* Number of rows in image.          */
-  size_t                  s1;   /* Number of columns in image.       */
-  int                envseed;   /* ==1: evironment for rng setup.    */
-  long                minlab;   /* Minimum label in image.           */
-  long               numlabs;   /* Number of labels in image.        */
-  float                 *img;   /* The input image.                  */
-  size_t                *box;  /* The box parameters of each label. */
-  unsigned char        **pix;   /* The pixels of each label.         */
-  float                 *std;  /* Standard deviation of the sums.   */
-  size_t               upnum;   /* Number of random samples.         */
-  size_t          numthreads;   /* Number of threads to use.         */
-  float          sclipmultip;   /* Multiple of STD for sigma clip.   */
-  float            sclipaccu;   /* Accuracy to stop sigma clipping.  */
-};
-
-
-
-
-
-/* Information for each thread. */
-struct tupperlimitparams
+static gal_data_t *
+upperlimit_make_clump_tiles(struct mkcatalog_passparams *pp)
 {
-  size_t                  id;   /* Id of this thread.                */
-  size_t             *indexs;   /* Indexes for this thread.          */
-  pthread_barrier_t       *b;   /* Barrier for all threads.          */
-  struct upperlimitparams *p;   /* Pointer to main program struct.   */
-};
-
-
-
-
-
-/* Columns of labeled region information. Note that the width columns
-   (WIDCOL) are actually the same as the max columns (MAXCOL). The
-   width values are written over the maximum columns because we don't
-   need the maximum values any more. */
-#define XMINCOL 0
-#define YMINCOL 1
-#define XMAXCOL 2
-#define YMAXCOL 3
-#define XWIDCOL XMAXCOL
-#define YWIDCOL YMAXCOL
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-/*********************************************************************/
-/************               Preparations              ****************/
-/*********************************************************************/
-static void
-fillseginfo(struct upperlimitparams *p, long *seg)
-{
-  float *f, *ff;
-  long *l, *ll, maxlab;
-  unsigned char *u, *uu;
-  size_t xw, yw, xmin, ymin;
-  size_t i, j, asize, label, size=p->s0*p->s1;
-
-
-  /* Find the minimum and maximum labels: */
-  maxlab    = INT32_MIN;
-  p->minlab = INT32_MAX;
-  ll=(l=seg)+size;
-  do
-    /* We don't want to look at pixels with value zero or blank pixels, so
-       ignore them.  */
-    if( *l!=0 && *l!=GAL_DATA_BLANK_LONG )
+  gal_data_t *input=pp->p->input;
+  size_t ndim=input->ndim, *dsize=input->dsize;
+
+  int32_t *O, *C;
+  gal_data_t *tiles=NULL;
+  float *I, *II, *start=input->array;
+  size_t increment=0, num_increment=1;
+  size_t i, d, *min, *max, width=2*ndim;
+  size_t *coord=gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+  size_t *minmax=gal_data_malloc_array(GAL_TYPE_SIZE_T,
+                                       width*pp->clumpsinobj);
+
+  /* Initialize the minimum and maximum position for each tile/clump. So,
+     we'll initialize the minimum coordinates to the maximum possible
+     `size_t' value (in `GAL_BLANK_SIZE_T') and the maximums to zero. */
+  for(i=0;i<pp->clumpsinobj;++i)
+    for(d=0;d<ndim;++d)
       {
-        if(*l>maxlab)    maxlab=*l;
-        if(*l<p->minlab) p->minlab=*l;
+        minmax[ i * width +        d ] = GAL_BLANK_SIZE_T; /* Minimum. */
+        minmax[ i * width + ndim + d ] = 0;                /* Maximum. */
       }
-  while(++l<ll);
-
-
-  /* Do a small sanity check: */
-  if(maxlab<0 || p->minlab<0)
-    error(EXIT_FAILURE, 0, "the labeled image must not contain negative "
-          "pixels");
-
-
-  /* Allocate the space to keep the object information. This array will
-     keep the minimum and maximum coordinate for each labeled region. Note
-     that max is in the image, and we want to use the labels as indexs (row
-     numbers), so we must allocate max+1 rows.
-
-     Note: we are not assuming that the first index is 1. Because many
-     cases can arise where the lowest index can be a large number (for
-     example when the user can be working on a sub-set of objects selected
-     from a large catalog), so we don't want to allocate a huge amount of
-     memory and waste thread resources on them. We have stored the minimum
-     label and will use that in reporting the final output to make a full
-     array. */
-  p->numlabs = maxlab - p->minlab + 1;
-  errno=0;
-  p->box=malloc(p->numlabs * 4 * sizeof *p->box);
-  if(p->box==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for p->box",
-          p->numlabs * 4 * sizeof *p->box);
-
-  /* Initialize the information array*/
-  for(i=0;i<p->numlabs;++i)
-    {
-      p->box[ i*4 + XMINCOL ] = p->s0;
-      p->box[ i*4 + YMINCOL ] = p->s1;
-      p->box[ i*4 + XMAXCOL ] = p->box[ i*4 + YMAXCOL ] = 0;
-    }
-
-  /* Fill it in with the minimum and maximum positions. */
-  for(i=0;i<p->s0;++i)
-    for(j=0;j<p->s1;++j)
-      if(seg[i*p->s1+j]>0)
-       {
-         label = seg[i*p->s1+j] - p->minlab;
-         if(i<p->box[ label*4 + XMINCOL ]) p->box[ label*4 + XMINCOL ] = i;
-         if(j<p->box[ label*4 + YMINCOL ]) p->box[ label*4 + YMINCOL ] = j;
-         if(i>p->box[ label*4 + XMAXCOL ]) p->box[ label*4 + XMAXCOL ] = i;
-         if(j>p->box[ label*4 + YMAXCOL ]) p->box[ label*4 + YMAXCOL ] = j;
-       }
-
-  /* For a check. Note that in DS9 the coordinates are inverted and
-     start from 1, not zero.
-  {
-    i=0;
-    printf("%zu: (%zu, %zu) --> (%zu, %zu)\n", i+p->minlab,
-          p->box[i*4+YMINCOL]+1, p->box[i*4+XMINCOL]+1,
-          p->box[i*4+YMAXCOL]+1, p->box[i*4+XMAXCOL]+1);
-  }
-  */
-
-
-  /* Allocate the pointers to the object's pixels and allocate space
-     for the area of each detection. In the meantime, Change the xmax
-     and ymax to width in X and width in Y*/
-  errno=0;
-  p->pix=malloc(p->numlabs * sizeof *p->pix);
-  if(p->pix==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for p->pix",
-          p->numlabs * sizeof *p->pix);
-  for(i=0; i<p->numlabs; ++i)
-    {
-      /* Replace the max values with the width of each box. */
-      p->box[i*4+XWIDCOL] = p->box[i*4+XMAXCOL] - p->box[i*4+XMINCOL] + 1;
-      p->box[i*4+YWIDCOL] = p->box[i*4+YMAXCOL] - p->box[i*4+YMINCOL] + 1;
-
-      /* Allocate space for the pixels of this label. */
-      errno=0;
-      asize = p->box[i*4+XWIDCOL] * p->box[i*4+YWIDCOL] * sizeof **p->pix;
-      p->pix[i] = malloc(asize);
-      if(p->pix[i]==NULL)
-        error(EXIT_FAILURE, errno, "%zu bytes for p->pix[i] (i=%zu)",
-              asize, i);
-
-      /* Set the array values. Any pixel that is equal to the object's
-        label over the box is given a value of 1.0f, other pixels are
-        given a value of 0.0f. We set the starting pointers for the
-        larger and smaller arrays in this row, then go along the row
-        until it finshes. `j' will take us to the next row after this
-        row finishes. */
-      xmin = p->box[ i*4+XMINCOL ];
-      ymin = p->box[ i*4+YMINCOL ];
-      xw   = p->box[ i*4+XWIDCOL ];
-      yw   = p->box[ i*4+YWIDCOL ];
-      for(j=0; j<xw; ++j)
-       {
-         uu = ( u = p->pix[i] + yw*j ) + yw;
-         l  = seg + (xmin+j)*p->s1 + ymin;
-         do *u = (*l++ == i+p->minlab); while(++u<uu);
-       }
-    }
-
-
-  /* For a check:
-  i=0;
-  gal_fits_array_to_file("tmpf.fits", "check", BYTE_IMG, p->pix[i],
-                        p->box[i*4+XWIDCOL], p->box[i*4+YWIDCOL], 0,
-                        NULL, NULL, "myprog");
-  exit(0);
-  */
-
-  /* Allocate an array for the output values, note that this is for the
-     full list of IDs, not just those in the image. */
-  p->std = malloc( (maxlab+1) * sizeof *p->std );
-  if(p->std==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for p->std (upperlimit.c)",
-          (maxlab+1) * sizeof *p->std);
-  ff=(f=p->std)+(maxlab+1); do *f++=NAN; while(f<ff);
-}
 
-
-
-
-
-/* Subtract the Sky and set all the segmentation and mask pixels to
-   NaN. Note that the mask and sky images are optional. If the caller
-   didn't want them, they have to be set to NULL.
-
-   We could also be doing this during the actual flux calculation, but
-   since many iterations of many objects will be done, it is more efficient
-   to do all the preparations (setting blank pixels and subtracting the
-   sky. */
-void
-prepareimg(struct upperlimitparams *p, float *img, float *sky, long *seg,
-           long *mask)
-{
-  float *f, *ff;
-  size_t size=p->s0*p->s1;
-
-  /* Allocate the space for the image. */
-  errno=0;
-  p->img=malloc(size * sizeof *p->img);
-  if(p->img==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for p->img (upperlimit.c)",
-          size * sizeof *p->img);
-
-  /* Set the values. */
-  ff=(f=p->img)+size;
-  do
+  /* Parse over the object and get the clump's minimum and maximum. */
+  while( pp->start_end_inc[0] + increment <= pp->start_end_inc[1] )
     {
-      /* Note that the Sky and mask are optional (can be NULL), so we are
-         checking their existance (not NULL) first.  */
-      if(mask)
+      /* Set the pointers for this tile. */
+      I = pp->st_i + increment;
+      O = pp->st_o + increment;
+      C = pp->st_c + increment;
+
+      /* Go over the contiguous region. */
+      II = I + dsize[ndim-1];
+      do
         {
-          *f = *img++ - (( *seg || *mask )  ? NAN :  ( sky ? *sky : 0 ));
-          ++mask;
+          /* Only consider clumps. */
+          if( *O==pp->object && *C>0 )
+            {
+              /* Get the coordinates of this pixel. */
+              gal_dimension_index_to_coord(I-start, ndim, dsize, coord);
+
+              /* Check to see if this coordinate is the smallest/largest
+                 found so far for this label. Note that labels start from
+                 1, while indexs here start from zero. */
+              min = &minmax[ (*C-1) * width        ];
+              max = &minmax[ (*C-1) * width + ndim ];
+              for(d=0;d<ndim;++d)
+                {
+                  if( coord[d] < min[d] ) min[d] = coord[d];
+                  if( coord[d] > max[d] ) max[d] = coord[d];
+                }
+            }
+
+          /* Increment the other pointers. */
+          ++O; ++C;
         }
-      else
-        *f = *img++ - (*seg  ? NAN :  ( sky ? *sky : 0 ));
+      while(++I<II);
 
-      /* Increment the pointers (when they exist) to the next pixel. */
-      ++seg;
-      if(sky) ++sky;
+      /* Increment to the next contiguous region. */
+      increment += ( gal_tile_block_increment(input, dsize, num_increment++,
+                                              NULL) );
     }
-  while(++f<ff);
 
-  /* For a check:
-  gal_fits_array_to_file("in.fits", "check", FLOAT_IMG, p->img,
-                        p->s0, p->s1, 0, NULL, NULL, "myprog");
-  exit(0);
+  /* For a check.
+  for(i=0;i<pp->clumpsinobj;++i)
+    printf("%zu: (%zu, %zu) --> (%zu, %zu)\n", i+1, minmax[i*width],
+           minmax[i*width+1], minmax[i*width+2], minmax[i*width+3]);
   */
+
+  /* Make the tiles. */
+  tiles=gal_tile_series_from_minmax(input, minmax, pp->clumpsinobj);
+
+  /* Cleanup and return. */
+  free(coord);
+  free(minmax);
+  return tiles;
 }
 
 
@@ -330,183 +141,133 @@ prepareimg(struct upperlimitparams *p, float *img, 
float *sky, long *seg,
 
 
 /*********************************************************************/
-/************       Actual operation on threads       ****************/
+/*******************         For one tile         ********************/
 /*********************************************************************/
-static void *
-upperlimit_on_thread(void *inparam)
+static double
+upperlimit_one_tile(struct mkcatalog_passparams *pp, gal_data_t *tile,
+                    unsigned long seed, int32_t clumplab)
 {
-  /* The first thing to do is to say what the input pointer actually is. */
-  struct tupperlimitparams *tp = (struct tupperlimitparams *)inparam;
-  struct upperlimitparams *p = tp->p;
-
-  /* Now you can go onto do defining the function like any other
-     function: first you define the variables and so on... */
-  double *sum;
-  gsl_rng *rng;
-  unsigned char *u, *uu;
-  float *l, ave, med, *fsum;
-  size_t i, j, c, xw, yw, lab, xmin, ymin;
-
-  /* Allcate space to keep the total sums */
-  errno=0;
-  sum=malloc(p->upnum*sizeof *sum);
-  if(sum==NULL)
-    error(EXIT_FAILURE, errno, "%zu bytes for sum (upperlimit.c)",
-          p->upnum*sizeof *sum);
-
-  /* Allocate the random number generator for this thread. Note that when
-     envseed is non-zero, then we need to use the given environment
-     value. However, we cannot use the same value for all threads because
-     all the positions of all threads will be the same, so we add the
-     thread-id (which is also a known paramter) to the seed. A given number
-     of segments will be distributed between a given number of threads in a
-     reproducible manner, so with this seed value for this thread, we will
-     get reproducible results.*/
-  rng=gsl_rng_alloc(gsl_rng_default);
-  gsl_rng_set(rng, gsl_rng_default_seed+tp->id);
-
-
-  /* Go over the jobs indexed for this thread: */
-  for(i=0; tp->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  struct mkcatalogparams *p=pp->p;
+  size_t ndim=p->input->ndim, *dsize=p->input->dsize;
+
+  void *tarray;
+  double sum, out;
+  int continueparse;
+  gal_data_t *sigclip;
+  uint8_t *M=NULL, *st_m=NULL;
+  float *uparr=pp->up_vals->array;
+  size_t increment, num_increment;
+  float *I, *II, *SK, *st_i, *st_sky;
+  size_t d, tcounter=0, counter=0, se_inc[2];
+  int32_t *O, *oO, *st_o, *st_oo, *st_oc, *oC=NULL;
+  size_t *rcoord=gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+  size_t maxcount = p->upnum * MKCATALOG_UPPERLIMIT_STOP_MULTIP;
+
+  /* Initializations. */
+  tarray=tile->array;
+  gsl_rng_set(pp->rng, seed);
+
+
+  /* `se_inc' is just used temporarily, the important thing here is
+     `st_oo'. */
+  st_oo = ( clumplab
+            ? gal_tile_start_end_ind_inclusive(tile, p->objects, se_inc)
+            : pp->st_o );
+  st_oc = clumplab ? (int32_t *)(p->clumps->array) + se_inc[0] : NULL;
+
+
+  /* Continue measuring magnitudes randomly until we get the desired
+     number. */
+  while(tcounter<maxcount && counter<p->upnum)
     {
-      /* Setup this labels parameters */
-      c=0;
-      lab=tp->indexs[i];
-      xw = p->box[ lab*4 + XWIDCOL ];
-      yw = p->box[ lab*4 + YWIDCOL ];
-
-      /* The starting point should be placed between 0 and Image_width
-        - box_width. */
-      while(c<p->upnum)
-       {
-         /* Get a randomly placed start for this label */
-         sum[c] = 0.0f;
-         xmin = gsl_rng_uniform_int(rng, p->s0-xw);
-         ymin = gsl_rng_uniform_int(rng, p->s1-yw);
-
-         /* Go over the pixels and get the sum of the pixel values,
-            this is very similar to the loop in fillseginfo. */
-         for(j=0; j<xw; ++j)
-           {
-             uu = ( u = p->pix[lab] + yw*j ) + yw;
-             l  = p->img + (xmin+j)*p->s1 + ymin;
-
-             /* We can't simply multiply, since NaN values might be
-                involved over regions that we don't need.*/
-             do {sum[c] += *u==1.0f ? *l : 0.0f; ++l;} while(++u<uu);
-           }
-
-         /* Only use this sum if it is not a NaN. */
-          /*printf("%zu: %f\n", c, sum[c]);*/
-         if(!isnan(sum[c])) ++c;
-       }
-
-      /* Get the standard deviation by sigma-clipping */
-      gal_fits_change_type (sum, DOUBLE_IMG, c, 0, (void **)&fsum, FLOAT_IMG);
-      gal_statistics_sigma_clip_converge(fsum, 0, c, p->sclipmultip,
-                                         p->sclipaccu, &ave, &med,
-                                        &p->std[p->minlab+lab], 0);
-
-      /*
-      printf("%f\n", p->std[ lab*p->numoutcols + p->nimg ]);
-      exit(0);
-
-      p->std[ lab*p->numoutcols + p->nimg ] = gsl_stats_sd(sum, 1, c);
-      */
-    }
-
-  /* Free the random number generator space. */
-  free(sum);
-  gsl_rng_free(rng);
-
-  /* Wait until all other threads finish. When there was only one thread,
-     we explicitly set the pointer to the barrier structure to NULL, so
-     only wait when a barrier is actually defined.*/
-  if(tp->b)
-    pthread_barrier_wait(tp->b);
-
-  /* Return the NULL pointer. */
-  return NULL;
-}
-
-
-
+      /* Get the random coordinates, note that `gsl_rng_uniform_int'
+         returns an inclusive value. */
+      for(d=0;d<ndim;++d)
+        rcoord[d] = gsl_rng_uniform_int(pp->rng, dsize[d]-tile->dsize[d]-1);
+
+      /* Set the tile's new starting pointer. */
+      tile->array = gal_data_ptr_increment(p->input->array,
+                          gal_dimension_coord_to_index(ndim, dsize, rcoord),
+                                           p->input->type);
+
+      /* Starting and ending coordinates for this random position, note
+         that in `pp' we have the starting and ending coordinates of the
+         actual tile. */
+      increment     = 0;
+      num_increment = 1;
+      continueparse = 1;
+      sum           = 0.0f;
+
+      /* Starting pointers for the random tile. */
+      st_i   = gal_tile_start_end_ind_inclusive(tile, p->input, se_inc);
+      st_o               = (int32_t *)(p->objects->array) + se_inc[0];
+      st_sky             = (float   *)(p->sky->array)     + se_inc[0];
+      if(p->upmask) st_m = (uint8_t *)(p->upmask->array)  + se_inc[0];
+
+
+      /* Starting pointers for the original tile.*/
+
+      /* Parse over this object/clump. */
+      while( se_inc[0] + increment <= se_inc[1] )
+        {
+          /* Set the pointers. */
+          I                = st_i   + increment;    /* Random tile.   */
+          SK               = st_sky + increment;    /* Random tile.   */
+          O                = st_o   + increment;    /* Random tile.   */
+          if(st_m) M       = st_m   + increment;    /* Random tile.   */
+          oO               = st_oo  + increment;    /* Original tile. */
+          if(clumplab) oC  = st_oc  + increment;    /* Original tile. */
+
+
+          /* Parse over this contiguous region, similar to the first and
+             second pass functions. */
+          II = I + tile->dsize[ndim-1];
+          do
+            {
+              /* Only use pixels over this object/clump. */
+              if( *oO==pp->object
+                  && ( oC==NULL || clumplab==0 || *oC==clumplab ) )
+                {
+                  if( *O || (M && *M) || ( p->hasblank && isnan(*I) ) )
+                    continueparse=0;
+                  else
+                    sum += *I-*SK;
+                }
+
+              /* Increment the other pointers. */
+              ++SK; ++O; ++oO; if(oC) ++oC;
+            }
+          while(continueparse && ++I<II);
+
+
+          /* Increment to the next contiguous region of this tile. */
+          if(continueparse)
+            increment += ( gal_tile_block_increment(p->input, dsize,
+                                                    num_increment++, NULL) );
+          else break;
+        }
 
+      /* Further processing is only necessary if this random tile
+         actually covered the sky region. */
+      if(continueparse) uparr[ counter++ ] = sum;
 
-/* This is the thread spinner function. For each image we will divide
-   all the objects between the threads and the  */
-void
-upperlimit_manager(struct upperlimitparams *p)
-{
-  int err;
-  pthread_t t;          /* All thread ids saved in this, not used. */
-  size_t numbarriers;
-  pthread_attr_t attr;
-  pthread_barrier_t b;
-  size_t i, *indexs, thrdcols;
-  struct tupperlimitparams *tp;
-
-  /* Allocate the array of `param' structures. Note that in most cases, the
-     number of threads will not be a constant like this simple case, it
-     will be a variable passed to the thread-spinner. So we are using
-     dynamic allocation for more general use as a tutorial. */
-  errno=0;
-  tp = malloc(p->numthreads*sizeof *tp);
-  if( tp==NULL )
-    error(EXIT_FAILURE, errno, "%zu bytes for tp (upperlimit.c)",
-          p->numthreads*sizeof *tp);
-
-  /* Distribute the actions into the threads: */
-  gal_threads_dist_in_threads(p->numlabs, p->numthreads, &indexs, &thrdcols);
-
-  /* Do the job: when only one thread is necessary, there is no need to
-     spin off one thread, just call the function directly (spinning off
-     threads is expensive). This is for the generic thread spinner
-     function, not this simple function where `p->numthreads' is a
-     constant. */
-  if(p->numthreads==1)
-    {
-      tp[0].p=p;
-      tp[0].id=0;
-      tp[0].b=NULL;
-      tp[0].indexs=indexs;
-      upperlimit_on_thread(&tp[0]);
+      /* Increment the total-counter. */
+      ++tcounter;
     }
-  else
+
+  /* Calculate the standard deviation of this distribution. */
+  if(counter==p->upnum)
     {
-      /* Initialize the attributes. Note that this running thread
-         (that spinns off the nt threads) is also a thread, so the
-         number the barriers should be one more than the number of
-         threads spinned off. */
-      numbarriers = ( (p->numlabs<p->numthreads ? p->numlabs : p->numthreads)
-                      + 1 );
-      gal_threads_attr_barrier_init(&attr, &b, numbarriers);
-
-      /* Spin off the threads: */
-      for(i=0;i<p->numthreads;++i)
-        if(indexs[i*thrdcols]!=GAL_THREADS_NON_THRD_INDEX)
-          {
-            tp[i].p=p;
-            tp[i].id=i;
-            tp[i].b=&b;
-            tp[i].indexs=&indexs[i*thrdcols];
-            err=pthread_create(&t, &attr, upperlimit_on_thread, &tp[i]);
-            if(err)
-              {
-                fprintf(stderr, "can't create thread %zu", i);
-                exit(EXIT_FAILURE);
-              }
-          }
-
-      /* Wait for all threads to finish and free the spaces. */
-      pthread_barrier_wait(&b);
-      pthread_attr_destroy(&attr);
-      pthread_barrier_destroy(&b);
+      sigclip=gal_statistics_sigma_clip(pp->up_vals, p->upsigmaclip[0],
+                                        p->upsigmaclip[1], 1, 1);
+      out = ((float *)(sigclip->array))[3] * p->upnsigma;
     }
+  else out=NAN;
 
-  /* Clean up. */
-  free(tp);
-  free(indexs);
+  /* Reset the tile's array pointer, clean up and return. */
+  tile->array=tarray;
+  free(rcoord);
+  return out;
 }
 
 
@@ -529,45 +290,43 @@ upperlimit_manager(struct upperlimitparams *p)
 
 
 /*********************************************************************/
-/************             External function           ****************/
+/*******************     High level funciton      ********************/
 /*********************************************************************/
-float *
-upperlimit(float *img, float *sky, long *seg, long *mask, size_t s0,
-           size_t s1, size_t upnum, size_t numthreads, int envseed,
-           float sclipmultip, float sclipaccu)
+void
+upperlimit_calculate(struct mkcatalog_passparams *pp)
 {
   size_t i;
-  struct upperlimitparams p;
-
-  /* Put the input parameters in the structure. */
-  p.s0=s0;
-  p.s1=s1;
-  p.upnum=upnum;
-  p.envseed=envseed;
-  p.sclipaccu=sclipaccu;
-  p.numthreads=numthreads;
-  p.sclipmultip=sclipmultip;
-
-  /* Fill the information for each segment. */
-  fillseginfo(&p, seg);
-
-  /* Prepare the image. */
-  prepareimg(&p, img, sky, seg, mask);
-
-  /* Find the upper limit for all the objects on a thread. */
-  upperlimit_manager(&p);
-
-  /* For a check:
-  for(i=1;i<p.minlab+p.numlabs;++i)
-    printf("i=%zu: %-10.5f\n", i, p.std[i]);
-  exit(0);
-  */
+  double *ci;
+  unsigned long seed;
+  gal_data_t *clumptiles;
+  struct mkcatalogparams *p=pp->p;
+
+  /* First find the upper limit magnitude for this object. */
+  pp->oi[OCOL_UPPERLIMIT_B] = upperlimit_one_tile(pp, pp->tile,
+                                                  p->seed+pp->object, 0);
+
+  /* If a clumps image is present (a clump catalog is requested( and this
+     object has clumps, then find the upper limit magnitude for the clumps
+     within this object. */
+  if(p->clumps && pp->clumpsinobj)
+    {
+      /* Make tiles covering the clumps. */
+      clumptiles=upperlimit_make_clump_tiles(pp);
+
+      /* Go over all the clumps. The random number generator seed for each
+         clump/object has to be unique, but also reproducible (given the
+         intial seed and identical inputs). So we have defined it based on
+         the total number of objects and clumps and this object and clump's
+         IDs. */
+      for(i=0;i<pp->clumpsinobj;++i)
+        {
+          ci=&pp->ci[ i * CCOL_NUMCOLS ];
+          seed = p->seed + p->numobjects + p->numclumps * pp->object + i;
+          ci[CCOL_UPPERLIMIT_B] = upperlimit_one_tile(pp, &clumptiles[i],
+                                                          seed, i+1);
+        }
 
-  /* Clean up and return. */
-  for(i=0; i<p.numlabs; ++i)
-    free(p.pix[i]);
-  free(p.img);
-  free(p.pix);
-  free(p.box);
-  return p.std;
+      /* Clean up the clump tiles. */
+      gal_data_array_free(clumptiles, pp->clumpsinobj, 0);
+    }
 }
diff --git a/bin/mkcatalog/upperlimit.h b/bin/mkcatalog/upperlimit.h
index e1dff06..7ce57bf 100644
--- a/bin/mkcatalog/upperlimit.h
+++ b/bin/mkcatalog/upperlimit.h
@@ -20,14 +20,10 @@ General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
 **********************************************************************/
-#ifndef __MKCATALOG_UPPERLIMIT_H__
-#define __MKCATALOG_UPPERLIMIT_H__
+#ifndef UPPERLIMIT_H
+#define UPPERLIMIT_H
 
-
-/* Functions: */
-float *
-upperlimit(float *img, float *sky, long *seg, long *mask, size_t s0,
-           size_t s1, size_t upnum, size_t numthreads, int envseed,
-           float sclipmultip, float sclipaccu);
+void
+upperlimit_calculate(struct mkcatalog_passparams *pp);
 
 #endif
diff --git a/bin/mknoise/args.h b/bin/mknoise/args.h
index 4eccecf..ba19980 100644
--- a/bin/mknoise/args.h
+++ b/bin/mknoise/args.h
@@ -33,7 +33,7 @@ struct argp_option program_options[] =
   {
     {
       "stdadd",
-      ARGS_OPTION_KEY_STDADD,
+      UI_KEY_STDADD,
       "FLT",
       0,
       "Standard deviation addition constant.",
@@ -46,7 +46,7 @@ struct argp_option program_options[] =
     },
     {
       "background",
-      ARGS_OPTION_KEY_BACKGROUND,
+      UI_KEY_BACKGROUND,
       "FLT",
       0,
       "Fixed background magnitude for whole input.",
@@ -59,7 +59,7 @@ struct argp_option program_options[] =
     },
     {
       "zeropoint",
-      ARGS_OPTION_KEY_ZEROPOINT,
+      UI_KEY_ZEROPOINT,
       "FLT",
       0,
       "Zeropoint magnitude of input.",
@@ -76,7 +76,7 @@ struct argp_option program_options[] =
 
     {
       "envseed",
-      ARGS_OPTION_KEY_ENVSEED,
+      UI_KEY_ENVSEED,
       0,
       0,
       "Use GSL_RNG_SEED environment variable for seed",
diff --git a/bin/mknoise/main.c b/bin/mknoise/main.c
index dd2c311..73cda3e 100644
--- a/bin/mknoise/main.c
+++ b/bin/mknoise/main.c
@@ -49,7 +49,7 @@ main (int argc, char *argv[])
   mknoise(&p);
 
   /* Free all non-freed allocations. */
-  ui_free_report(&p);
+  ui_free_report(&p, &t1);
 
   /* Return successfully.*/
   return EXIT_SUCCESS;
diff --git a/bin/mknoise/ui.c b/bin/mknoise/ui.c
index f2a6134..a8429b8 100644
--- a/bin/mknoise/ui.c
+++ b/bin/mknoise/ui.c
@@ -207,17 +207,13 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in `ui_check_options_and_arguments'.
 static void
 ui_read_check_only_options(struct mknoiseparams *p)
 {
 
-  /* Check if the format of the output table is valid, given the type of
-     the output. */
-  gal_table_check_fits_format(p->cp.output, p->cp.tableformat);
-
 }
-
+*/
 
 
 
@@ -269,10 +265,18 @@ ui_preparations(struct mknoiseparams *p)
   p->input=gal_fits_img_read_to_type(p->inputname, p->cp.hdu,
                                      GAL_TYPE_FLOAT64, p->cp.minmapsize);
 
+
   /* Read the WSC structure. */
   gal_wcs_read(p->inputname, p->cp.hdu, 0, 0, &p->input->nwcs,
                &p->input->wcs);
 
+
+  /* If we are dealing with an input table, make sure the format of the
+     output table is valid, given the type of the output. */
+  if(p->input->ndim==1)
+    gal_table_check_fits_format(p->cp.output, p->cp.tableformat);
+
+
   /* Set the output name: */
   if(p->cp.output)
     gal_checkset_check_remove_file(p->cp.output, 0, p->cp.dontdelete);
@@ -280,6 +284,7 @@ ui_preparations(struct mknoiseparams *p)
     p->cp.output=gal_checkset_automatic_output(&p->cp, p->inputname,
                                                "_noised.fits");
 
+
   /* Convert the background value from magnitudes to flux. Note that
      magnitudes are actually calculated from the ratio of brightness,
      not flux. But in the context of MakeNoise where everything is
@@ -288,6 +293,7 @@ ui_preparations(struct mknoiseparams *p)
      brightness).*/
   p->background=pow(10, (p->zeropoint-p->background_mag)/2.5f);
 
+
   /* Allocate the random number generator: */
   gsl_rng_env_setup();
   p->rng=gsl_rng_alloc(gsl_rng_default);
@@ -353,9 +359,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
mknoiseparams *p)
 
 
   /* Read the options into the program's structure, and check them and
-     their relations prior to printing. */
+     their relations prior to printing.
   ui_read_check_only_options(p);
-
+  */
 
   /* Print the option values if asked. Note that this needs to be done
      after the option checks so un-sane values are not printed in the
@@ -408,11 +414,15 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
mknoiseparams *p)
 /************      Free allocated, report         *************/
 /**************************************************************/
 void
-ui_free_report(struct mknoiseparams *p)
+ui_free_report(struct mknoiseparams *p, struct timeval *t1)
 {
   /* Free the allocated arrays: */
   free(p->cp.hdu);
   free(p->rng_type);
   free(p->cp.output);
   gal_data_free(p->input);
+
+  /* Print the final message. */
+  if(!p->cp.quiet)
+    gal_timing_report(t1, PROGRAM_NAME" finished in: ", 0);
 }
diff --git a/bin/mknoise/ui.h b/bin/mknoise/ui.h
index 0e3fc40..137b237 100644
--- a/bin/mknoise/ui.h
+++ b/bin/mknoise/ui.h
@@ -35,10 +35,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_STDADD      = 's',
-  ARGS_OPTION_KEY_BACKGROUND  = 'b',
-  ARGS_OPTION_KEY_ZEROPOINT   = 'z',
-  ARGS_OPTION_KEY_ENVSEED     = 'e',
+  UI_KEY_STDADD      = 's',
+  UI_KEY_BACKGROUND  = 'b',
+  UI_KEY_ZEROPOINT   = 'z',
+  UI_KEY_ENVSEED     = 'e',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
@@ -52,6 +52,6 @@ void
 ui_read_check_inputs_setup(int argc, char *argv[], struct mknoiseparams *p);
 
 void
-ui_free_report(struct mknoiseparams *p);
+ui_free_report(struct mknoiseparams *p, struct timeval *t1);
 
 #endif
diff --git a/bin/mkprof/args.h b/bin/mkprof/args.h
index 963762a..5922ea6 100644
--- a/bin/mkprof/args.h
+++ b/bin/mkprof/args.h
@@ -32,7 +32,7 @@ struct argp_option program_options[] =
   {
     {
       "background",
-      ARGS_OPTION_KEY_BACKGROUND,
+      UI_KEY_BACKGROUND,
       "STR",
       0,
       "A background image to make the profiles on.",
@@ -45,7 +45,7 @@ struct argp_option program_options[] =
     },
     {
       "backhdu",
-      ARGS_OPTION_KEY_BACKHDU,
+      UI_KEY_BACKHDU,
       "INT/STR",
       0,
       "HDU of background image.",
@@ -58,7 +58,7 @@ struct argp_option program_options[] =
     },
     {
       "clearcanvas",
-      ARGS_OPTION_KEY_CLEARCANVAS,
+      UI_KEY_CLEARCANVAS,
       0,
       0,
       "All pixels in background image read as zero.",
@@ -76,7 +76,7 @@ struct argp_option program_options[] =
 
     {
       "naxis1",
-      ARGS_OPTION_KEY_NAXIS1,
+      UI_KEY_NAXIS1,
       "INT",
       0,
       "Number of pixels along first FITS axis.",
@@ -89,7 +89,7 @@ struct argp_option program_options[] =
     },
     {
       "naxis2",
-      ARGS_OPTION_KEY_NAXIS2,
+      UI_KEY_NAXIS2,
       "INT",
       0,
       "Number of pixels along second FITS axis.",
@@ -102,7 +102,7 @@ struct argp_option program_options[] =
     },
     {
       "oversample",
-      ARGS_OPTION_KEY_OVERSAMPLE,
+      UI_KEY_OVERSAMPLE,
       "INT",
       0,
       "Scale of oversampling (>0 and odd).",
@@ -115,7 +115,7 @@ struct argp_option program_options[] =
     },
     {
       "psfinimg",
-      ARGS_OPTION_KEY_PSFINIMG,
+      UI_KEY_PSFINIMG,
       0,
       0,
       "PSF profiles made with all in output image.",
@@ -128,7 +128,7 @@ struct argp_option program_options[] =
     },
     {
       "individual",
-      ARGS_OPTION_KEY_INDIVIDUAL,
+      UI_KEY_INDIVIDUAL,
       0,
       0,
       "Build all profiles separately.",
@@ -141,7 +141,7 @@ struct argp_option program_options[] =
     },
     {
       "nomerged",
-      ARGS_OPTION_KEY_NOMERGED,
+      UI_KEY_NOMERGED,
       0,
       0,
       "Do not create a merged image of all profiles.",
@@ -164,7 +164,7 @@ struct argp_option program_options[] =
     },
     {
       "numrandom",
-      ARGS_OPTION_KEY_NUMRANDOM,
+      UI_KEY_NUMRANDOM,
       "INT",
       0,
       "No. of random points in Monte Carlo integration.",
@@ -177,7 +177,7 @@ struct argp_option program_options[] =
     },
     {
       "tolerance",
-      ARGS_OPTION_KEY_TOLERANCE,
+      UI_KEY_TOLERANCE,
       "FLT",
       0,
       "Tolerance to switch to less accurate method.",
@@ -190,7 +190,7 @@ struct argp_option program_options[] =
     },
     {
       "tunitinp",
-      ARGS_OPTION_KEY_TUNITINP,
+      UI_KEY_TUNITINP,
       0,
       0,
       "Truncation is in units of pixels, not radius.",
@@ -203,7 +203,7 @@ struct argp_option program_options[] =
     },
     {
       "xshift",
-      ARGS_OPTION_KEY_XSHIFT,
+      UI_KEY_XSHIFT,
       "FLT",
       0,
       "Shift profile centers and enlarge image, X axis.",
@@ -216,7 +216,7 @@ struct argp_option program_options[] =
     },
     {
       "yshift",
-      ARGS_OPTION_KEY_YSHIFT,
+      UI_KEY_YSHIFT,
       "FLT",
       0,
       "Shift profile centers and enlarge image, Y axis.",
@@ -229,7 +229,7 @@ struct argp_option program_options[] =
     },
     {
       "prepforconv",
-      ARGS_OPTION_KEY_PREPFORCONV,
+      UI_KEY_PREPFORCONV,
       0,
       0,
       "Shift and expand based on first catalog PSF.",
@@ -242,7 +242,7 @@ struct argp_option program_options[] =
     },
     {
       "zeropoint",
-      ARGS_OPTION_KEY_ZEROPOINT,
+      UI_KEY_ZEROPOINT,
       "FLT",
       0,
       "Magnitude zero point.",
@@ -255,7 +255,7 @@ struct argp_option program_options[] =
     },
     {
       "circumwidth",
-      ARGS_OPTION_KEY_CIRCUMWIDTH,
+      UI_KEY_CIRCUMWIDTH,
       "FLT",
       0,
       "Width of circumference (inward) profiles",
@@ -268,7 +268,7 @@ struct argp_option program_options[] =
     },
     {
       "replace",
-      ARGS_OPTION_KEY_REPLACE,
+      UI_KEY_REPLACE,
       0,
       0,
       "Replace overlapping profile pixels, don't add.",
@@ -281,7 +281,7 @@ struct argp_option program_options[] =
     },
     {
       "magatpeak",
-      ARGS_OPTION_KEY_MAGATPEAK,
+      UI_KEY_MAGATPEAK,
       0,
       0,
       "Magnitude is for peak pixel, not full profile.",
@@ -294,7 +294,7 @@ struct argp_option program_options[] =
     },
     {
       "envseed",
-      ARGS_OPTION_KEY_ENVSEED,
+      UI_KEY_ENVSEED,
       0,
       0,
       "Use GSL_RNG_SEED environment variable for seed.",
@@ -317,7 +317,7 @@ struct argp_option program_options[] =
     },
     {
       "xcol",
-      ARGS_OPTION_KEY_XCOL,
+      UI_KEY_XCOL,
       "STR/INT",
       0,
       "Center along first FITS axis (horizontal).",
@@ -330,7 +330,7 @@ struct argp_option program_options[] =
     },
     {
       "ycol",
-      ARGS_OPTION_KEY_YCOL,
+      UI_KEY_YCOL,
       "STR/INT",
       0,
       "Center along second FITS axis (vertical).",
@@ -343,7 +343,7 @@ struct argp_option program_options[] =
     },
     {
       "racol",
-      ARGS_OPTION_KEY_RACOL,
+      UI_KEY_RACOL,
       "STR/INT",
       0,
       "Center right ascension.",
@@ -356,7 +356,7 @@ struct argp_option program_options[] =
     },
     {
       "deccol",
-      ARGS_OPTION_KEY_DECCOL,
+      UI_KEY_DECCOL,
       "STR/INT",
       0,
       "Center declination.",
@@ -369,7 +369,7 @@ struct argp_option program_options[] =
     },
     {
       "fcol",
-      ARGS_OPTION_KEY_FCOL,
+      UI_KEY_FCOL,
       "STR/INT",
       0,
       "sersic (1), moffat (2), gaussian (3), point (4), "
@@ -383,7 +383,7 @@ struct argp_option program_options[] =
     },
     {
       "rcol",
-      ARGS_OPTION_KEY_RCOL,
+      UI_KEY_RCOL,
       "STR/INT",
       0,
       "Effective radius or FWHM in pixels.",
@@ -396,7 +396,7 @@ struct argp_option program_options[] =
     },
     {
       "ncol",
-      ARGS_OPTION_KEY_NCOL,
+      UI_KEY_NCOL,
       "STR/INT",
       0,
       "Sersic index or Moffat beta.",
@@ -409,7 +409,7 @@ struct argp_option program_options[] =
     },
     {
       "pcol",
-      ARGS_OPTION_KEY_PCOL,
+      UI_KEY_PCOL,
       "STR/INT",
       0,
       "Position angle.",
@@ -422,7 +422,7 @@ struct argp_option program_options[] =
     },
     {
       "qcol",
-      ARGS_OPTION_KEY_QCOL,
+      UI_KEY_QCOL,
       "STR/INT",
       0,
       "Axis ratio.",
@@ -435,7 +435,7 @@ struct argp_option program_options[] =
     },
     {
       "mcol",
-      ARGS_OPTION_KEY_MCOL,
+      UI_KEY_MCOL,
       "STR/INT",
       0,
       "Magnitude.",
@@ -448,7 +448,7 @@ struct argp_option program_options[] =
     },
     {
       "tcol",
-      ARGS_OPTION_KEY_TCOL,
+      UI_KEY_TCOL,
       "STR/INT",
       0,
       "Truncation in units of --rcol, unless --tunitinp.",
@@ -461,7 +461,7 @@ struct argp_option program_options[] =
     },
     {
       "mforflatpix",
-      ARGS_OPTION_KEY_MFORFLATPIX,
+      UI_KEY_MFORFLATPIX,
       0,
       0,
       "mcol is flat pixel value (when fcol is 5 or 6)",
@@ -484,7 +484,7 @@ struct argp_option program_options[] =
     },
     {
       "crpix1",
-      ARGS_OPTION_KEY_CRPIX1,
+      UI_KEY_CRPIX1,
       "FLT",
       0,
       "Pixel coordinate of reference point (axis 1).",
@@ -497,7 +497,7 @@ struct argp_option program_options[] =
     },
     {
       "crpix2",
-      ARGS_OPTION_KEY_CRPIX2,
+      UI_KEY_CRPIX2,
       "FLT",
       0,
       "Pixel coordinate of reference point (axis 2).",
@@ -510,7 +510,7 @@ struct argp_option program_options[] =
     },
     {
       "crval1",
-      ARGS_OPTION_KEY_CRVAL1,
+      UI_KEY_CRVAL1,
       "FLT",
       0,
       "Right ascension at reference point (degrees).",
@@ -523,7 +523,7 @@ struct argp_option program_options[] =
     },
     {
       "crval2",
-      ARGS_OPTION_KEY_CRVAL2,
+      UI_KEY_CRVAL2,
       "FLT",
       0,
       "Declination at reference point (degrees).",
@@ -536,7 +536,7 @@ struct argp_option program_options[] =
     },
     {
       "resolution",
-      ARGS_OPTION_KEY_RESOLUTION,
+      UI_KEY_RESOLUTION,
       "FLT",
       0,
       "Resolution of image (arcseconds/pixel).",
diff --git a/bin/mkprof/main.h b/bin/mkprof/main.h
index 74f62cf..23767dc 100644
--- a/bin/mkprof/main.h
+++ b/bin/mkprof/main.h
@@ -142,7 +142,6 @@ struct mkprofparams
 
   /* Output */
   gal_data_t           *out;  /* Output image.                            */
-  int                  type;  /* User's desired output type.              */
   char              *outdir;  /* Output directory.                        */
   char            *basename;  /* Merged image name with no directory.     */
 
diff --git a/bin/mkprof/mkprof.c b/bin/mkprof/mkprof.c
index 2bebb33..cd6f011 100644
--- a/bin/mkprof/mkprof.c
+++ b/bin/mkprof/mkprof.c
@@ -226,7 +226,7 @@ mkprof_build(void *inparam)
   pthread_cond_t *qready=&p->qready;
 
   /* Make each profile that was specified for this thread. */
-  for(i=0;mkp->indexs[i]!=GAL_THREADS_NON_THRD_INDEX;++i)
+  for(i=0; mkp->indexs[i]!=GAL_BLANK_SIZE_T; ++i)
     {
       /* Create a new builtqueue element with all the information. fbq
          will be used when we want to add ibq to p->bq. It is defined
@@ -323,7 +323,7 @@ mkprof_build(void *inparam)
              this thread to add up its built profiles). So we have to
              lock the mutex to pass on this built structure to the
              builtqueue. */
-          else if (mkp->indexs[i+1]==GAL_THREADS_NON_THRD_INDEX)
+          else if (mkp->indexs[i+1]==GAL_BLANK_SIZE_T)
             {
               pthread_mutex_lock(qlock);
               fbq->next=p->bq;
@@ -375,10 +375,10 @@ mkprof_write(struct mkprofparams *p)
   struct timeval t1;
   long os=p->oversample;
   int replace=p->replace;
+  gal_data_t *out=p->out, *log;
   struct builtqueue *ibq=NULL, *tbq;
   float *to, *from, *colend, *rowend;
   size_t complete=0, num=p->num, clog;
-  gal_data_t *out=p->out, *log, *towrite;
   size_t i, j, iw, jw, ii, jj, w=p->naxes[0], ow;
 
 
@@ -512,20 +512,12 @@ mkprof_write(struct mkprofparams *p)
       /* Get the current time for verbose output. */
       if(!p->cp.quiet) gettimeofday(&t1, NULL);
 
-      /* Prepare type of output. */
-      if(out->type==p->type)
-        towrite=out;
-      else
-        {
-          towrite=gal_data_copy_to_new_type(out, p->type);
-          free(out);
-        }
-
-      /* Write the final image into a FITS file. */
-      gal_fits_img_write(towrite, p->mergedimgname, NULL, PROGRAM_STRING);
+      /* Write the final image into a FITS file with the requested type. */
+      gal_fits_img_write_to_type(out, p->mergedimgname, NULL,
+                                 PROGRAM_STRING, p->cp.type);
 
       /* Clean up */
-      gal_data_free(towrite);
+      gal_data_free(out);
 
       /* In verbose mode, print the information. */
       if(!p->cp.quiet)
@@ -623,7 +615,7 @@ mkprof(struct mkprofparams *p)
 
       /* Spin off the threads: */
       for(i=0;i<nt;++i)
-        if(indexs[i*thrdcols]!=GAL_THREADS_NON_THRD_INDEX)
+        if(indexs[i*thrdcols]!=GAL_BLANK_SIZE_T)
           {
             mkp[i].p=p;
             mkp[i].b=&b;
diff --git a/bin/mkprof/ui.c b/bin/mkprof/ui.c
index 0b352d7..8dab859 100644
--- a/bin/mkprof/ui.c
+++ b/bin/mkprof/ui.c
@@ -127,7 +127,7 @@ ui_initialize_options(struct mkprofparams *p,
   cp->coptions           = gal_commonopts_options;
 
   /* Default program parameters. */
-  p->type=GAL_TYPE_FLOAT32;
+  p->cp.type=GAL_TYPE_FLOAT32;
 
 
   /* Modify the common options for this program. */
@@ -235,7 +235,7 @@ static void
 ui_read_check_only_options(struct mkprofparams *p)
 {
   /* When a no-merged image is to be created, type is necessary. */
-  if( p->type==GAL_TYPE_INVALID && p->nomerged==0)
+  if( p->cp.type==GAL_TYPE_INVALID && p->nomerged==0)
     error(EXIT_FAILURE, 0, "an output type `--type' is necessary when a "
           "merged image is to be built.");
 
@@ -510,7 +510,7 @@ ui_read_cols(struct mkprofparams *p)
       if(corrtype)
         {
           /* Make sure there are no blank values in this column. */
-          if( checkblank && gal_blank_present(corrtype) )
+          if( checkblank && gal_blank_present(corrtype, 1) )
             error(EXIT_FAILURE, 0, "%s column has blank values. "
                   "Input columns cannot contain blank values", colname);
 
diff --git a/bin/mkprof/ui.h b/bin/mkprof/ui.h
index fe954fa..3b948b2 100644
--- a/bin/mkprof/ui.h
+++ b/bin/mkprof/ui.h
@@ -37,45 +37,45 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_BACKGROUND      = 'k',
-  ARGS_OPTION_KEY_BACKHDU         = 'B',
-  ARGS_OPTION_KEY_NAXIS1          = 'x',
-  ARGS_OPTION_KEY_NAXIS2          = 'y',
-  ARGS_OPTION_KEY_CLEARCANVAS     = 'C',
-  ARGS_OPTION_KEY_OVERSAMPLE      = 's',
-  ARGS_OPTION_KEY_INDIVIDUAL      = 'i',
-  ARGS_OPTION_KEY_NOMERGED        = 'm',
-  ARGS_OPTION_KEY_NUMRANDOM       = 'r',
-  ARGS_OPTION_KEY_TOLERANCE       = 't',
-  ARGS_OPTION_KEY_TUNITINP        = 'p',
-  ARGS_OPTION_KEY_XSHIFT          = 'X',
-  ARGS_OPTION_KEY_YSHIFT          = 'Y',
-  ARGS_OPTION_KEY_PREPFORCONV     = 'c',
-  ARGS_OPTION_KEY_ZEROPOINT       = 'z',
-  ARGS_OPTION_KEY_CIRCUMWIDTH     = 'w',
-  ARGS_OPTION_KEY_REPLACE         = 'R',
-  ARGS_OPTION_KEY_ENVSEED         = 'e',
-  ARGS_OPTION_KEY_MFORFLATPIX     = 'f',
+  UI_KEY_BACKGROUND      = 'k',
+  UI_KEY_BACKHDU         = 'B',
+  UI_KEY_NAXIS1          = 'x',
+  UI_KEY_NAXIS2          = 'y',
+  UI_KEY_CLEARCANVAS     = 'C',
+  UI_KEY_OVERSAMPLE      = 's',
+  UI_KEY_INDIVIDUAL      = 'i',
+  UI_KEY_NOMERGED        = 'm',
+  UI_KEY_NUMRANDOM       = 'r',
+  UI_KEY_TOLERANCE       = 't',
+  UI_KEY_TUNITINP        = 'p',
+  UI_KEY_XSHIFT          = 'X',
+  UI_KEY_YSHIFT          = 'Y',
+  UI_KEY_PREPFORCONV     = 'c',
+  UI_KEY_ZEROPOINT       = 'z',
+  UI_KEY_CIRCUMWIDTH     = 'w',
+  UI_KEY_REPLACE         = 'R',
+  UI_KEY_ENVSEED         = 'e',
+  UI_KEY_MFORFLATPIX     = 'f',
 
   /* Only with long version. */
-  ARGS_OPTION_KEY_PSFINIMG        = 1000,
-  ARGS_OPTION_KEY_MAGATPEAK,
-  ARGS_OPTION_KEY_XCOL,
-  ARGS_OPTION_KEY_YCOL,
-  ARGS_OPTION_KEY_RACOL,
-  ARGS_OPTION_KEY_DECCOL,
-  ARGS_OPTION_KEY_FCOL,
-  ARGS_OPTION_KEY_RCOL,
-  ARGS_OPTION_KEY_NCOL,
-  ARGS_OPTION_KEY_PCOL,
-  ARGS_OPTION_KEY_QCOL,
-  ARGS_OPTION_KEY_MCOL,
-  ARGS_OPTION_KEY_TCOL,
-  ARGS_OPTION_KEY_CRPIX1,
-  ARGS_OPTION_KEY_CRPIX2,
-  ARGS_OPTION_KEY_CRVAL1,
-  ARGS_OPTION_KEY_CRVAL2,
-  ARGS_OPTION_KEY_RESOLUTION,
+  UI_KEY_PSFINIMG        = 1000,
+  UI_KEY_MAGATPEAK,
+  UI_KEY_XCOL,
+  UI_KEY_YCOL,
+  UI_KEY_RACOL,
+  UI_KEY_DECCOL,
+  UI_KEY_FCOL,
+  UI_KEY_RCOL,
+  UI_KEY_NCOL,
+  UI_KEY_PCOL,
+  UI_KEY_QCOL,
+  UI_KEY_MCOL,
+  UI_KEY_TCOL,
+  UI_KEY_CRPIX1,
+  UI_KEY_CRPIX2,
+  UI_KEY_CRVAL1,
+  UI_KEY_CRVAL2,
+  UI_KEY_RESOLUTION,
 };
 
 
diff --git a/bin/noisechisel/args.h b/bin/noisechisel/args.h
index b265b2c..46f4dbf 100644
--- a/bin/noisechisel/args.h
+++ b/bin/noisechisel/args.h
@@ -35,7 +35,7 @@ struct argp_option program_options[] =
     /* Input options. */
     {
       "kernel",
-      ARGS_OPTION_KEY_KERNEL,
+      UI_KEY_KERNEL,
       "STR",
       0,
       "Filename of Kernel to convolve with input",
@@ -48,7 +48,7 @@ struct argp_option program_options[] =
     },
     {
       "khdu",
-      ARGS_OPTION_KEY_KHDU,
+      UI_KEY_KHDU,
       "STR",
       0,
       "HDU containing Kernel image.",
@@ -61,7 +61,7 @@ struct argp_option program_options[] =
     },
     {
       "skysubtracted",
-      ARGS_OPTION_KEY_SKYSUBTRACTED,
+      UI_KEY_SKYSUBTRACTED,
       0,
       0,
       "Input is Sky subtracted (for error estimation).",
@@ -74,7 +74,7 @@ struct argp_option program_options[] =
     },
     {
       "minskyfrac",
-      ARGS_OPTION_KEY_MINSKYFRAC,
+      UI_KEY_MINSKYFRAC,
       "FLT",
       0,
       "Min. fraction of undetected area in tile.",
@@ -87,7 +87,7 @@ struct argp_option program_options[] =
     },
     {
       "minnumfalse",
-      ARGS_OPTION_KEY_MINNUMFALSE,
+      UI_KEY_MINNUMFALSE,
       "INT",
       0,
       "Minimum number for S/N estimation.",
@@ -103,7 +103,7 @@ struct argp_option program_options[] =
     /* Tessellation. */
     {
       "largetilesize",
-      ARGS_OPTION_KEY_LARGETILESIZE,
+      UI_KEY_LARGETILESIZE,
       "INT[,INT]",
       0,
       "Sim. to --tilesize, but for larger tiles.",
@@ -121,7 +121,7 @@ struct argp_option program_options[] =
     /* Output options. */
     {
       "onlydetection",
-      ARGS_OPTION_KEY_ONLYDETECTION,
+      UI_KEY_ONLYDETECTION,
       0,
       0,
       "Stop at the end of detection.",
@@ -134,7 +134,7 @@ struct argp_option program_options[] =
     },
     {
       "grownclumps",
-      ARGS_OPTION_KEY_GROWNCLUMPS,
+      UI_KEY_GROWNCLUMPS,
       0,
       0,
       "Save grown clumps instead of original.",
@@ -157,7 +157,7 @@ struct argp_option program_options[] =
     },
     {
       "mirrordist",
-      ARGS_OPTION_KEY_MIRRORDIST,
+      UI_KEY_MIRRORDIST,
       "FLT",
       0,
       "Max. dist. (error multip.) to find mode.",
@@ -170,7 +170,7 @@ struct argp_option program_options[] =
     },
     {
       "modmedqdiff",
-      ARGS_OPTION_KEY_MODMEDQDIFF,
+      UI_KEY_MODMEDQDIFF,
       "FLT",
       0,
       "Max. mode and median quant diff. per tile.",
@@ -183,7 +183,7 @@ struct argp_option program_options[] =
     },
     {
       "qthresh",
-      ARGS_OPTION_KEY_QTHRESH,
+      UI_KEY_QTHRESH,
       "FLT",
       0,
       "Quantile threshold on convolved image.",
@@ -196,7 +196,7 @@ struct argp_option program_options[] =
     },
     {
       "smoothwidth",
-      ARGS_OPTION_KEY_SMOOTHWIDTH,
+      UI_KEY_SMOOTHWIDTH,
       "INT",
       0,
       "Flat kernel width to smooth interpolated.",
@@ -209,7 +209,7 @@ struct argp_option program_options[] =
     },
     {
       "checkqthresh",
-      ARGS_OPTION_KEY_CHECKQTHRESH,
+      UI_KEY_CHECKQTHRESH,
       0,
       0,
       "Save quantile threshold estimation in file.",
@@ -222,7 +222,7 @@ struct argp_option program_options[] =
     },
     {
       "erode",
-      ARGS_OPTION_KEY_ERODE,
+      UI_KEY_ERODE,
       "INT",
       0,
       "Number of erosions after thresholding.",
@@ -235,7 +235,7 @@ struct argp_option program_options[] =
     },
     {
       "erodengb",
-      ARGS_OPTION_KEY_ERODENGB,
+      UI_KEY_ERODENGB,
       "INT",
       0,
       "4 or 8 connectivity in erosion.",
@@ -248,7 +248,7 @@ struct argp_option program_options[] =
     },
     {
       "noerodequant",
-      ARGS_OPTION_KEY_NOERODEQUANT,
+      UI_KEY_NOERODEQUANT,
       "FLT",
       0,
       "Quantile for no erosion.",
@@ -261,7 +261,7 @@ struct argp_option program_options[] =
     },
     {
       "opening",
-      ARGS_OPTION_KEY_OPENING,
+      UI_KEY_OPENING,
       "INT",
       0,
       "Depth of opening after erosion.",
@@ -274,7 +274,7 @@ struct argp_option program_options[] =
     },
     {
       "openingngb",
-      ARGS_OPTION_KEY_OPENINGNGB,
+      UI_KEY_OPENINGNGB,
       "INT",
       0,
       "4 or 8 connectivity in opening.",
@@ -287,7 +287,7 @@ struct argp_option program_options[] =
     },
     {
       "sigmaclip",
-      ARGS_OPTION_KEY_SIGMACLIP,
+      UI_KEY_SIGMACLIP,
       "FLT,FLT",
       0,
       "Sigma multiple and, tolerance or number.",
@@ -301,7 +301,7 @@ struct argp_option program_options[] =
     },
     {
       "checkdetsky",
-      ARGS_OPTION_KEY_CHECKDETSKY,
+      UI_KEY_CHECKDETSKY,
       0,
       0,
       "Save Sky value estimation for pseudo-dets.",
@@ -314,7 +314,7 @@ struct argp_option program_options[] =
     },
     {
       "dthresh",
-      ARGS_OPTION_KEY_DTHRESH,
+      UI_KEY_DTHRESH,
       "FLT",
       0,
       "Sigma threshold for Pseudo-detections.",
@@ -327,7 +327,7 @@ struct argp_option program_options[] =
     },
     {
       "detsnminarea",
-      ARGS_OPTION_KEY_DETSNMINAREA,
+      UI_KEY_DETSNMINAREA,
       "INT",
       0,
       "Min. pseudo-detection area for S/N dist.",
@@ -340,7 +340,7 @@ struct argp_option program_options[] =
     },
     {
       "checkdetsn",
-      ARGS_OPTION_KEY_CHECKDETSN,
+      UI_KEY_CHECKDETSN,
       0,
       0,
       "Save pseudo-detection S/N values to a file.",
@@ -353,7 +353,7 @@ struct argp_option program_options[] =
     },
     {
       "detquant",
-      ARGS_OPTION_KEY_DETQUANT,
+      UI_KEY_DETQUANT,
       "FLT",
       0,
       "Quantile in pseudo-det. to define true.",
@@ -366,7 +366,7 @@ struct argp_option program_options[] =
     },
     {
       "dilate",
-      ARGS_OPTION_KEY_DILATE,
+      UI_KEY_DILATE,
       "INT",
       0,
       "Number of times to dilate true detections.",
@@ -378,8 +378,21 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET
     },
     {
+      "cleandilated",
+      UI_KEY_CLEANDILATED,
+      0,
+      0,
+      "Remove small S/N dilated objects.",
+      ARGS_GROUP_DETECTION,
+      &p->cleandilated,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
       "checkdetection",
-      ARGS_OPTION_KEY_CHECKDETECTION,
+      UI_KEY_CHECKDETECTION,
       0,
       0,
       "Save all the detection steps to a file.",
@@ -392,7 +405,7 @@ struct argp_option program_options[] =
     },
     {
       "checksky",
-      ARGS_OPTION_KEY_CHECKSKY,
+      UI_KEY_CHECKSKY,
       0,
       0,
       "Final sky and its STD steps in a file.",
@@ -414,7 +427,7 @@ struct argp_option program_options[] =
     },
     {
       "segsnminarea",
-      ARGS_OPTION_KEY_SEGSNMINAREA,
+      UI_KEY_SEGSNMINAREA,
       "INT",
       0,
       "Minimum area of clumps for S/N estimation.",
@@ -427,7 +440,7 @@ struct argp_option program_options[] =
     },
     {
       "checkclumpsn",
-      ARGS_OPTION_KEY_CHECKCLUMPSN,
+      UI_KEY_CHECKCLUMPSN,
       0,
       0,
       "Save Sky clump S/N values into a file.",
@@ -440,7 +453,7 @@ struct argp_option program_options[] =
     },
     {
       "segquant",
-      ARGS_OPTION_KEY_SEGQUANT,
+      UI_KEY_SEGQUANT,
       "FLT",
       0,
       "S/N Quantile of true sky clumps.",
@@ -453,7 +466,7 @@ struct argp_option program_options[] =
     },
     {
       "keepmaxnearriver",
-      ARGS_OPTION_KEY_KEEPMAXNEARRIVER,
+      UI_KEY_KEEPMAXNEARRIVER,
       0,
       0,
       "Keep clumps with peak touching a river.",
@@ -466,7 +479,7 @@ struct argp_option program_options[] =
     },
     {
       "gthresh",
-      ARGS_OPTION_KEY_GTHRESH,
+      UI_KEY_GTHRESH,
       "FLT",
       0,
       "Multiple of STD to stop growing clumps.",
@@ -479,7 +492,7 @@ struct argp_option program_options[] =
     },
     {
       "minriverlength",
-      ARGS_OPTION_KEY_MINRIVERLENGTH,
+      UI_KEY_MINRIVERLENGTH,
       "INT",
       0,
       "Minimum len of useful grown clump rivers.",
@@ -492,7 +505,7 @@ struct argp_option program_options[] =
     },
     {
       "objbordersn",
-      ARGS_OPTION_KEY_OBJBORDERSN,
+      UI_KEY_OBJBORDERSN,
       "FLT",
       0,
       "Min. S/N for grown clumps as one object.",
@@ -505,7 +518,7 @@ struct argp_option program_options[] =
     },
     {
       "checksegmentation",
-      ARGS_OPTION_KEY_CHECKSEGMENTATION,
+      UI_KEY_CHECKSEGMENTATION,
       0,
       0,
       "Store segmentation steps in a file.",
@@ -520,7 +533,7 @@ struct argp_option program_options[] =
 
     {
       "continueaftercheck",
-      ARGS_OPTION_KEY_CONTINUEAFTERCHECK,
+      UI_KEY_CONTINUEAFTERCHECK,
       0,
       0,
       "Continue processing after checks.",
diff --git a/bin/noisechisel/clumps.c b/bin/noisechisel/clumps.c
index 2a5541b..613f7b5 100644
--- a/bin/noisechisel/clumps.c
+++ b/bin/noisechisel/clumps.c
@@ -852,7 +852,7 @@ clumps_make_sn_table(struct clumps_thread_params *cltprm)
   clumps_get_raw_info(cltprm);
 
 
-  /* Calculate the signal to noise for successful clumps */
+  /* Calculate the signal to noise ratio for successful clumps */
   snarr=cltprm->sn->array;
   if(cltprm->snind) indarr=cltprm->snind->array;
   for(i=1;i<tablen;++i)
@@ -863,6 +863,7 @@ clumps_make_sn_table(struct clumps_thread_params *cltprm)
       I   = row[ INFO_INFLUX ]  / row[ INFO_INAREA ];
       O   = row[ INFO_RIVFLUX ] / row[ INFO_RIVAREA ];
 
+
       /* If the inner flux is smaller than the outer flux (happens only in
          noise cases) or the area is smaller than the minimum area to
          calculate signal-to-noise, then set the S/N of this segment to
@@ -882,7 +883,7 @@ clumps_make_sn_table(struct clumps_thread_params *cltprm)
              equal to i. */
           ind = sky0_det1 ? i : counter++;
           if(cltprm->snind) indarr[ind]=i;
-          snarr[ind]=( sqrt(Ni/p->cpscorr)*(I-O)
+          snarr[ind]=( sqrt(Ni/p->cpscorr) * (I-O)
                        / sqrt( (I>0?I:-1*I) + (O>0?O:-1*O) + var ) );
         }
       else
@@ -999,7 +1000,7 @@ clumps_find_make_sn_table(void *in_prm)
 
 
   /* Go over all the tiles/detections given to this thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* IDs. */
       cltprm.id = tind  = tprm->indexs[i];
diff --git a/bin/noisechisel/detection.c b/bin/noisechisel/detection.c
index a61646a..76b8250 100644
--- a/bin/noisechisel/detection.c
+++ b/bin/noisechisel/detection.c
@@ -92,6 +92,7 @@ detection_initial(struct noisechiselparams *p)
       p->binary->name=NULL;
     }
 
+
   /* Correct the no-erode values. */
   bf=(b=p->binary->array)+p->binary->size;
   do *b = *b==THRESHOLD_NO_ERODE_VALUE ? 1 : *b; while(++b<bf);
@@ -108,6 +109,7 @@ detection_initial(struct noisechiselparams *p)
       free(msg);
     }
 
+
   /* Label the connected components. */
   p->numinitialdets=gal_binary_connected_components(p->binary, &p->olabel, 1);
   if(p->detectionname)
@@ -221,7 +223,7 @@ detection_fill_holes_open(void *in_prm)
 
 
   /* Go over all the tiles given to this thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* For easy reading. */
       tile=&p->ltl.tiles[tprm->indexs[i]];
@@ -384,10 +386,53 @@ detection_pseudo_find(struct noisechiselparams *p, 
gal_data_t *workbin,
 
 
 
+/* Write the S/N tables to a file. */
+static void
+detection_sn_write_to_file(struct noisechiselparams *p, gal_data_t *sn,
+                           gal_data_t *snind, int s0d1D2)
+{
+  char *str;
+  struct gal_linkedlist_stll *comments=NULL;
+
+  /* Comment for extension on further explanation. */
+  asprintf(&str, "See also: `%s' HDU of output with "
+           "`--checkdetection'", ( s0d1D2<2
+                                   ? "PSEUDOS-FOR-SN": "DILATED" ));
+  gal_linkedlist_add_to_stll(&comments, str, 0);
+
+
+  /* Description comment. */
+  str = ( s0d1D2
+          ? ( s0d1D2==2
+              ? "S/N of dilated detections."
+              : "Pseudo-detection S/N over initial detections." )
+          : "Pseudo-detection S/N over initial undetections.");
+  gal_linkedlist_add_to_stll(&comments, str, 1);
+
+
+  /* Set the file name. */
+  str = ( s0d1D2
+          ? ( s0d1D2==2 ? p->detsn_D_name : p->detsn_d_name )
+          : p->detsn_s_name );
+  threshold_write_sn_table(p, sn, snind, str, comments);
+  gal_linkedlist_free_stll(comments, 1);
+
+
+  /* Abort NoiseChisel if the user asked for it. */
+  if(s0d1D2==2 && !p->continueaftercheck)
+    ui_abort_after_check(p, p->detsn_s_name, p->detsn_d_name,
+                         "pseudo-detection and dilated S/N values in "
+                         "a table");
+}
+
+
+
+
+
 
 static gal_data_t *
-detection_pseudo_sn(struct noisechiselparams *p, gal_data_t *worklab,
-                    size_t num, int s0d1)
+detection_sn(struct noisechiselparams *p, gal_data_t *worklab, size_t num,
+             int s0d1D2, char *extname)
 {
   float *snarr;
   uint8_t *flag;
@@ -395,25 +440,24 @@ detection_pseudo_sn(struct noisechiselparams *p, 
gal_data_t *worklab,
   gal_data_t *sn, *snind;
   int32_t *plabend, *indarr=NULL;
   double ave, err, *xy, *brightness;
-  struct gal_linkedlist_stll *comments=NULL;
   size_t ind, ndim=p->input->ndim, xyncols=1+ndim;
   size_t i, *area, counter=0, *dsize=p->input->dsize;
   size_t *coord=gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
   float *img=p->input->array, *f=p->input->array, *ff=f+p->input->size;
-  int32_t *plab = worklab->array, *dlab = s0d1 ? NULL : p->olabel->array;
+  int32_t *plab = worklab->array, *dlab = s0d1D2 ? NULL : p->olabel->array;
 
 
   /* Sanity check. */
   if(p->input->type!=GAL_TYPE_FLOAT32)
-    error(EXIT_FAILURE, 0, "the input dataset to `detection_pseudo_sn' "
+    error(EXIT_FAILURE, 0, "the input dataset to `detection_sn' "
           "must be float32 type, it is %s",
           gal_type_to_string(p->input->type, 1));
   if(!isnan(GAL_BLANK_FLOAT32))
-    error(EXIT_FAILURE, 0, "currently `detection_pseudo_sn' only "
+    error(EXIT_FAILURE, 0, "currently `detection_sn' only "
           "recognizes a NaN value for blank floating point data types, the "
           "blank value is defined to be %f", GAL_BLANK_FLOAT32);
   if(ndim!=2)
-    error(EXIT_FAILURE, 0, "currently `detection_pseudo_sn' only "
+    error(EXIT_FAILURE, 0, "currently `detection_sn' only "
           "works on 2D datasets, your input is %zu dimensions", ndim);
 
 
@@ -423,7 +467,9 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
   area       = gal_data_calloc_array(GAL_TYPE_SIZE_T,  tablen          );
   brightness = gal_data_calloc_array(GAL_TYPE_FLOAT64, tablen          );
   xy         = gal_data_calloc_array(GAL_TYPE_FLOAT64, xyncols*tablen  );
-  flag       = s0d1==0 ? gal_data_calloc_array(GAL_TYPE_UINT8, tablen) : NULL;
+  flag       = ( s0d1D2==0
+                 ? gal_data_calloc_array(GAL_TYPE_UINT8, tablen)
+                 : NULL );
   sn         = gal_data_alloc(NULL, GAL_TYPE_FLOAT32, 1, &tablen, NULL, 1,
                               p->cp.minmapsize, "SIGNAL-TO-NOISE", "ratio",
                               NULL);
@@ -443,7 +489,7 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
           /* For Sky pseudo-detections we'll start to see if it has already
              been determined that the object lies over a detected object or
              not. If it does, then just ignore it. */
-          if(s0d1==0)
+          if(s0d1D2==0)
             {
               if( flag[*plab] ) { ++plab; ++dlab; continue; }
               else if(*dlab)    /* We are on a detection. */
@@ -451,7 +497,7 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
             }
 
           /* If we are on a blank pixel, ignore this pixel. */
-          if( isnan(*f) ) { ++plab; if(s0d1==0) ++dlab; continue; }
+          if( isnan(*f) ) { ++plab; if(s0d1D2==0) ++dlab; continue; }
 
           /* Save all the necessary values. */
           ++area[*plab];
@@ -466,7 +512,7 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
 
       /* Increment the other two labels. */
       ++plab;
-      if(s0d1==0) ++dlab;
+      if(s0d1D2==0) ++dlab;
     }
   while(++f<ff);
 
@@ -482,20 +528,22 @@ detection_pseudo_sn(struct noisechiselparams *p, 
gal_data_t *worklab,
   */
 
 
-  /* If the user wants to see the steps (on the background), remove all the
-     pseudo-detections that will not be used in the final quantile
-     calcluation. */
+  /* If the user wants to see the steps (on the background) and we are
+     working on pseudo-detections, remove those that will not be used in
+     the final quantile calculation. */
   if(p->detectionname)
     {
-      plabend = (plab=worklab->array) + worklab->size;
-      do
-        if( *plab!=GAL_BLANK_INT32
-            && ( area[*plab]<p->detsnminarea || brightness[*plab]<0) )
-          *plab=0;
-      while(++plab<plabend);
-      worklab->name="PSEUDOS-FOR-SN";
-      gal_fits_img_write(worklab, p->detectionname, NULL,
-                         PROGRAM_STRING);
+      if(s0d1D2<2)
+        {
+          plabend = (plab=worklab->array) + worklab->size;
+          do
+            if( *plab!=GAL_BLANK_INT32
+                && ( area[*plab]<p->detsnminarea || brightness[*plab]<0) )
+              *plab=0;
+          while(++plab<plabend);
+        }
+      worklab->name=extname;
+      gal_fits_img_write(worklab, p->detectionname, NULL, PROGRAM_STRING);
       worklab->name=NULL;
     }
 
@@ -503,7 +551,7 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
   /* Calculate the signal to noise for successful detections: */
   snarr=sn->array;
   if(snind) indarr=snind->array;
-  if(s0d1) { snarr[0]=NAN; if(snind) indarr[0]=GAL_BLANK_INT32; }
+  if(s0d1D2) { snarr[0]=NAN; if(snind) indarr[0]=GAL_BLANK_INT32; }
   for(i=1;i<tablen;++i)
     {
       ave=brightness[i]/area[i];
@@ -529,7 +577,7 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
              counter. But for initial detections, it is very important that
              their Signal to noise ratio be placed in the same index as
              their label. */
-          ind = s0d1 ? i : counter++;
+          ind = s0d1D2 ? i : counter++;
           if(snind) indarr[ind]=i;
           snarr[ind] = ( sqrt( (float)(area[i])/p->cpscorr )
                          * ave / sqrt(ave+err) );
@@ -537,7 +585,7 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
       else
         /* In detection pseudo-detections, order matters, so we will set
            all non-usable values to blank. */
-        if(s0d1)
+        if(s0d1D2)
           {
             snarr[i]=NAN;
             if(snind) indarr[i]=GAL_BLANK_INT32;;
@@ -546,7 +594,7 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
 
 
   /* If we are in Sky mode, the sizes have to be corrected */
-  if(s0d1==0)
+  if(s0d1D2==0)
     {
       sn->dsize[0]=sn->size=counter;
       if(snind) snind->dsize[0]=snind->size=counter;
@@ -555,24 +603,7 @@ detection_pseudo_sn(struct noisechiselparams *p, 
gal_data_t *worklab,
 
   /* If the user wanted a list of S/N values for all pseudo-detections,
      save it. */
-  if(snind)
-    {
-      /* Make the comments, then write the table. */
-      gal_linkedlist_add_to_stll(&comments, "See also: `PSEUDOS-FOR-SN' "
-                                 "HDU of output with `--checkdetection'", 1);
-      gal_linkedlist_add_to_stll(&comments, s0d1 ? "Pseudo-detection S/N "
-                                 "over initially detected regions."
-                                 : "Pseudo-detection S/N over initially "
-                                 "undetected regions.", 1);
-      threshold_write_sn_table(p, sn, snind, ( s0d1 ? p->detsn_d_name
-                                               : p->detsn_s_name ), comments);
-      gal_linkedlist_free_stll(comments, 1);
-
-      /* Abort NoiseChisel if the user asked for it. */
-      if(s0d1 && !p->continueaftercheck)
-        ui_abort_after_check(p, p->detsn_s_name, p->detsn_d_name,
-                             "pseudo-detection S/N values in a table");
-    }
+  if(snind) detection_sn_write_to_file(p, sn, snind, s0d1D2);
 
 
   /* Clean up and return. */
@@ -589,6 +620,8 @@ detection_pseudo_sn(struct noisechiselparams *p, gal_data_t 
*worklab,
 
 
 
+/* ONLY FOR PSEUDO DETECTIONS: remove pseudo-detections that have a small
+   S/N from the binary image (the labeled image will be left untouched). */
 static void
 detection_pseudo_remove_low_sn(struct noisechiselparams *p,
                                gal_data_t *workbin, gal_data_t *worklab,
@@ -609,7 +642,7 @@ detection_pseudo_remove_low_sn(struct noisechiselparams *p,
 
 
   /* Go over the pseudo-detection labels and only keep those that must be
-     kept (using the new labels) in the binary array.. */
+     kept (using the new labels) in the binary array. */
   if( p->input->flag & GAL_DATA_FLAG_HASBLANK )
     do
       *b++ = *l == GAL_BLANK_INT32 ? GAL_BLANK_UINT8 : keep[ *l ] > 0;
@@ -656,7 +689,7 @@ detection_pseudo_real(struct noisechiselparams *p)
   /* Over the Sky: find the pseudo-detections and make the S/N table. */
   if(!p->cp.quiet) gettimeofday(&t1, NULL);
   numpseudo=detection_pseudo_find(p, workbin, worklab, 0);
-  sn=detection_pseudo_sn(p, worklab, numpseudo, 0);
+  sn=detection_sn(p, worklab, numpseudo, 0, "PSEUDOS-FOR-SN");
 
 
   /* Get the S/N quantile and report it if we are in non-quiet mode. */
@@ -676,7 +709,7 @@ detection_pseudo_real(struct noisechiselparams *p)
   /* Over the detections: find pseudo-detections and make S/N table. */
   if(!p->cp.quiet) gettimeofday(&t1, NULL);
   numpseudo=detection_pseudo_find(p, workbin, worklab, 1);
-  sn=detection_pseudo_sn(p, worklab, numpseudo, 1);
+  sn=detection_sn(p, worklab, numpseudo, 1, "PSEUDOS-FOR-SN");
 
 
   /* Remove the pseudo detections with a low S/N. */
@@ -693,6 +726,91 @@ detection_pseudo_real(struct noisechiselparams *p)
 
 
 
+/* This is for the final detections (dilated) detections. */
+static size_t
+detection_final_remove_small_sn(struct noisechiselparams *p, size_t num)
+{
+  size_t i;
+  int8_t *b;
+  float *snarr;
+  gal_data_t *sn, *snind;
+  int32_t *l, *lf, curlab=1;
+  struct gal_linkedlist_stll *comments=NULL;
+  int32_t *newlabs=gal_data_calloc_array(GAL_TYPE_INT32, num+1);
+
+
+  /* Get the Signal to noise ratio of all detections. */
+  sn=detection_sn(p, p->olabel, num, 2, "DILATED");
+
+
+  /* Only keep the objects with an S/N above the pseudo-detection limit. */
+  snarr=sn->array;
+  for(i=1;i<num+1;++i)
+    newlabs[i] = snarr[i] > p->detsnthresh ? curlab++ : 0;
+
+
+  /* Go over the labeled image and correct the labels. */
+  b=p->binary->array;
+  lf=(l=p->olabel->array)+p->olabel->size;
+  if( p->input->flag & GAL_DATA_FLAG_HASBLANK )
+    {
+      do
+        {
+          if( *l != GAL_BLANK_INT32 )
+            *b = (*l=newlabs[ *l ]) > 0;
+          ++b;
+        }
+      while(++l<lf);
+    }
+  else do *b++ = (*l=newlabs[ *l ]) > 0; while(++l<lf);
+
+
+  /* Save the S/N values if the user asked for them. */
+  if(p->detsn_D_name)
+    {
+      /* Make the S/N index array. */
+      snind=gal_data_alloc(NULL, GAL_TYPE_INT32, 1, &num, NULL, 0,
+                           p->cp.minmapsize, NULL, NULL, NULL);
+
+      /* Fill in the indexs. Note that the S/N array had num+1 elements, so
+         we also want to shift them back by one element, so we also need to
+         correct its size. */
+      l=snind->array;
+      sn->size = sn->dsize[0] = num;
+      for(i=0;i<num;++i) { l[i]=i+1; snarr[i]=snarr[i+1]; }
+
+      /* Make the comments, then write the table. */
+      gal_linkedlist_add_to_stll(&comments, "See also: `DILATED' "
+                                 "HDU of output with `--checkdetection'.", 1);
+      gal_linkedlist_add_to_stll(&comments, "S/N of finally dilated "
+                                 "detections.", 1);
+
+
+      threshold_write_sn_table(p, sn, snind, p->detsn_D_name, comments);
+      gal_linkedlist_free_stll(comments, 1);
+
+    }
+
+
+  /* For a check image. */
+  if(p->detectionname)
+    {
+      p->olabel->name="DETECTION_FINAL";
+      gal_fits_img_write(p->olabel, p->detectionname, NULL,
+                         PROGRAM_STRING);
+      p->olabel->name=NULL;
+    }
+
+  /* Clean up and return. */
+  free(newlabs);
+  gal_data_free(sn);
+  return curlab-1;
+}
+
+
+
+
+
 
 
 
@@ -720,6 +838,7 @@ detection_remove_false_initial(struct noisechiselparams *p,
   int32_t *newlabels=gal_data_calloc_array(GAL_TYPE_UINT32,
                                             p->numinitialdets+1);
 
+
   /* Find the new labels for all the existing labels. Recall that
      `newlabels' was initialized to zero, so any label that is not given a
      new label here will be automatically removed. After the first pixel of
@@ -840,23 +959,42 @@ detection(struct noisechiselparams *p)
   if(p->dilate)
     {
       gal_binary_dilate(workbin, p->dilate, 8, 1);
-      p->numdetections = gal_binary_connected_components(workbin, &p->olabel,
-                                                       8);
+      num_true_initial = gal_binary_connected_components(workbin, &p->olabel,
+                                                         8);
     }
-  else p->numdetections=num_true_initial;
   if(!p->cp.quiet)
     {
       asprintf(&msg, "%zu detections after %zu dilation%s",
-              p->numdetections, p->dilate, p->dilate>1 ? "s." : ".");
-      gal_timing_report(&t0, msg, 1);
+              num_true_initial, p->dilate, p->dilate>1 ? "s." : ".");
+      gal_timing_report(&t0, msg, 2);
       free(msg);
     }
-  if(p->detectionname)
+
+
+  /* When the final (dilated or over-all object) detection's S/N is less
+     than the pseudo-detection's S/N limit, the object is false. For a real
+     detection, the actual object S/N should be higher than any of its
+     pseudo-detection because it has a much larger area (and possibly more
+     flux under it).  So when the final S/N is smaller than the minimum
+     acceptable S/N threshold, we have a false pseudo-detection. */
+  if(p->cleandilated)
+    p->numdetections = detection_final_remove_small_sn(p, num_true_initial);
+  else
     {
-      p->olabel->name="TRUE-INITIAL-DETECTIONS";
-      gal_fits_img_write(p->olabel, p->detectionname, NULL,
-                         PROGRAM_STRING);
-      p->olabel->name=NULL;
+      p->numdetections = num_true_initial;
+      if(p->detectionname)
+        {
+          p->olabel->name="DETECTION_FINAL";
+          gal_fits_img_write(p->olabel, p->detectionname, NULL,
+                             PROGRAM_STRING);
+          p->olabel->name=NULL;
+        }
+    }
+  if(!p->cp.quiet)
+    {
+      asprintf(&msg, "%zu final true detections.", p->numdetections);
+      gal_timing_report(&t0, msg, 1);
+      free(msg);
     }
 
 
@@ -868,6 +1006,12 @@ detection(struct noisechiselparams *p)
   p->binary=workbin;
 
 
+  /* The initial Sky and Sky STD values were only for detection. */
+  gal_data_free(p->sky);
+  gal_data_free(p->std);
+  p->sky = p->std = NULL;
+
+
   /* If the user wanted to check the threshold and hasn't called
      `continueaftercheck', then stop NoiseChisel. */
   if(p->detectionname && !p->continueaftercheck)
diff --git a/bin/noisechisel/main.h b/bin/noisechisel/main.h
index b22aa60..25dff0d 100644
--- a/bin/noisechisel/main.h
+++ b/bin/noisechisel/main.h
@@ -73,6 +73,7 @@ struct noisechiselparams
   uint8_t          checkdetsn;  /* Save pseudo-detection S/N values.      */
   float              detquant;  /* True detection quantile.               */
   size_t               dilate;  /* Number of times to dilate true dets.   */
+  uint8_t        cleandilated;  /* Remove dilated objects with small S/N. */
   uint8_t      checkdetection;  /* Save all detection steps to a file.    */
   uint8_t            checksky;  /* Check the Sky value estimation.        */
 
@@ -90,6 +91,7 @@ struct noisechiselparams
   char            *detskyname;  /* Name of Initial det sky check image.   */
   char          *detsn_s_name;  /* Sky pseudo-detections S/N name.        */
   char          *detsn_d_name;  /* Detection pseudo-detections S/N name.  */
+  char          *detsn_D_name;  /* Final detection S/N name.              */
   char         *detectionname;  /* Name of detection steps file.          */
   char               *skyname;  /* Name of Sky estimation steps file.     */
   char        *clumpsn_s_name;  /* Sky clump S/N name.                    */
diff --git a/bin/noisechisel/noisechisel.c b/bin/noisechisel/noisechisel.c
index b9cd4d0..f92125f 100644
--- a/bin/noisechisel/noisechisel.c
+++ b/bin/noisechisel/noisechisel.c
@@ -80,10 +80,6 @@ noisechisel_convolve(struct noisechiselparams *p)
 static void
 noisechisel_find_sky_subtract(struct noisechiselparams *p)
 {
-  /* Free the initial Sky and its STD estimates. */
-  gal_data_free(p->sky);
-  gal_data_free(p->std);
-
   /* Find the final Sky value. */
   sky_and_std(p, p->skyname);
 
@@ -199,25 +195,16 @@ noisechisel_output(struct noisechiselparams *p)
 
 
   /* Write the object labels and useful information into it's header. */
-  if(p->onlydetection)
-    {
-      p->olabel->name = "OBJECTS";
-      gal_fits_key_add_to_ll(&keys, GAL_TYPE_SIZE_T, "NDETS", 0,
-                             &p->numobjects, 0, "Total number of detections",
-                             0, "counter");
-    }
-  else
-    {
-      p->olabel->name = "OBJECTS";
-      gal_fits_key_add_to_ll(&keys, GAL_TYPE_STRING, "WCLUMPS", 0, "yes", 0,
-                             "Generate catalog with clumps?", 0, "bool");
-      gal_fits_key_add_to_ll(&keys, GAL_TYPE_SIZE_T, "NOBJS", 0,
-                             &p->numobjects, 0, "Total number of objects",
-                             0, "counter");
-    }
+  if(p->onlydetection==0)
+    gal_fits_key_add_to_ll(&keys, GAL_TYPE_STRING, "WCLUMPS", 0, "yes", 0,
+                           "Generate catalog with clumps?", 0, "bool");
+  gal_fits_key_add_to_ll(&keys, GAL_TYPE_SIZE_T, "NUMLABS", 0,
+                         &p->numobjects, 0, "Total number of labels "
+                         "(inclusive)", 0, "counter");
   gal_fits_key_add_to_ll(&keys, GAL_TYPE_FLOAT32, "DETSN", 0, &p->detsnthresh,
                          0, "Minimum S/N of true pseudo-detections", 0,
                          "ratio");
+  p->olabel->name = p->onlydetection ? "DETECTIONS" : "OBJECTS";
   gal_fits_img_write(p->olabel, p->cp.output, keys, PROGRAM_STRING);
   p->olabel->name=NULL;
   keys=NULL;
@@ -230,7 +217,7 @@ noisechisel_output(struct noisechiselparams *p)
   if(p->onlydetection==0)
     {
       p->clabel->name="CLUMPS";
-      gal_fits_key_add_to_ll(&keys, GAL_TYPE_SIZE_T, "NCLUMPS", 0,
+      gal_fits_key_add_to_ll(&keys, GAL_TYPE_SIZE_T, "NUMLABS", 0,
                              &p->numclumps, 0, "Total number of clumps", 0,
                              "counter");
       gal_fits_key_add_to_ll(&keys, GAL_TYPE_FLOAT32, "CLUMPSN", 0,
@@ -245,7 +232,8 @@ noisechisel_output(struct noisechiselparams *p)
   /* Write the Sky image into the output */
   if(p->sky->name) free(p->sky->name);
   p->sky->name="SKY";
-  gal_tile_full_values_write(p->sky, &p->cp.tl, p->cp.output, PROGRAM_STRING);
+  gal_tile_full_values_write(p->sky, &p->cp.tl, p->cp.output,
+                             NULL, PROGRAM_STRING);
   p->sky->name=NULL;
 
 
@@ -260,7 +248,8 @@ noisechisel_output(struct noisechiselparams *p)
   gal_fits_key_add_to_ll(&keys, GAL_TYPE_FLOAT32, "MEDSTD", 0,
                          &p->medstd, 0, "Median raw tile standard deviation",
                          0, p->input->unit);
-  gal_tile_full_values_write(p->std, &p->cp.tl, p->cp.output, PROGRAM_STRING);
+  gal_tile_full_values_write(p->std, &p->cp.tl, p->cp.output, keys,
+                             PROGRAM_STRING);
   p->std->name=NULL;
 }
 
diff --git a/bin/noisechisel/segmentation.c b/bin/noisechisel/segmentation.c
index 5d4968b..74b7697 100644
--- a/bin/noisechisel/segmentation.c
+++ b/bin/noisechisel/segmentation.c
@@ -304,9 +304,9 @@ segmentation_relab_overall(struct clumps_thread_params 
*cltprm)
   clprm->totobjects += cltprm->numobjects;
   clprm->totclumps  += cltprm->numtrueclumps;
 
-  /* Unlock it (if it was locked). */
+  /* Unlock the mutex (if it was locked). */
   if(clprm->p->cp.numthreads>1)
-    pthread_mutex_destroy(&clprm->labmutex);
+    pthread_mutex_unlock(&clprm->labmutex);
 
   /* Increase all the object labels by `startinglab'. */
   do olabel[*s] += startinglab; while(++s<sf);
@@ -351,7 +351,7 @@ segmentation_on_threads(void *in_prm)
   cltprm.clprm   = clprm;
 
   /* Go over all the detections given to this thread (counting from zero.) */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* Set the ID of this detection, note that for the threads, we
          counted from zero, but the IDs start from 1, so we'll add a 1 to
@@ -853,9 +853,10 @@ segmentation(struct noisechiselparams *p)
   if(!p->cp.quiet)
     {
       asprintf(&msg, "%zu object%s""containing %zu clump%sfound.",
-               p->numobjects-1, p->numobjects==2 ? " " : "s ",
-               p->numclumps-1,  p->numclumps ==2 ? " " : "s ");
+               p->numobjects, p->numobjects==1 ? " " : "s ",
+               p->numclumps,  p->numclumps ==1 ? " " : "s ");
       gal_timing_report(&t1, msg, 1);
+      free(msg);
     }
 
   /* If the user wanted to check the segmentation and hasn't called
diff --git a/bin/noisechisel/sky.c b/bin/noisechisel/sky.c
index cf97484..b90ddf8 100644
--- a/bin/noisechisel/sky.c
+++ b/bin/noisechisel/sky.c
@@ -77,7 +77,7 @@ sky_mean_std_undetected(void *in_prm)
 
 
   /* Go over all the tiles given to this thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* Basic definitions */
       numsky=0;
@@ -171,8 +171,8 @@ sky_and_std(struct noisechiselparams *p, char *checkname)
                        cp->numthreads);
   if(checkname)
     {
-      gal_tile_full_values_write(p->sky, tl, checkname, PROGRAM_STRING);
-      gal_tile_full_values_write(p->std, tl, checkname, PROGRAM_STRING);
+      gal_tile_full_values_write(p->sky, tl, checkname, NULL, PROGRAM_STRING);
+      gal_tile_full_values_write(p->std, tl, checkname, NULL, PROGRAM_STRING);
     }
 
   /* Get the basic information about the standard deviation
diff --git a/bin/noisechisel/threshold.c b/bin/noisechisel/threshold.c
index 6d38004..0ec6258 100644
--- a/bin/noisechisel/threshold.c
+++ b/bin/noisechisel/threshold.c
@@ -78,7 +78,7 @@ threshold_apply_on_thread(void *in_prm)
   float *value1=taprm->value1, *value2=taprm->value2;
 
   /* Go over all the tiles assigned to this thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* For easy reading. */
       tid=tprm->indexs[i];
@@ -193,7 +193,7 @@ threshold_write_sn_table(struct noisechiselparams *p, 
gal_data_t *insn,
 
   /* Remove all blank elements. The index and sn values must have the same
      set of blank elements, but checking on the integer array is faster. */
-  if( gal_blank_present(inind) )
+  if( gal_blank_present(inind, 1) )
     {
       ind=gal_data_copy(inind);
       sn=gal_data_copy(insn);
@@ -273,8 +273,8 @@ threshold_interp_smooth(struct noisechiselparams *p, 
gal_data_t **first,
     {
       (*first)->name="THRESH1_INTERP";
       (*second)->name="THRESH2_INTERP";
-      gal_tile_full_values_write(*first, tl, filename, PROGRAM_STRING);
-      gal_tile_full_values_write(*second, tl, filename, PROGRAM_STRING);
+      gal_tile_full_values_write(*first, tl, filename, NULL, PROGRAM_STRING);
+      gal_tile_full_values_write(*second, tl, filename, NULL, PROGRAM_STRING);
       (*first)->name = (*second)->name = NULL;
     }
 
@@ -298,8 +298,10 @@ threshold_interp_smooth(struct noisechiselparams *p, 
gal_data_t **first,
         {
           (*first)->name="THRESH1_SMOOTH";
           (*second)->name="THRESH2_SMOOTH";
-          gal_tile_full_values_write(*first, tl, filename, PROGRAM_STRING);
-          gal_tile_full_values_write(*second, tl, filename, PROGRAM_STRING);
+          gal_tile_full_values_write(*first, tl, filename, NULL,
+                                     PROGRAM_STRING);
+          gal_tile_full_values_write(*second, tl, filename, NULL,
+                                     PROGRAM_STRING);
           (*first)->name = (*second)->name = NULL;
         }
     }
@@ -360,7 +362,7 @@ qthresh_on_tile(void *in_prm)
                        NULL, NULL, NULL);
 
   /* Go over all the tiles given to this thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* Re-initialize the usage array's space (will be changed in
          `gal_data_copy_to_allocated' for each tile). */
@@ -474,22 +476,22 @@ threshold_quantile_find_apply(struct noisechiselparams *p)
 
 
   /* Find the threshold on each tile, free the temporary processing space
-     and set the blank flag. */
+     and set the blank flag on both. Since they have the same blank
+     elements, it is only necessary to check one (with the `updateflag'
+     value set to 1), then update the next. */
   qprm.p=p;
   gal_threads_spin_off(qthresh_on_tile, &qprm, tl->tottiles, cp->numthreads);
   free(qprm.usage);
-  if( gal_blank_present(qprm.erode_th) )
-    {
-      qprm.erode_th->flag   |= GAL_DATA_FLAG_HASBLANK;
-      qprm.noerode_th->flag |= GAL_DATA_FLAG_HASBLANK;
-    }
+  if( gal_blank_present(qprm.erode_th, 1) )
+    qprm.noerode_th->flag |= GAL_DATA_FLAG_HASBLANK;
+  qprm.noerode_th->flag |= GAL_DATA_FLAG_BLANK_CH;
   if(p->qthreshname)
     {
       qprm.erode_th->name="QTHRESH_ERODE";
       qprm.noerode_th->name="QTHRESH_NOERODE";
-      gal_tile_full_values_write(qprm.erode_th, tl, p->qthreshname,
+      gal_tile_full_values_write(qprm.erode_th, tl, p->qthreshname, NULL,
                                  PROGRAM_STRING);
-      gal_tile_full_values_write(qprm.noerode_th, tl, p->qthreshname,
+      gal_tile_full_values_write(qprm.noerode_th, tl, p->qthreshname, NULL,
                                  PROGRAM_STRING);
       qprm.erode_th->name = qprm.noerode_th->name = NULL;
     }
diff --git a/bin/noisechisel/ui.c b/bin/noisechisel/ui.c
index e8d2a8e..2db2aa8 100644
--- a/bin/noisechisel/ui.c
+++ b/bin/noisechisel/ui.c
@@ -355,6 +355,9 @@ ui_set_output_names(struct noisechiselparams *p)
       p->detsn_d_name=gal_checkset_automatic_output(&p->cp, basename,
                  ( p->cp.tableformat==GAL_TABLE_FORMAT_TXT
                    ? "_detsn_det.txt" : "_detsn_det.fits") );
+      p->detsn_D_name=gal_checkset_automatic_output(&p->cp, basename,
+                 ( p->cp.tableformat==GAL_TABLE_FORMAT_TXT
+                   ? "_detsn_dilated.txt" : "_detsn_dilated.fits") );
     }
 
   /* Detection steps. */
@@ -577,8 +580,7 @@ ui_preparations(struct noisechiselparams *p)
   /* Check for blank values to help later processing. AFTERWARDS, set the
      USE_ZERO flag, so the zero-bit (if the input doesn't have any blank
      value) will be meaningful. */
-  if( gal_blank_present(p->input) ) p->input->flag |= GAL_DATA_FLAG_HASBLANK;
-  p->input->flag |= GAL_DATA_FLAG_BLANK_CH;
+  gal_blank_present(p->input, 1);
 
 
   /* Read in the kernel for convolution. */
diff --git a/bin/noisechisel/ui.h b/bin/noisechisel/ui.h
index ca28276..8c0d3d6 100644
--- a/bin/noisechisel/ui.h
+++ b/bin/noisechisel/ui.h
@@ -35,46 +35,47 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_LARGETILESIZE      = 'L',
-  ARGS_OPTION_KEY_KERNEL             = 'k',
-  ARGS_OPTION_KEY_SKYSUBTRACTED      = 'E',
-  ARGS_OPTION_KEY_MINSKYFRAC         = 'B',
-  ARGS_OPTION_KEY_MIRRORDIST         = 'r',
-  ARGS_OPTION_KEY_MODMEDQDIFF        = 'Q',
-  ARGS_OPTION_KEY_QTHRESH            = 't',
-  ARGS_OPTION_KEY_ERODE              = 'e',
-  ARGS_OPTION_KEY_OPENING            = 'p',
-  ARGS_OPTION_KEY_SIGMACLIP          = 's',
-  ARGS_OPTION_KEY_DTHRESH            = 'R',
-  ARGS_OPTION_KEY_DETSNMINAREA       = 'i',
-  ARGS_OPTION_KEY_DETQUANT           = 'c',
-  ARGS_OPTION_KEY_DILATE             = 'd',
-  ARGS_OPTION_KEY_SEGSNMINAREA       = 'm',
-  ARGS_OPTION_KEY_SEGQUANT           = 'g',
-  ARGS_OPTION_KEY_KEEPMAXNEARRIVER   = 'v',
-  ARGS_OPTION_KEY_GTHRESH            = 'G',
-  ARGS_OPTION_KEY_MINRIVERLENGTH     = 'y',
-  ARGS_OPTION_KEY_OBJBORDERSN        = 'O',
-  ARGS_OPTION_KEY_CONTINUEAFTERCHECK = 'C',
+  UI_KEY_LARGETILESIZE      = 'L',
+  UI_KEY_KERNEL             = 'k',
+  UI_KEY_SKYSUBTRACTED      = 'E',
+  UI_KEY_MINSKYFRAC         = 'B',
+  UI_KEY_MIRRORDIST         = 'r',
+  UI_KEY_MODMEDQDIFF        = 'Q',
+  UI_KEY_QTHRESH            = 't',
+  UI_KEY_ERODE              = 'e',
+  UI_KEY_OPENING            = 'p',
+  UI_KEY_SIGMACLIP          = 's',
+  UI_KEY_DTHRESH            = 'R',
+  UI_KEY_DETSNMINAREA       = 'i',
+  UI_KEY_DETQUANT           = 'c',
+  UI_KEY_DILATE             = 'd',
+  UI_KEY_SEGSNMINAREA       = 'm',
+  UI_KEY_SEGQUANT           = 'g',
+  UI_KEY_KEEPMAXNEARRIVER   = 'v',
+  UI_KEY_GTHRESH            = 'G',
+  UI_KEY_MINRIVERLENGTH     = 'y',
+  UI_KEY_OBJBORDERSN        = 'O',
+  UI_KEY_CONTINUEAFTERCHECK = 'C',
 
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
-  ARGS_OPTION_KEY_KHDU               = 1000,
-  ARGS_OPTION_KEY_MINNUMFALSE,
-  ARGS_OPTION_KEY_ONLYDETECTION,
-  ARGS_OPTION_KEY_GROWNCLUMPS,
-  ARGS_OPTION_KEY_SMOOTHWIDTH,
-  ARGS_OPTION_KEY_CHECKQTHRESH,
-  ARGS_OPTION_KEY_ERODENGB,
-  ARGS_OPTION_KEY_NOERODEQUANT,
-  ARGS_OPTION_KEY_OPENINGNGB,
-  ARGS_OPTION_KEY_CHECKDETSKY,
-  ARGS_OPTION_KEY_CHECKDETSN,
-  ARGS_OPTION_KEY_CHECKDETECTION,
-  ARGS_OPTION_KEY_CHECKSKY,
-  ARGS_OPTION_KEY_CHECKCLUMPSN,
-  ARGS_OPTION_KEY_CHECKSEGMENTATION,
+  UI_KEY_KHDU               = 1000,
+  UI_KEY_MINNUMFALSE,
+  UI_KEY_ONLYDETECTION,
+  UI_KEY_GROWNCLUMPS,
+  UI_KEY_SMOOTHWIDTH,
+  UI_KEY_CHECKQTHRESH,
+  UI_KEY_ERODENGB,
+  UI_KEY_NOERODEQUANT,
+  UI_KEY_OPENINGNGB,
+  UI_KEY_CHECKDETSKY,
+  UI_KEY_CHECKDETSN,
+  UI_KEY_CLEANDILATED,
+  UI_KEY_CHECKDETECTION,
+  UI_KEY_CHECKSKY,
+  UI_KEY_CHECKCLUMPSN,
+  UI_KEY_CHECKSEGMENTATION,
 };
 
 
diff --git a/bin/statistics/args.h b/bin/statistics/args.h
index 04ae3c5..32c558c 100644
--- a/bin/statistics/args.h
+++ b/bin/statistics/args.h
@@ -33,7 +33,7 @@ struct argp_option program_options[] =
   {
     {
       "column",
-      ARGS_OPTION_KEY_COLUMN,
+      UI_KEY_COLUMN,
       "STR",
       0,
       "Column name or number if input is a table.",
@@ -46,7 +46,7 @@ struct argp_option program_options[] =
     },
     {
       "refcol",
-      ARGS_OPTION_KEY_REFCOL,
+      UI_KEY_REFCOL,
       "STR",
       0,
       "Reference column name or number.",
@@ -59,7 +59,7 @@ struct argp_option program_options[] =
     },
     {
       "greaterequal",
-      ARGS_OPTION_KEY_GREATEREQUAL,
+      UI_KEY_GREATEREQUAL,
       "FLT",
       0,
       "Only use values greater-equal than this.",
@@ -72,7 +72,7 @@ struct argp_option program_options[] =
     },
     {
       "lessthan",
-      ARGS_OPTION_KEY_LESSTHAN,
+      UI_KEY_LESSTHAN,
       "FLT",
       0,
       "Only use values less than this.",
@@ -85,7 +85,7 @@ struct argp_option program_options[] =
     },
     {
       "qrange",
-      ARGS_OPTION_KEY_QRANGE,
+      UI_KEY_QRANGE,
       "FLT[,FLT]",
       0,
       "Quantile range: one (from Q to 1-Q) or two.",
@@ -103,7 +103,7 @@ struct argp_option program_options[] =
     /* Tessellation */
     {
       "interpolate",
-      ARGS_OPTION_KEY_INTERPOLATE,
+      UI_KEY_INTERPOLATE,
       0,
       0,
       "Interpolate over blank tiles to fill them.",
@@ -125,7 +125,7 @@ struct argp_option program_options[] =
     },
     {
       "number",
-      ARGS_OPTION_KEY_NUMBER,
+      UI_KEY_NUMBER,
       0,
       0,
       "Number (non-blank).",
@@ -139,7 +139,7 @@ struct argp_option program_options[] =
     },
     {
       "minimum",
-      ARGS_OPTION_KEY_MINIMUM,
+      UI_KEY_MINIMUM,
       0,
       0,
       "Minimum.",
@@ -153,7 +153,7 @@ struct argp_option program_options[] =
     },
     {
       "maximum",
-      ARGS_OPTION_KEY_MAXIMUM,
+      UI_KEY_MAXIMUM,
       0,
       0,
       "Maximum.",
@@ -167,7 +167,7 @@ struct argp_option program_options[] =
     },
     {
       "sum",
-      ARGS_OPTION_KEY_SUM,
+      UI_KEY_SUM,
       0,
       0,
       "Sum.",
@@ -181,7 +181,7 @@ struct argp_option program_options[] =
     },
     {
       "mean",
-      ARGS_OPTION_KEY_MEAN,
+      UI_KEY_MEAN,
       0,
       0,
       "Mean.",
@@ -195,7 +195,7 @@ struct argp_option program_options[] =
     },
     {
       "std",
-      ARGS_OPTION_KEY_STD,
+      UI_KEY_STD,
       0,
       0,
       "Standad deviation.",
@@ -209,7 +209,7 @@ struct argp_option program_options[] =
     },
     {
       "median",
-      ARGS_OPTION_KEY_MEDIAN,
+      UI_KEY_MEDIAN,
       0,
       0,
       "Median.",
@@ -223,7 +223,7 @@ struct argp_option program_options[] =
     },
     {
       "quantile",
-      ARGS_OPTION_KEY_QUANTILE,
+      UI_KEY_QUANTILE,
       "FLT[,...]",
       0,
       "Quantile (multiple values acceptable).",
@@ -237,7 +237,7 @@ struct argp_option program_options[] =
     },
     {
       "quantfunc",
-      ARGS_OPTION_KEY_QUANTFUNC,
+      UI_KEY_QUANTFUNC,
       "FLT[,...]",
       0,
       "Quantile function (multiple values acceptable).",
@@ -251,7 +251,7 @@ struct argp_option program_options[] =
     },
     {
       "mode",
-      ARGS_OPTION_KEY_MODE,
+      UI_KEY_MODE,
       0,
       0,
       "Mode (Appendix C of arXiv:1505.01664).",
@@ -265,7 +265,7 @@ struct argp_option program_options[] =
     },
     {
       "modequant",
-      ARGS_OPTION_KEY_MODEQUANT,
+      UI_KEY_MODEQUANT,
       0,
       0,
       "Mode quantile (see --mode)",
@@ -279,7 +279,7 @@ struct argp_option program_options[] =
     },
     {
       "modesym",
-      ARGS_OPTION_KEY_MODESYM,
+      UI_KEY_MODESYM,
       0,
       0,
       "Mode symmetricity (see --mode).",
@@ -293,7 +293,7 @@ struct argp_option program_options[] =
     },
     {
       "modesymvalue",
-      ARGS_OPTION_KEY_MODESYMVALUE,
+      UI_KEY_MODESYMVALUE,
       0,
       0,
       "Value at mode symmetricity (see --mode).",
@@ -316,7 +316,7 @@ struct argp_option program_options[] =
     },
     {
       "asciihist",
-      ARGS_OPTION_KEY_ASCIIHIST,
+      UI_KEY_ASCIIHIST,
       0,
       0,
       "Print an ASCII histogram.",
@@ -329,7 +329,7 @@ struct argp_option program_options[] =
     },
     {
       "asciicfp",
-      ARGS_OPTION_KEY_ASCIICFP,
+      UI_KEY_ASCIICFP,
       0,
       0,
       "Print an ASCII cumulative frequency plot.",
@@ -342,7 +342,7 @@ struct argp_option program_options[] =
     },
     {
       "histogram",
-      ARGS_OPTION_KEY_HISTOGRAM,
+      UI_KEY_HISTOGRAM,
       0,
       0,
       "Save the histogram in output.",
@@ -355,7 +355,7 @@ struct argp_option program_options[] =
     },
     {
       "cumulative",
-      ARGS_OPTION_KEY_CUMULATIVE,
+      UI_KEY_CUMULATIVE,
       0,
       0,
       "Save the cumulative frequency plot in output.",
@@ -368,7 +368,7 @@ struct argp_option program_options[] =
     },
     {
       "mirror",
-      ARGS_OPTION_KEY_MIRROR,
+      UI_KEY_MIRROR,
       "FLT",
       0,
       "Save the histogram and CFP of the mirror dist.",
@@ -381,7 +381,7 @@ struct argp_option program_options[] =
     },
     {
       "ontile",
-      ARGS_OPTION_KEY_ONTILE,
+      UI_KEY_ONTILE,
       0,
       0,
       "Single values on separate tiles, not full input.",
@@ -394,7 +394,7 @@ struct argp_option program_options[] =
     },
     {
       "sky",
-      ARGS_OPTION_KEY_SKY,
+      UI_KEY_SKY,
       0,
       0,
       "Find the Sky and its STD over the tessellation.",
@@ -407,7 +407,7 @@ struct argp_option program_options[] =
     },
     {
       "sigmaclip",
-      ARGS_OPTION_KEY_SIGMACLIP,
+      UI_KEY_SIGMACLIP,
       0,
       0,
       "Overall sigma-clipping (see `--sclipparams')",
@@ -430,7 +430,7 @@ struct argp_option program_options[] =
     },
     {
       "kernel",
-      ARGS_OPTION_KEY_KERNEL,
+      UI_KEY_KERNEL,
       "STR",
       0,
       "File name of kernel to convolve input.",
@@ -443,7 +443,7 @@ struct argp_option program_options[] =
     },
     {
       "khdu",
-      ARGS_OPTION_KEY_KHDU,
+      UI_KEY_KHDU,
       "STR",
       0,
       "HDU/extension name or number of kernel.",
@@ -456,7 +456,7 @@ struct argp_option program_options[] =
     },
     {
       "mirrordist",
-      ARGS_OPTION_KEY_MIRRORDIST,
+      UI_KEY_MIRRORDIST,
       "FLT",
       0,
       "Max. distance (error multip.) to find mode.",
@@ -469,7 +469,7 @@ struct argp_option program_options[] =
     },
     {
       "modmedqdiff",
-      ARGS_OPTION_KEY_MODMEDQDIFF,
+      UI_KEY_MODMEDQDIFF,
       "FLT",
       0,
       "Max. mode and median quantile diff. per tile.",
@@ -482,7 +482,7 @@ struct argp_option program_options[] =
     },
     {
       "sclipparams",
-      ARGS_OPTION_KEY_SCLIPPARAMS,
+      UI_KEY_SCLIPPARAMS,
       "FLT,FLT",
       0,
       "Sigma clip: Multiple, and tolerance/number.",
@@ -496,7 +496,7 @@ struct argp_option program_options[] =
     },
     {
       "smoothwidth",
-      ARGS_OPTION_KEY_SMOOTHWIDTH,
+      UI_KEY_SMOOTHWIDTH,
       "INT",
       0,
       "Sky: flat kernel width to smooth interpolated.",
@@ -509,7 +509,7 @@ struct argp_option program_options[] =
     },
     {
       "checksky",
-      ARGS_OPTION_KEY_CHECKSKY,
+      UI_KEY_CHECKSKY,
       0,
       0,
       "Store steps in `_sky_steps.fits' file.",
@@ -531,7 +531,7 @@ struct argp_option program_options[] =
     },
     {
       "numbins",
-      ARGS_OPTION_KEY_NUMBINS,
+      UI_KEY_NUMBINS,
       "INT",
       0,
       "No. of bins in histogram or CFP tables.",
@@ -544,7 +544,7 @@ struct argp_option program_options[] =
     },
     {
       "numasciibins",
-      ARGS_OPTION_KEY_NUMASCIIBINS,
+      UI_KEY_NUMASCIIBINS,
       "INT",
       0,
       "No. of bins in ASCII histogram or CFP plots.",
@@ -557,7 +557,7 @@ struct argp_option program_options[] =
     },
     {
       "asciiheight",
-      ARGS_OPTION_KEY_ASCIIHEIGHT,
+      UI_KEY_ASCIIHEIGHT,
       "INT",
       0,
       "Height of ASCII histogram or CFP plots.",
@@ -570,7 +570,7 @@ struct argp_option program_options[] =
     },
     {
       "normalize",
-      ARGS_OPTION_KEY_NORMALIZE,
+      UI_KEY_NORMALIZE,
       0,
       0,
       "Set sum of all bins to 1.",
@@ -583,7 +583,7 @@ struct argp_option program_options[] =
     },
     {
       "maxbinone",
-      ARGS_OPTION_KEY_MAXBINONE,
+      UI_KEY_MAXBINONE,
       0,
       0,
       "Scale such that the maximum bin has value of one.",
@@ -596,7 +596,7 @@ struct argp_option program_options[] =
     },
     {
       "onebinstart",
-      ARGS_OPTION_KEY_ONEBINSTART,
+      UI_KEY_ONEBINSTART,
       "FLT",
       0,
       "Shift bins so one bin starts on this value.",
diff --git a/bin/statistics/sky.c b/bin/statistics/sky.c
index f4baed7..a2fc2c9 100644
--- a/bin/statistics/sky.c
+++ b/bin/statistics/sky.c
@@ -61,7 +61,7 @@ sky_on_thread(void *in_prm)
 
   /* Find the Sky and its standard deviation on the tiles given to this
      thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* Set the tile and copy its values into the array we'll be using. */
       tind = tprm->indexs[i];
@@ -193,9 +193,9 @@ sky(struct statisticsparams *p)
     }
   if(p->checksky)
     {
-      gal_tile_full_values_write(p->sky_t, tl, p->checkskyname,
+      gal_tile_full_values_write(p->sky_t, tl, p->checkskyname, NULL,
                                  PROGRAM_STRING);
-      gal_tile_full_values_write(p->std_t, tl, p->checkskyname,
+      gal_tile_full_values_write(p->std_t, tl, p->checkskyname, NULL,
                                  PROGRAM_STRING);
     }
 
@@ -214,9 +214,9 @@ sky(struct statisticsparams *p)
     gal_timing_report(&t1, "All blank tiles filled (interplated).", 1);
   if(p->checksky)
     {
-      gal_tile_full_values_write(p->sky_t, tl, p->checkskyname,
+      gal_tile_full_values_write(p->sky_t, tl, p->checkskyname, NULL,
                                  PROGRAM_STRING);
-      gal_tile_full_values_write(p->std_t, tl, p->checkskyname,
+      gal_tile_full_values_write(p->std_t, tl, p->checkskyname, NULL,
                                  PROGRAM_STRING);
     }
 
@@ -238,9 +238,9 @@ sky(struct statisticsparams *p)
                           1);
       if(p->checksky)
         {
-          gal_tile_full_values_write(p->sky_t, tl, p->checkskyname,
+          gal_tile_full_values_write(p->sky_t, tl, p->checkskyname, NULL,
                                      PROGRAM_STRING);
-          gal_tile_full_values_write(p->std_t, tl, p->checkskyname,
+          gal_tile_full_values_write(p->std_t, tl, p->checkskyname, NULL,
                                      PROGRAM_STRING);
         }
     }
@@ -251,8 +251,8 @@ sky(struct statisticsparams *p)
                                         ( p->cp.output
                                           ? p->cp.output
                                           : p->inputname ), "_sky.fits");
-  gal_tile_full_values_write(p->sky_t, tl, outname, PROGRAM_STRING);
-  gal_tile_full_values_write(p->std_t, tl, outname, PROGRAM_STRING);
+  gal_tile_full_values_write(p->sky_t, tl, outname, NULL, PROGRAM_STRING);
+  gal_tile_full_values_write(p->std_t, tl, outname, NULL, PROGRAM_STRING);
   if(!cp->quiet)
     printf("  - Written to `%s'.\n", outname);
 
diff --git a/bin/statistics/statistics.c b/bin/statistics/statistics.c
index 8022a44..15bc752 100644
--- a/bin/statistics/statistics.c
+++ b/bin/statistics/statistics.c
@@ -107,24 +107,24 @@ statistics_print_one_row(struct statisticsparams *p)
       /* Calculate respective values. Checking with `if(num==NULL)' gives
          compiler warnings of `this if clause does not guard ...'. So we
          are using this empty-if and else statement. */
-      case ARGS_OPTION_KEY_NUMBER:
+      case UI_KEY_NUMBER:
         num = num ? num : gal_statistics_number(p->input);           break;
-      case ARGS_OPTION_KEY_MINIMUM:
+      case UI_KEY_MINIMUM:
         min = min ? min : gal_statistics_minimum(p->input);          break;
-      case ARGS_OPTION_KEY_MAXIMUM:
+      case UI_KEY_MAXIMUM:
         max = max ? max : gal_statistics_maximum(p->input);          break;
-      case ARGS_OPTION_KEY_SUM:
+      case UI_KEY_SUM:
         sum = sum ? sum : gal_statistics_sum(p->input);              break;
-      case ARGS_OPTION_KEY_MEDIAN:
+      case UI_KEY_MEDIAN:
         med = med ? med : gal_statistics_median(p->sorted, 0); break;
-      case ARGS_OPTION_KEY_MEAN:
-      case ARGS_OPTION_KEY_STD:
+      case UI_KEY_MEAN:
+      case UI_KEY_STD:
         meanstd = meanstd ? meanstd : gal_statistics_mean_std(p->input);
         break;
-      case ARGS_OPTION_KEY_MODE:
-      case ARGS_OPTION_KEY_MODEQUANT:
-      case ARGS_OPTION_KEY_MODESYM:
-      case ARGS_OPTION_KEY_MODESYMVALUE:
+      case UI_KEY_MODE:
+      case UI_KEY_MODEQUANT:
+      case UI_KEY_MODESYM:
+      case UI_KEY_MODESYMVALUE:
         modearr = ( modearr ? modearr
                     : gal_statistics_mode(p->sorted, p->mirrordist, 0) );
         d=modearr->array;
@@ -132,8 +132,8 @@ statistics_print_one_row(struct statisticsparams *p)
         break;
 
       /* Will be calculated as printed. */
-      case ARGS_OPTION_KEY_QUANTILE:
-      case ARGS_OPTION_KEY_QUANTFUNC:
+      case UI_KEY_QUANTILE:
+      case UI_KEY_QUANTFUNC:
         break;
 
       /* The option isn't recognized. */
@@ -154,31 +154,31 @@ statistics_print_one_row(struct statisticsparams *p)
       switch(tmp->v)
         {
         /* Previously calculated values. */
-        case ARGS_OPTION_KEY_NUMBER:     out=num;                  break;
-        case ARGS_OPTION_KEY_MINIMUM:    out=min;                  break;
-        case ARGS_OPTION_KEY_MAXIMUM:    out=max;                  break;
-        case ARGS_OPTION_KEY_SUM:        out=sum;                  break;
-        case ARGS_OPTION_KEY_MEDIAN:     out=med;                  break;
-        case ARGS_OPTION_KEY_MEAN:
+        case UI_KEY_NUMBER:     out=num;                  break;
+        case UI_KEY_MINIMUM:    out=min;                  break;
+        case UI_KEY_MAXIMUM:    out=max;                  break;
+        case UI_KEY_SUM:        out=sum;                  break;
+        case UI_KEY_MEDIAN:     out=med;                  break;
+        case UI_KEY_MEAN:
           out=statistics_pull_out_element(meanstd, 0); mustfree=1; break;
-        case ARGS_OPTION_KEY_STD:
+        case UI_KEY_STD:
           out=statistics_pull_out_element(meanstd, 1); mustfree=1; break;
-        case ARGS_OPTION_KEY_MODE:
+        case UI_KEY_MODE:
           out=statistics_pull_out_element(modearr, 0); mustfree=1; break;
-        case ARGS_OPTION_KEY_MODEQUANT:
+        case UI_KEY_MODEQUANT:
           out=statistics_pull_out_element(modearr, 1); mustfree=1; break;
-        case ARGS_OPTION_KEY_MODESYM:
+        case UI_KEY_MODESYM:
           out=statistics_pull_out_element(modearr, 2); mustfree=1; break;
-        case ARGS_OPTION_KEY_MODESYMVALUE:
+        case UI_KEY_MODESYMVALUE:
           out=statistics_pull_out_element(modearr, 3); mustfree=1; break;
 
         /* Not previously calculated. */
-        case ARGS_OPTION_KEY_QUANTILE:
+        case UI_KEY_QUANTILE:
           arg = statistics_read_check_args(p);
           out = gal_statistics_quantile(p->sorted, arg, 0);
           break;
 
-        case ARGS_OPTION_KEY_QUANTFUNC:
+        case UI_KEY_QUANTFUNC:
           arg = statistics_read_check_args(p);
           tmpv = gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &dsize,
                                 NULL, 1, -1, NULL, NULL, NULL);
@@ -243,7 +243,7 @@ statistics_interpolate_and_write(struct statisticsparams *p,
 
   /* Do the interpolation (if necessary). */
   if( p->interpolate
-      && !(p->cp.interponlyblank && gal_blank_present(values)==0) )
+      && !(p->cp.interponlyblank && gal_blank_present(values, 1)==0) )
     {
       interpd=gal_interpolate_close_neighbors(values, &cp->tl,
                                               cp->interpnumngb,
@@ -254,7 +254,7 @@ statistics_interpolate_and_write(struct statisticsparams *p,
     }
 
   /* Write the values. */
-  gal_tile_full_values_write(values, &cp->tl, output, PROGRAM_STRING);
+  gal_tile_full_values_write(values, &cp->tl, output, NULL, PROGRAM_STRING);
 }
 
 
@@ -283,23 +283,23 @@ statistics_on_tile(struct statisticsparams *p)
       /* Set the type of the output array. */
       switch(operation->v)
         {
-        case ARGS_OPTION_KEY_NUMBER:
+        case UI_KEY_NUMBER:
           type=GAL_TYPE_INT32; break;
 
-        case ARGS_OPTION_KEY_MINIMUM:
-        case ARGS_OPTION_KEY_MAXIMUM:
-        case ARGS_OPTION_KEY_MEDIAN:
-        case ARGS_OPTION_KEY_MODE:
-        case ARGS_OPTION_KEY_QUANTFUNC:
+        case UI_KEY_MINIMUM:
+        case UI_KEY_MAXIMUM:
+        case UI_KEY_MEDIAN:
+        case UI_KEY_MODE:
+        case UI_KEY_QUANTFUNC:
           type=p->input->type; break;
 
-        case ARGS_OPTION_KEY_SUM:
-        case ARGS_OPTION_KEY_MEAN:
-        case ARGS_OPTION_KEY_STD:
-        case ARGS_OPTION_KEY_QUANTILE:
-        case ARGS_OPTION_KEY_MODEQUANT:
-        case ARGS_OPTION_KEY_MODESYM:
-        case ARGS_OPTION_KEY_MODESYMVALUE:
+        case UI_KEY_SUM:
+        case UI_KEY_MEAN:
+        case UI_KEY_STD:
+        case UI_KEY_QUANTILE:
+        case UI_KEY_MODEQUANT:
+        case UI_KEY_MODESYM:
+        case UI_KEY_MODESYMVALUE:
           type=GAL_TYPE_FLOAT64; break;
 
         default:
@@ -315,10 +315,10 @@ statistics_on_tile(struct statisticsparams *p)
          here, because below, the functions are repeated on each tile. */
       switch(operation->v)
         {
-        case ARGS_OPTION_KEY_QUANTILE:
+        case UI_KEY_QUANTILE:
           arg = statistics_read_check_args(p);
           break;
-        case ARGS_OPTION_KEY_QUANTFUNC:
+        case UI_KEY_QUANTFUNC:
           arg = statistics_read_check_args(p);
           tmpv = gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &dsize,
                                 NULL, 1, -1, NULL, NULL, NULL);
@@ -333,43 +333,43 @@ statistics_on_tile(struct statisticsparams *p)
           /* Do the proper operation. */
           switch(operation->v)
             {
-            case ARGS_OPTION_KEY_NUMBER:
+            case UI_KEY_NUMBER:
               tmp=gal_statistics_number(tile);                      break;
 
-            case ARGS_OPTION_KEY_MINIMUM:
+            case UI_KEY_MINIMUM:
               tmp=gal_statistics_minimum(tile);                     break;
 
-            case ARGS_OPTION_KEY_MAXIMUM:
+            case UI_KEY_MAXIMUM:
               tmp=gal_statistics_maximum(tile);                     break;
 
-            case ARGS_OPTION_KEY_MEDIAN:
+            case UI_KEY_MEDIAN:
               tmp=gal_statistics_median(tile, 1);                   break;
 
-            case ARGS_OPTION_KEY_QUANTFUNC:
+            case UI_KEY_QUANTFUNC:
               tmp=gal_statistics_quantile_function(tile, tmpv, 1);  break;
 
-            case ARGS_OPTION_KEY_SUM:
+            case UI_KEY_SUM:
               tmp=gal_statistics_sum(tile);                         break;
 
-            case ARGS_OPTION_KEY_MEAN:
+            case UI_KEY_MEAN:
               tmp=gal_statistics_mean(tile);                        break;
 
-            case ARGS_OPTION_KEY_STD:
+            case UI_KEY_STD:
               tmp=gal_statistics_std(tile);                         break;
 
-            case ARGS_OPTION_KEY_QUANTILE:
+            case UI_KEY_QUANTILE:
               tmp=gal_statistics_quantile(tile, arg, 1);            break;
 
-            case ARGS_OPTION_KEY_MODE:
-            case ARGS_OPTION_KEY_MODESYM:
-            case ARGS_OPTION_KEY_MODEQUANT:
-            case ARGS_OPTION_KEY_MODESYMVALUE:
+            case UI_KEY_MODE:
+            case UI_KEY_MODESYM:
+            case UI_KEY_MODEQUANT:
+            case UI_KEY_MODESYMVALUE:
               switch(operation->v)
                 {
-                case ARGS_OPTION_KEY_MODE:         mind=0;  break;
-                case ARGS_OPTION_KEY_MODESYM:      mind=2;  break;
-                case ARGS_OPTION_KEY_MODEQUANT:    mind=1;  break;
-                case ARGS_OPTION_KEY_MODESYMVALUE: mind=3;  break;
+                case UI_KEY_MODE:         mind=0;  break;
+                case UI_KEY_MODESYM:      mind=2;  break;
+                case UI_KEY_MODEQUANT:    mind=1;  break;
+                case UI_KEY_MODESYMVALUE: mind=3;  break;
                 }
               tmp=gal_statistics_mode(tile, p->mirrordist, 1);
               ttmp=statistics_pull_out_element(tmp, mind);
@@ -397,7 +397,7 @@ statistics_on_tile(struct statisticsparams *p)
 
       /* Clean up. */
       gal_data_free(values);
-      if(operation->v==ARGS_OPTION_KEY_QUANTFUNC) gal_data_free(tmpv);
+      if(operation->v==UI_KEY_QUANTFUNC) gal_data_free(tmpv);
     }
 
   /* Clean up. */
diff --git a/bin/statistics/ui.c b/bin/statistics/ui.c
index 46d6607..ce414ef 100644
--- a/bin/statistics/ui.c
+++ b/bin/statistics/ui.c
@@ -259,14 +259,14 @@ ui_add_to_single_value(struct argp_option *option, char 
*arg,
       /* Do the appropriate operations with the  */
       switch(option->key)
         {
-        case ARGS_OPTION_KEY_QUANTILE:
-        case ARGS_OPTION_KEY_QUANTFUNC:
+        case UI_KEY_QUANTILE:
+        case UI_KEY_QUANTFUNC:
           /* For the quantile and the quantile function, its possible to
              give any number of arguments, so add the operation index and
              the argument once for each given number. */
           for(i=0;i<inputs->size;++i)
             {
-              if(option->key==ARGS_OPTION_KEY_QUANTILE && (d[i]<0 || d[i]>1) )
+              if(option->key==UI_KEY_QUANTILE && (d[i]<0 || d[i]>1) )
                 error_at_line(EXIT_FAILURE, 0, filename, lineno, "values "
                               "to `--quantile' (`-u') must be between 0 "
                               "and 1, you had asked for %g (read from `%s')",
@@ -449,10 +449,10 @@ ui_read_check_only_options(struct statisticsparams *p)
   for(tmp=p->singlevalue; tmp!=NULL; tmp=tmp->next)
     switch(tmp->v)
       {
-      case ARGS_OPTION_KEY_MODE:
-      case ARGS_OPTION_KEY_MODESYM:
-      case ARGS_OPTION_KEY_MODEQUANT:
-      case ARGS_OPTION_KEY_MODESYMVALUE:
+      case UI_KEY_MODE:
+      case UI_KEY_MODESYM:
+      case UI_KEY_MODEQUANT:
+      case UI_KEY_MODESYMVALUE:
         if( isnan(p->mirrordist) )
           error(EXIT_FAILURE, 0, "`--mirrordist' is required for the "
                 "mode-related single measurements (`--mode', `--modequant', "
@@ -678,10 +678,10 @@ ui_make_sorted_if_necessary(struct statisticsparams *p)
   for(tmp=p->singlevalue; tmp!=NULL; tmp=tmp->next)
     switch(tmp->v)
       {
-      case ARGS_OPTION_KEY_MODE:
-      case ARGS_OPTION_KEY_MEDIAN:
-      case ARGS_OPTION_KEY_QUANTILE:
-      case ARGS_OPTION_KEY_QUANTFUNC:
+      case UI_KEY_MODE:
+      case UI_KEY_MEDIAN:
+      case UI_KEY_QUANTILE:
+      case UI_KEY_QUANTFUNC:
         is_necessary=1;
         break;
       }
diff --git a/bin/statistics/ui.h b/bin/statistics/ui.h
index 6d50231..d81efa4 100644
--- a/bin/statistics/ui.h
+++ b/bin/statistics/ui.h
@@ -35,50 +35,50 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_COLUMN       = 'c',
-  ARGS_OPTION_KEY_REFCOL       = 'r',
-  ARGS_OPTION_KEY_GREATEREQUAL = 'g',
-  ARGS_OPTION_KEY_LESSTHAN     = 'l',
-  ARGS_OPTION_KEY_QRANGE       = 'Q',
-  ARGS_OPTION_KEY_MEAN         = 'm',
-  ARGS_OPTION_KEY_STD          = 'd',
-  ARGS_OPTION_KEY_MEDIAN       = 'E',
-  ARGS_OPTION_KEY_MODE         = 'O',
-  ARGS_OPTION_KEY_QUANTILE     = 'u',
-  ARGS_OPTION_KEY_ASCIIHIST    = 'A',
-  ARGS_OPTION_KEY_HISTOGRAM    = 'H',
-  ARGS_OPTION_KEY_CUMULATIVE   = 'C',
-  ARGS_OPTION_KEY_SIGMACLIP    = 's',
-  ARGS_OPTION_KEY_NORMALIZE    = 'n',
-  ARGS_OPTION_KEY_ONTILE       = 't',
-  ARGS_OPTION_KEY_INTERPOLATE  = 'i',
-  ARGS_OPTION_KEY_SKY          = 'y',
-  ARGS_OPTION_KEY_KERNEL       = 'k',
+  UI_KEY_COLUMN       = 'c',
+  UI_KEY_REFCOL       = 'r',
+  UI_KEY_GREATEREQUAL = 'g',
+  UI_KEY_LESSTHAN     = 'l',
+  UI_KEY_QRANGE       = 'Q',
+  UI_KEY_MEAN         = 'm',
+  UI_KEY_STD          = 'd',
+  UI_KEY_MEDIAN       = 'E',
+  UI_KEY_MODE         = 'O',
+  UI_KEY_QUANTILE     = 'u',
+  UI_KEY_ASCIIHIST    = 'A',
+  UI_KEY_HISTOGRAM    = 'H',
+  UI_KEY_CUMULATIVE   = 'C',
+  UI_KEY_SIGMACLIP    = 's',
+  UI_KEY_NORMALIZE    = 'n',
+  UI_KEY_ONTILE       = 't',
+  UI_KEY_INTERPOLATE  = 'i',
+  UI_KEY_SKY          = 'y',
+  UI_KEY_KERNEL       = 'k',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
-  ARGS_OPTION_KEY_NUMBER       = 1000,
-  ARGS_OPTION_KEY_MINIMUM,
-  ARGS_OPTION_KEY_MAXIMUM,
-  ARGS_OPTION_KEY_SUM,
-  ARGS_OPTION_KEY_MODEQUANT,
-  ARGS_OPTION_KEY_MODESYM,
-  ARGS_OPTION_KEY_MODESYMVALUE,
-  ARGS_OPTION_KEY_QUANTFUNC,
-  ARGS_OPTION_KEY_ASCIICFP,
-  ARGS_OPTION_KEY_MIRROR,
-  ARGS_OPTION_KEY_NUMBINS,
-  ARGS_OPTION_KEY_NUMASCIIBINS,
-  ARGS_OPTION_KEY_ASCIIHEIGHT,
-  ARGS_OPTION_KEY_LOWERBIN,
-  ARGS_OPTION_KEY_ONEBINSTART,
-  ARGS_OPTION_KEY_MAXBINONE,
-  ARGS_OPTION_KEY_KHDU,
-  ARGS_OPTION_KEY_MIRRORDIST,
-  ARGS_OPTION_KEY_MODMEDQDIFF,
-  ARGS_OPTION_KEY_SMOOTHWIDTH,
-  ARGS_OPTION_KEY_CHECKSKY,
-  ARGS_OPTION_KEY_SCLIPPARAMS,
+  UI_KEY_NUMBER       = 1000,
+  UI_KEY_MINIMUM,
+  UI_KEY_MAXIMUM,
+  UI_KEY_SUM,
+  UI_KEY_MODEQUANT,
+  UI_KEY_MODESYM,
+  UI_KEY_MODESYMVALUE,
+  UI_KEY_QUANTFUNC,
+  UI_KEY_ASCIICFP,
+  UI_KEY_MIRROR,
+  UI_KEY_NUMBINS,
+  UI_KEY_NUMASCIIBINS,
+  UI_KEY_ASCIIHEIGHT,
+  UI_KEY_LOWERBIN,
+  UI_KEY_ONEBINSTART,
+  UI_KEY_MAXBINONE,
+  UI_KEY_KHDU,
+  UI_KEY_MIRRORDIST,
+  UI_KEY_MODMEDQDIFF,
+  UI_KEY_SMOOTHWIDTH,
+  UI_KEY_CHECKSKY,
+  UI_KEY_SCLIPPARAMS,
 };
 
 
diff --git a/bin/table/args.h b/bin/table/args.h
index 42fff9a..8ee1cb7 100644
--- a/bin/table/args.h
+++ b/bin/table/args.h
@@ -33,7 +33,7 @@ struct argp_option program_options[] =
   {
     {
       "column",
-      ARGS_OPTION_KEY_COLUMN,
+      UI_KEY_COLUMN,
       "STR",
       0,
       "Column number (counting from 1) or search string.",
@@ -51,7 +51,7 @@ struct argp_option program_options[] =
 
     {
       "information",
-      ARGS_OPTION_KEY_INFORMATION,
+      UI_KEY_INFORMATION,
       0,
       0,
       "Only print table and column information.",
diff --git a/bin/table/ui.h b/bin/table/ui.h
index 50fb96c..5637881 100644
--- a/bin/table/ui.h
+++ b/bin/table/ui.h
@@ -35,8 +35,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_COLUMN      = 'c',
-  ARGS_OPTION_KEY_INFORMATION = 'i',
+  UI_KEY_COLUMN      = 'c',
+  UI_KEY_INFORMATION = 'i',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
diff --git a/bin/warp/args.h b/bin/warp/args.h
index b082090..195e7fd 100644
--- a/bin/warp/args.h
+++ b/bin/warp/args.h
@@ -35,7 +35,7 @@ struct argp_option program_options[] =
     /* Input. */
     {
       "hstartwcs",
-      ARGS_OPTION_KEY_HSTARTWCS,
+      UI_KEY_HSTARTWCS,
       "INT",
       0,
       "Header keyword number to start reading WCS.",
@@ -48,7 +48,7 @@ struct argp_option program_options[] =
     },
     {
       "hendwcs",
-      ARGS_OPTION_KEY_HENDWCS,
+      UI_KEY_HENDWCS,
       "INT",
       0,
       "Header keyword number to end reading WCS.",
@@ -65,7 +65,7 @@ struct argp_option program_options[] =
     /* Output. */
     {
       "keepwcs",
-      ARGS_OPTION_KEY_KEEPWCS,
+      UI_KEY_KEEPWCS,
       0,
       0,
       "Do not apply warp to input's WCS",
@@ -78,7 +78,7 @@ struct argp_option program_options[] =
     },
     {
       "coveredfrac",
-      ARGS_OPTION_KEY_COVEREDFRAC,
+      UI_KEY_COVEREDFRAC,
       "FLT",
       0,
       "Acceptable fraction of output pixel covered.",
@@ -98,7 +98,7 @@ struct argp_option program_options[] =
     },
     {
       "align",
-      ARGS_OPTION_KEY_ALIGN,
+      UI_KEY_ALIGN,
       0,
       0,
       "Align the image and celestial axes.",
@@ -112,7 +112,7 @@ struct argp_option program_options[] =
     },
     {
       "rotate",
-      ARGS_OPTION_KEY_ROTATE,
+      UI_KEY_ROTATE,
       "FLT",
       0,
       "Rotate by the given angle in degrees.",
@@ -126,7 +126,7 @@ struct argp_option program_options[] =
     },
     {
       "scale",
-      ARGS_OPTION_KEY_SCALE,
+      UI_KEY_SCALE,
       "FLT[,FLT]",
       0,
       "Scale along the given axis(es).",
@@ -140,7 +140,7 @@ struct argp_option program_options[] =
     },
     {
       "flip",
-      ARGS_OPTION_KEY_FLIP,
+      UI_KEY_FLIP,
       "INT[,INT]",
       0,
       "Flip along the given axis(es).",
@@ -154,7 +154,7 @@ struct argp_option program_options[] =
     },
     {
       "shear",
-      ARGS_OPTION_KEY_SHEAR,
+      UI_KEY_SHEAR,
       "FLT[,FLT]",
       0,
       "Shear along the given axis(es).",
@@ -168,7 +168,7 @@ struct argp_option program_options[] =
     },
     {
       "translate",
-      ARGS_OPTION_KEY_TRANSLATE,
+      UI_KEY_TRANSLATE,
       "FLT[,FLT]",
       0,
       "Translate along the given axis(es).",
@@ -182,7 +182,7 @@ struct argp_option program_options[] =
     },
     {
       "project",
-      ARGS_OPTION_KEY_PROJECT,
+      UI_KEY_PROJECT,
       "FLT[,FLT]",
       0,
       "Project along the given axis(es).",
@@ -196,7 +196,7 @@ struct argp_option program_options[] =
     },
     {
       "matrix",
-      ARGS_OPTION_KEY_MATRIX,
+      UI_KEY_MATRIX,
       "STR",
       0,
       "Raw transformation matrix, highest priority.",
@@ -210,7 +210,7 @@ struct argp_option program_options[] =
     },
     {
       "centeroncorner",
-      ARGS_OPTION_KEY_CENTERONCORNER,
+      UI_KEY_CENTERONCORNER,
       0,
       0,
       "Center of coordinates on first pixel corner.",
diff --git a/bin/warp/ui.c b/bin/warp/ui.c
index 7904a88..2d82578 100644
--- a/bin/warp/ui.c
+++ b/bin/warp/ui.c
@@ -218,6 +218,10 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /**************************************************************/
 /**********      Modular matrix linked list       *************/
 /**************************************************************/
+/* Save the codes of the user's desired modular warpings into the linked
+   list. Because the types of these options are `GAL_TYPE_INVALID', this
+   function will not be called when printing the full list of parameters
+   and their values. */
 static void *
 ui_add_to_modular_warps_ll(struct argp_option *option, char *arg,
                            char *filename, size_t lineno, void *params)
@@ -229,7 +233,7 @@ ui_add_to_modular_warps_ll(struct argp_option *option, char 
*arg,
 
 
   /* Parse the (possible) arguments. */
-  if(option->key==ARGS_OPTION_KEY_ALIGN)
+  if(option->key==UI_KEY_ALIGN)
     {
       /* For functions the standard checking isn't done, so first, we'll
          make sure that if we are in a configuration file (where
@@ -254,7 +258,7 @@ ui_add_to_modular_warps_ll(struct argp_option *option, char 
*arg,
   /* If this was a matrix, then put it in the matrix element of the main
      data structure. Otherwise, add the list of given values to the modular
      warpings list. */
-  if(option->key==ARGS_OPTION_KEY_MATRIX)
+  if(option->key==UI_KEY_MATRIX)
     {
       /* Some sanity checks. */
       if(p->matrix)
@@ -280,14 +284,14 @@ ui_add_to_modular_warps_ll(struct argp_option *option, 
char *arg,
 
       /* Some modular-warp specific sanity checks: rotate only needs one
          number, and flip's values should only be 0 and 1. */
-      if(option->key==ARGS_OPTION_KEY_ROTATE)
+      if(option->key==UI_KEY_ROTATE)
         {
           if(new->size!=1)
             error_at_line(EXIT_FAILURE, 0, filename, lineno, "the `rotate' "
                       "option only takes one value (the angle of rotation). "
                       "You have given: `%s'", arg);
         }
-      else if (option->key==ARGS_OPTION_KEY_FLIP)
+      else if (option->key==UI_KEY_FLIP)
         {
           for(i=0;i<new->size;++i)
             {
@@ -611,11 +615,11 @@ ui_matrix_from_modular(struct warpparams *p)
          structure.*/
       switch(pop->status)
         {
-        case ARGS_OPTION_KEY_ALIGN:
+        case UI_KEY_ALIGN:
           ui_matrix_make_align(p, module);
           break;
 
-        case ARGS_OPTION_KEY_ROTATE:
+        case UI_KEY_ROTATE:
           s = sin( v1 * M_PI / 180 );
           c = cos( v1 * M_PI / 180 );
           module[0]=c;          module[1]=s;      module[2]=0.0f;
@@ -623,13 +627,13 @@ ui_matrix_from_modular(struct warpparams *p)
           module[6]=0.0f;       module[7]=0.0f;   module[8]=1.0f;
           break;
 
-        case ARGS_OPTION_KEY_SCALE:
+        case UI_KEY_SCALE:
           module[0]=v1;         module[1]=0.0f;   module[2]=0.0f;
           module[3]=0.0f;       module[4]=v2;     module[5]=0.0f;
           module[6]=0.0f;       module[7]=0.0f;   module[8]=1.0f;
           break;
 
-        case ARGS_OPTION_KEY_FLIP:
+        case UI_KEY_FLIP:
           if      ( v1==1.0f && v2==0.0f )
             {
               module[0]=1.0f;   module[1]=0.0f;
@@ -655,19 +659,19 @@ ui_matrix_from_modular(struct warpparams *p)
           module[6]=0.0f;       module[7]=0.0f;   module[8]=1.0f;
           break;
 
-        case ARGS_OPTION_KEY_SHEAR:
+        case UI_KEY_SHEAR:
           module[0]=1.0f;       module[1]=v1;     module[2]=0.0f;
           module[3]=v2;         module[4]=1.0f;   module[5]=0.0f;
           module[6]=0.0f;       module[7]=0.0f;   module[8]=1.0f;
           break;
 
-        case ARGS_OPTION_KEY_TRANSLATE:
+        case UI_KEY_TRANSLATE:
           module[0]=1.0f;       module[1]=0.0f;     module[2]=v1;
           module[3]=0.0f;       module[4]=1.0f;     module[5]=v2;
           module[6]=0.0f;       module[7]=0.0f;     module[8]=1.0f;
           break;
 
-        case ARGS_OPTION_KEY_PROJECT:
+        case UI_KEY_PROJECT:
           module[0]=1.0f;       module[1]=0.0f;     module[2]=0.0f;
           module[3]=0.0f;       module[4]=1.0f;     module[5]=0.0f;
           module[6]=v1;         module[7]=v2;       module[8]=1.0f;
@@ -819,25 +823,25 @@ ui_set_suffix(struct warpparams *p)
   if(p->matrix==NULL && p->modularll->next==NULL)
     switch(p->modularll->status)
       {
-      case ARGS_OPTION_KEY_ALIGN:
+      case UI_KEY_ALIGN:
         return "_aligned.fits";
 
-      case ARGS_OPTION_KEY_ROTATE:
+      case UI_KEY_ROTATE:
         return "_rotated.fits";
 
-      case ARGS_OPTION_KEY_SCALE:
+      case UI_KEY_SCALE:
         return "_scaled.fits";
 
-      case ARGS_OPTION_KEY_FLIP:
+      case UI_KEY_FLIP:
         return "_flipped.fits";
 
-      case ARGS_OPTION_KEY_SHEAR:
+      case UI_KEY_SHEAR:
         return "_sheared.fits";
 
-      case ARGS_OPTION_KEY_TRANSLATE:
+      case UI_KEY_TRANSLATE:
         return "_translated.fits";
 
-      case ARGS_OPTION_KEY_PROJECT:
+      case UI_KEY_PROJECT:
         return "_projected.fits";
 
       default:
@@ -988,4 +992,8 @@ ui_free_report(struct warpparams *p, struct timeval *t1)
   free(p->cp.output);
   gal_data_free(p->input);
   gal_data_free(p->matrix);
+
+  /* Report how long the operation took. */
+  if(!p->cp.quiet)
+    gal_timing_report(t1, PROGRAM_NAME" finished in: ", 0);
 }
diff --git a/bin/warp/ui.h b/bin/warp/ui.h
index c6ca23d..a8b140d 100644
--- a/bin/warp/ui.h
+++ b/bin/warp/ui.h
@@ -35,22 +35,22 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum option_keys_enum
 {
   /* With short-option version. */
-  ARGS_OPTION_KEY_KEEPWCS         = 'k',
-  ARGS_OPTION_KEY_COVEREDFRAC     = 'C',
-  ARGS_OPTION_KEY_ALIGN           = 'a',
-  ARGS_OPTION_KEY_ROTATE          = 'r',
-  ARGS_OPTION_KEY_SCALE           = 's',
-  ARGS_OPTION_KEY_FLIP            = 'f',
-  ARGS_OPTION_KEY_SHEAR           = 'e',
-  ARGS_OPTION_KEY_TRANSLATE       = 't',
-  ARGS_OPTION_KEY_PROJECT         = 'p',
-  ARGS_OPTION_KEY_MATRIX          = 'm',
-  ARGS_OPTION_KEY_CENTERONCORNER  = 'c',
+  UI_KEY_KEEPWCS         = 'k',
+  UI_KEY_COVEREDFRAC     = 'C',
+  UI_KEY_ALIGN           = 'a',
+  UI_KEY_ROTATE          = 'r',
+  UI_KEY_SCALE           = 's',
+  UI_KEY_FLIP            = 'f',
+  UI_KEY_SHEAR           = 'e',
+  UI_KEY_TRANSLATE       = 't',
+  UI_KEY_PROJECT         = 'p',
+  UI_KEY_MATRIX          = 'm',
+  UI_KEY_CENTERONCORNER  = 'c',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
-  ARGS_OPTION_KEY_HSTARTWCS       = 1000,
-  ARGS_OPTION_KEY_HENDWCS,
+  UI_KEY_HSTARTWCS       = 1000,
+  UI_KEY_HENDWCS,
 };
 
 
diff --git a/bin/warp/warp.c b/bin/warp/warp.c
index 887505c..1508b9c 100644
--- a/bin/warp/warp.c
+++ b/bin/warp/warp.c
@@ -125,7 +125,7 @@ warponthread(void *inparam)
   double ocrn[8], icrn_base[8], icrn[8], *output=p->output->array;
   double pcrn[8], *outfpixval=p->outfpixval, ccrn[GAL_POLYGON_MAX_CORNERS];
 
-  for(i=0;(ind=iwp->indexs[i])!=GAL_THREADS_NON_THRD_INDEX;++i)
+  for(i=0; (ind=iwp->indexs[i])!=GAL_BLANK_SIZE_T; ++i)
     {
       /* Initialize the output pixel value: */
       numinput=0;
@@ -539,7 +539,7 @@ warp(struct warpparams *p)
 
       /* Spin off the threads: */
       for(i=0;i<nt;++i)
-        if(indexs[i*thrdcols]!=GAL_THREADS_NON_THRD_INDEX)
+        if(indexs[i*thrdcols]!=GAL_BLANK_SIZE_T)
           {
             iwp[i].p=p;
             iwp[i].b=&b;
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index 6cb04e2..4349cfa 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -437,6 +437,7 @@ Sky value
 
 NoiseChisel
 
+* NoiseChisel changes after publication::  Changes to the software after 
publication.
 * Invoking astnoisechisel::     Options and arguments for NoiseChisel.
 
 Invoking NoiseChisel
@@ -448,10 +449,18 @@ Invoking NoiseChisel
 
 MakeCatalog
 
-* Quantifying data limits::     For comparing different catalogs.
+* Detection and catalog production::  Discussing why/how to treat these 
separately
+* Quantifying measurement limits::  For comparing different catalogs.
 * Measuring elliptical parameters::  Estimating elliptical parameters.
-* Invoking astmkcatalog::       Options and arguments to MakeCatalog.
 * Adding new columns to MakeCatalog::  How to add new columns.
+* Invoking astmkcatalog::       Options and arguments to MakeCatalog.
+
+Invoking MakeCatalog
+
+* MakeCatalog input files::     Specifying the different input files.
+* MakeCatalog general settings::  Options for general column settings.
+* Upper-limit magnitude settings::  Necessary to define upper-limit magnitudes.
+* MakeCatalog output columns::  How to specify the columns in the output.
 
 Modeling and fitting
 
@@ -1858,80 +1867,82 @@ all, all observations have noise associated with them.
 
 @end enumerate
 
-Fortunately Sufi had heard of GNU Astronomy Utilities from a colleague
-in Isfahan (where he worked) and had installed it on his computer a
-year before. It had tools to do all the steps above. He had used
-MakeProfiles before, but wasn't sure which columns he had chosen in
-his user or system wide configuration files for which parameters, see
address@hidden files}. So to start his simulation, Sufi runs
-MakeProfiles with the @option{-P} option to make sure what columns in
-a catalog MakeProfiles currently recognizes and the output image
-parameters:
+Fortunately Sufi had heard of GNU Astronomy Utilities from a colleague in
+Isfahan (where he worked) and had installed it on his computer a year
+before. It had tools to do all the steps above. He had used MakeProfiles
+before, but wasn't sure which columns he had chosen in his user or system
+wide configuration files for which parameters, see @ref{Configuration
+files}. So to start his simulation, Sufi runs MakeProfiles with the
address@hidden option to make sure what columns in a catalog MakeProfiles
+currently recognizes and the output image parameters. In particular, Sufi
+is interested in the recognized columns (shown below).
 
 @example
 $ astmkprof -P
-# MakeProfiles (GNU Astronomy Utilities 0.1) 0.1
-# Configured on 21 September 952 at 19:37
-# Written on Sat Oct  6 15:49:31 953
 
-# Output:
- naxis1              1000
- naxis2              1000
- oversample          5
+[[[ ... Truncated lines ... ]]]
 
-[[[ Truncated middle of list ]]]
+# Output:
+ type         float32     # Type of output: e.g., int16, float32, etc...
+ naxis1       1000        # Number of pixels along first FITS axis.
+ naxis2       1000        # Number of pixels along second FITS axis.
+ oversample   5           # Scale of oversampling (>0 and odd).
+
+[[[ ... Truncated lines ... ]]]
+
+# Columns, by info (see `--searchin'), or number (starting from 1):
+ xcol         2           # Center along first FITS axis (horizontal).
+ ycol         3           # Center along second FITS axis (vertical).
+ fcol         4           # sersic (1), moffat (2), gaussian (3),
+                          # point (4), flat (5), circumference (6).
+ rcol         5           # Effective radius or FWHM in pixels.
+ ncol         6           # Sersic index or Moffat beta.
+ pcol         7           # Position angle.
+ qcol         8           # Axis ratio.
+ mcol         9           # Magnitude.
+ tcol         10          # Truncation in units of radius or pixels.
+
+[[[ ... Truncated lines ... ]]]
 
-# Catalog:
- xcol                1
- ycol                2
- fcol                3
- rcol                4
- ncol                5
- pcol                6
- qcol                7
- mcol                8
- tcol                9
-
-[[[ Truncated rest of list ]]]
 @end example
 
 @noindent
-In particular, Sufi looks at the parameters under the catalog
-grouping.  Fortunately the columns are naturally numbered such that
-column 0 can be an ID he specifies for each object (which MakeProfiles
-ignores) and each subsequent column specifies a given
-parameter. Fortunately MakeProfiles has the capability to also make
-the PSF which is to be used on the mock image and using the
address@hidden option, he can also make the mock image to be
-larger by the correct amount and all the sources to be shifted by the
-correct amount.
-
-For his initial check he decides to simulate the nebula in the
-Andromeda constellation. The night he was observing, the PSF had
-roughly a FWHM of about 5 pixels, so as the first row, he defines the
-PSF parameters and sets the radius column (@code{rcol} above, fifth
-column) to @code{5.000}, he also chooses a Moffat function for its
-functional form. Remembering how diffuse the nebula in the Andromeda
-constellation was, he decides to simulate it with a mock S@'{e}rsic
-index 1.0 profile. He wants the output to be 500 pixels by 500 pixels,
-so he puts the mock profile in the center. Looking at his drawings of
-it, he decides a reasonable effective radius for it would be 40 pixels
-on this image pixel scale, he sets the axis ratio and position angle
-to approximately correct values too and finally he sets the total
-magnitude of the profile to 3.44 which he had accurately
-measured. Sufi also decides to truncate both the mock profile and PSF
-at 5 times the respective radius parameters. In the end he decides to
-put four stars on the four corners of the image at very low magnitudes
-as a visual scale.
-
-Using all the information above, he creates the catalog of mock
-profiles he wants in a file named @file{cat.txt} (short for catalog)
-using his favorite text editor and stores it in a directory named
address@hidden in his home directory. [The @command{cat}
-command prints the contents of a file, short for concatenation. So
-please copy-paste the outputs of the command address@hidden cat.txt}''
-into @file{cat.txt} when the editor opens in the steps above it, note
-that there are 6 lines]:
+In Gnuastro, column counting starts from 1, so the columns are ordered such
+that the first column (number 1) can be an ID he specifies for each object
+(and MakeProfiles ignores), each subsequent column is used used for another
+property of the profile. It is also possible to use column names for the
+values of these options and change these defaults, but Sufi preferred to
+stick to the defaults. Fortunately MakeProfiles has the capability to also
+make the PSF which is to be used on the mock image and using the
address@hidden option, he can also make the mock image to be larger
+by the correct amount and all the sources to be shifted by the correct
+amount.
+
+For his initial check he decides to simulate the nebula in the Andromeda
+constellation. The night he was observing, the PSF had roughly a FWHM of
+about 5 pixels, so as the first row (profile), he defines the PSF
+parameters and sets the radius column (@code{rcol} above, fifth column) to
address@hidden, he also chooses a Moffat function for its functional
+form. Remembering how diffuse the nebula in the Andromeda constellation
+was, he decides to simulate it with a mock S@'{e}rsic index 1.0 profile. He
+wants the output to be 500 pixels by 500 pixels, so he puts the mock
+profile in the center. Looking at his drawings of it, he decides a
+reasonable effective radius for it would be 40 pixels on this image pixel
+scale, he sets the axis ratio and position angle to approximately correct
+values too and finally he sets the total magnitude of the profile to 3.44
+which he had accurately measured. Sufi also decides to truncate both the
+mock profile and PSF at 5 times the respective radius parameters. In the
+end he decides to put four stars on the four corners of the image at very
+low magnitudes as a visual scale.
+
+Using all the information above, he creates the catalog of mock profiles he
+wants in a file named @file{cat.txt} (short for catalog) using his favorite
+text editor and stores it in a directory named @file{simulationtest} in his
+home directory. [The @command{cat} command prints the contents of a file,
+short for concatenation. So please copy-paste the lines after
address@hidden cat.txt}'' into @file{cat.txt} when the editor opens in the
+steps above it, note that there are 7 lines, first one starting with
address@hidden:
 
 @example
 $ mkdir ~/simulationtest
@@ -1942,12 +1953,13 @@ $ emacs cat.txt
 $ ls
 cat.txt
 $ cat cat.txt
- 0  0.0000   0.0000  1  5.000  4.765  0.0000  1.000  30.000  5.000
- 1  250.00   250.00  0  40.00  1.000  -25.00  0.400  3.4400  5.000
- 2  50.000   50.000  3  0.000  0.000  0.0000  0.000  9.0000  0.000
- 3  450.00   50.000  3  0.000  0.000  0.0000  0.000  9.2500  0.000
- 4  50.000   450.00  3  0.000  0.000  0.0000  0.000  9.5000  0.000
- 5  450.00   450.00  3  0.000  0.000  0.0000  0.000  9.7500  0.000
+# Column 4: PROFILE_NAME [,str7] Radial profile's functional name
+ 1  0.0000   0.0000  moffat  5.000  4.765  0.0000  1.000  30.000  5.000
+ 2  250.00   250.00  sersic  40.00  1.000  -25.00  0.400  3.4400  5.000
+ 3  50.000   50.000  point   0.000  0.000  0.0000  0.000  9.0000  0.000
+ 4  450.00   50.000  point   0.000  0.000  0.0000  0.000  9.2500  0.000
+ 5  50.000   450.00  point   0.000  0.000  0.0000  0.000  9.5000  0.000
+ 6  450.00   450.00  point   0.000  0.000  0.0000  0.000  9.7500  0.000
 @end example
 
 @noindent
@@ -1959,112 +1971,124 @@ necessary parameters and runs MakeProfiles with the 
following command:
 $ astmkprof --prepforconv --naxis1=500 --naxis2=500         \
             --zeropoint=18.0 cat.txt
 MakeProfiles started on Sat Oct  6 16:26:56 953
-  - 6 profiles read from cat.txt             in 0.000209 seconds
-  ---- Row 5 complete, 5 left to go.
-  ---- Row 3 complete, 4 left to go.
-  ---- Row 2 complete, 3 left to go.
-  ---- Row 4 complete, 2 left to go.
+  - 6 profiles read from cat.txt
+  - Random number generator (RNG) type: mt19937
+  - Using 8 threads.
+  ---- row 2 complete, 5 left to go
+  ---- row 3 complete, 4 left to go
+  ---- row 4 complete, 3 left to go
+  ---- row 5 complete, 2 left to go
   ---- ./0_cat.fits created.
-  ---- Row 0 complete, 1 left to go.
-  ---- Row 1 complete, 0 left to go.
-  - cat.fits created.                        in 0.024811 seconds
-MakeProfiles finished in:  0.236629 (seconds)
+  ---- row 0 complete, 1 left to go
+  ---- row 1 complete, 0 left to go
+  - ./cat.fits created.                                0.041651 seconds
+MakeProfiles finished in 0.267234 seconds
+
 $ls
-0_cat.fits  astmkprof.log  cat.fits  cat.txt
+0_cat.fits  cat.fits  cat.txt
 @end example
 
 @cindex Oversample
 @noindent
-The file @file{0.fits} is the PSF Sufi had asked for and
address@hidden is the image containing the 5 objects. The PSF is now
-available to him as a separate file for the convolution step. While he
-was preparing the catalog, one of his students came up and was also
-following the steps. When he opened the image, the student was
-surprised to see that all the stars are only one pixel and not in the
-shape of the PSF as we see when we image the sky at night. So Sufi
-explained to him that the stars will take the shape of the PSF after
-convolution and this is how they would look if we didn't have an
-atmosphere or an aperture when we took the image. The size of the
-image was also surprising for the student, instead of 500 by 500, it
-was 2630 by 2630 pixels. So Sufi had to explain why oversampling is
-very important for parts of the image where the flux change is
-significant over a pixel. Sufi then explained to him that after
+The file @file{0_cat.fits} is the PSF Sufi had asked for and
address@hidden is the image containing the other 5 objects. The PSF is now
+available to him as a separate file for the convolution step. While he was
+preparing the catalog, one of his students approached him and was also
+following the steps. When he opened the image, the student was surprised to
+see that all the stars are only one pixel and not in the shape of the PSF
+as we see when we image the sky at night. So Sufi explained to him that the
+stars will take the shape of the PSF after convolution and this is how they
+would look if we didn't have an atmosphere or an aperture when we took the
+image. The size of the image was also surprising for the student, instead
+of 500 by 500, it was 2630 by 2630 pixels. So Sufi had to explain why
+oversampling is very important for parts of the image where the flux change
+is significant over a pixel. Sufi then explained to him that after
 convolving we will re-sample the image to get our originally desired
 size. To convolve the image, Sufi ran the following command:
 
 @example
 $ astconvolve --kernel=0_cat.fits cat.fits
 Convolve started on Mon Apr  6 16:35:32 953
-Convolving cat.fits (hdu: 0)
- with the kernel 0.fits (hdu: 0).
- using 8 CPU threads in the frequency domain.
-  - Input and Kernel images padded.          in 0.045576 seconds
-  - Images converted to frequency domain.    in 10.486712 seconds
-  - Multiplied in the frequency domain.      in 0.032780 seconds
-  - Converted back to the spatial domain.    in 5.342335 seconds
-  - Padded parts removed.                    in 0.011880 seconds
-Convolve finished in:  15.972771 (seconds)
+  - Using 8 CPU threads.
+  - Input: cat.fits (hdu: 1)
+  - Kernel: 0_cat.fits (hdu: 1)
+  - Input and Kernel images padded.                    0.075541 seconds
+  - Images converted to frequency domain.              6.728407 seconds
+  - Multiplied in the frequency domain.                0.040659 seconds
+  - Converted back to the spatial domain.              3.465344 seconds
+  - Padded parts removed.                              0.016767 seconds
+Convolve finished in:  10.422161 seconds
+
 $ls
-0_cat.fits  astmkprof.log  cat_convolved.fits  cat.fits  cat.txt
+0_cat.fits  cat_convolved.fits  cat.fits  cat.txt
 @end example
 
 @noindent
 When convolution finished, Sufi opened the @file{cat_convolved.fits} file
 and showed the effect of convolution to his student and explained to him
 how a PSF with a larger FWHM would make the points even wider. With the
-convolved image ready, they were ready to re-sample it to the original
-pixel scale Sufi had planned. Sufi explained the basic concepts of warping
-the image to his student and ran Warp with the following command:
+convolved image ready, they were prepared to re-sample it to the original
+pixel scale Sufi had planned [from the @command{$ astmkprof -P} command
+above, recall that MakeProfiles had oversampled the image by 5 times]. Sufi
+explained the basic concepts of warping the image to his student and ran
+Warp with the following command:
 
 @example
-$ astwarp cat_convolved.fits --scale=0.2
+$ astwarp --scale=1/5 --centeroncorner cat_convolved.fits
 Warp started on Mon Apr  6 16:51:59 953
  Using 8 CPU threads.
- Input image: cat_convolved.fits
+ Input: cat_convolved.fits (hdu: 1)
  matrix:
-        0.2000   0.0000   0.4000
-        0.0000   0.2000   0.4000
-        0.0000   0.0000   1.0000
-Warp finished in:  0.481421 (seconds)
+       0.2000   0.0000   0.4000
+       0.0000   0.2000   0.4000
+       0.0000   0.0000   1.0000
+
 $ ls
-0_cat.fits     cat_convolved.fits         cat.fits
-astmkprof.log  cat_convolved_warped.fits  cat.txt
+0_cat.fits          cat_convolved_scaled.fits     cat.txt
+cat_convolved.fits  cat.fits
+
+$ astfits -p cat_convolved_scaled.fits | grep NAXIS
+NAXIS   =                    2 / number of data axes
+NAXIS1  =                  526 / length of data axis 1
+NAXIS2  =                  526 / length of data axis 2
 @end example
 
 @noindent
 @file{cat_convolved_warped.fits} now has the correct pixel scale. However,
 the image is still larger than what we had wanted, it is 526
 (@mymath{500+13+13}) by 526 pixels. The student is slightly confused, so
-Sufi also resamples the PSF with Warp and the same warping matrix and shows
-him that it is 27 (@mymath{2\times13+1}) by 27 pixels. Sufi goes on to
-explain how frequency space convolution will dim the edges and that is why
-he added the @option{--prepforconv} option to MakeProfiles, see @ref{If
-convolving afterwards}. Now that convolution is done Sufi can remove those
-extra pixels using Crop:
+Sufi also resamples the PSF with the same scale and shows him that it is 27
+(@mymath{2\times13+1}) by 27 pixels. Sufi goes on to explain how frequency
+space convolution will dim the edges and that is why he added the
address@hidden option to MakeProfiles, see @ref{If convolving
+afterwards}. Now that convolution is done, Sufi can remove those extra
+pixels using Crop with the command below. Crop's @option{--section} option
+accepts coordinates inclusively and counting from 1 (according to the FITS
+standard), so the crop's first pixel has to be 14, not 13.
 
 @example
-$ astcrop cat_convolved_warped.fits --section=13:*-13,13:*-13  \
-             --zeroisnotblank
+$ astcrop cat_convolved_scaled.fits --section=14:*-13,14:*-13    \
+          --zeroisnotblank
 Crop started on Sat Oct  6 17:03:24 953
-  - Read metadata of 1 images.               in 0.000560 seconds
-  ---- cat_convolved_warped_crop.fits 1 1
-Crop finished in:  0.018917 (seconds)
+  - Read metadata of 1 image.                          0.001304 seconds
+  ---- ...nvolved_scaled_cropped.fits created: 1 input.
+Crop finished in:  0.027204 seconds
+
 $ls
-0_cat.fits      astmkprof.log           cat_convolved_warped.fits
-0_warped.fits   cat_convolved.fits              cat.fits
-astcrop.log  cat_convolved_warped_crop.fits  cat.txt
+0_cat.fits          cat_convolved_scaled_cropped.fits  cat.fits
+cat_convolved.fits  cat_convolved_scaled.fits          cat.txt
 @end example
 
 @noindent
-Finally, the @file{cat_convolved_warped.fits} has the same dimensions as
-Sufi had asked for in the beginning. All this trouble was certainly worth
+Finally, @file{cat_convolved_scaled_cropped.fits} has the same dimensions
+as Sufi had desired in the beginning. All this trouble was certainly worth
 it because now there is no dimming on the edges of the image and the
 profile centers are more accurately sampled. The final step to simulate a
 real observation would be to add noise to the image. Sufi set the zeropoint
 magnitude to the same value that he set when making the mock profiles and
-looking again at his observation log, he found that at that night the
-background flux near the nebula had a magnitude of 7. So using these values
-he ran MakeNoise:
+looking again at his observation log, he had measured the background flux
+near the nebula had a magnitude of 7 that night. So using these values he
+ran MakeNoise:
 
 @example
 $ astmknoise --zeropoint=18 --background=7 --output=out.fits    \
@@ -2073,52 +2097,80 @@ MakeNoise started on Mon Apr  6 17:05:06 953
   - Generator type: mt19937
   - Generator seed: 1428318100
 MakeNoise finished in:  0.033491 (seconds)
+
 $ls
-0_cat.fits      cat_convolved.fits              cat.txt
-0_warped.fits   cat_convolved_warped_crop.fits  out.fits
-astcrop.log  cat_convolved_warped.fits
-astmkprof.log   cat.fits
+0_cat.fits         cat_convolved_scaled_cropped.fits cat.fits  out.fits
+cat_convolved.fits cat_convolved_scaled.fits         cat.txt
 @end example
 
 @noindent
-The @file{out.fits} file now has the noised image of the mock catalog
-Sufi had asked for. Seeing how the @option{--output} option allows the
-user to specify the name of the output file, the student was confused
-and wanted to know why Sufi hadn't used it before? Sufi then explained
-to him that for intermediate steps it is best to rely on the automatic
-output, see @ref{Automatic output}. Doing so will give all the
-intermediate files the same basic name structure, so in the end you
-can simply remove them all with the Shell's capabilities. So Sufi
-decided to show this to the student by making a shell script from the
-commands he had used before.
-
-The command-line shell has the capability to read all the separate
-input commands from a file. This is very useful when you want to do
-the same thing multiple times, with only the names of the files or
-minor parameters changing between the different instances. Using the
-shell's history (by pressing the up keyboard key) Sufi reviewed all
-the commands and then he retrieved the last 5 commands with the
address@hidden history 5} command. He selected all those lines he had
-input and put them in a text file named @file{mymock.sh}. Then he used
-some shell variables to set the two main constant parts of all the
-command to generalized variables.
+The @file{out.fits} file now contains the noised image of the mock catalog
+Sufi had asked for. Seeing how the @option{--output} option allows the user
+to specify the name of the output file, the student was confused and wanted
+to know why Sufi hadn't used it before? Sufi then explained to him that for
+intermediate steps it is best to rely on the automatic output, see
address@hidden output}. Doing so will give all the intermediate files the
+same basic name structure, so in the end you can simply remove them all
+with the Shell's capabilities. So Sufi decided to show this to the student
+by making a shell script from the commands he had used before.
+
+The command-line shell has the capability to read all the separate input
+commands from a file. This is very useful when you want to do the same
+thing multiple times, with only the names of the files or minor parameters
+changing between the different instances. Using the shell's history (by
+pressing the up keyboard key) Sufi reviewed all the commands and then he
+retrieved the last 5 commands with the @command{$ history 5} command. He
+selected all those lines he had input and put them in a text file named
address@hidden Then he defined the @code{edge} and @code{base} shell
+variables for easier customization later. Finally, before every command, he
+added some comments (lines starting with @key{#}) for future
+readability.
 
 @example
+# Basic settings:
 edge=13
 base=cat
+
+# Remove any existing image to avoid confusion.
 rm out.fits
 
-astmkprof --prepforconv --naxis1=500 --naxis2=500            \
+# Run MakeProfiles to create an oversampled FITS image.
+astmkprof --prepforconv --naxis1=500 --naxis2=500                \
           --zeropoint=18.0 "$base".txt
-astconvolve --kernel=0.fits "$base".fits
-astwarp "$base"_convolved.fits --scale=0.2
-astcrop "$base"_convolved_warped.fits                        \
-           --section=$edge:*-$edge,$edge:*-$edge
-astmknoise --zeropoint=18 --background=7 --output=out.fits   \
-           "$base"_convolved_warped_crop.fits
-rm 0*.fits cat*.fits *.log
+
+# Convolve the created image with the kernel.
+astconvolve --kernel=0_"$base".fits "$base".fits
+
+# Scale the image back to the intended resolution.
+astwarp --scale=1/5 --centeroncorner "$base"_convolved.fits
+
+# Crop the edges out (dimmed during convolution). `--section' accepts
+# inclusive coordinates, so the start of start of the section must be
+# one pixel larger than its end.
+st_edge=$(( edge + 1 ))
+astcrop "$base"_convolved_scaled.fits --zeroisnotblank          \
+        --section=$st_edge:*-$edge,$st_edge:*-$edge
+
+# Add noise to the image.
+astmknoise --zeropoint=18 --background=7 --output=out.fits      \
+           "$base"_convolved_scaled_cropped.fits
+
+# Remove all the temporary files.
+rm 0*.fits cat*.fits
 @end example
 
address@hidden Comments
+He used this chance to remind the student of the importance of comments in
+code or shell scripts: when writing the code, you have a very good mental
+picture of what you are doing, so writing comments might seem superfluous
+and excessive. However, in one month when you want to re-use the script,
+you have lost that mental picture and rebuilding it is can be very
+time-consuming and frustrating. The importance of comments is further
+amplified when you want to share the script with a friend/colleague. So it
+is very good to accompany any script/code with useful comments while you
+are writing it (have a good mental picture of what/why you are doing
+something).
+
 @cindex Gedit
 @cindex GNU Emacs
 Sufi then explained to the eager student that you define a variable by
@@ -4436,10 +4488,7 @@ this option are:
 @table @command
 @item txt
 A plain text table with white-space characters between the columns (see
address@hidden text table format}). Setting @option{--tabletype} to this
-value is acceptable, but in many cases irrelevant, because the plain text
-table format is identified by filename, and currently there is only format
-to write plane text tables.
address@hidden text table format}).
 @item fits-ascii
 A FITS ASCII table (see @ref{Recognized table formats}).
 @item fits-binary
@@ -11936,7 +11985,10 @@ noise/sky while those pixels with labels larger than 0 
are detections
 (separate segments will be given positive integers, starting from 1). For
 more on NoiseChisel's particular output format and its benefits (especially
 in conjunction with @ref{MakeCatalog}), please see
address@hidden://arxiv.org/abs/1611.06387, Akhlaghi [2016]}.
address@hidden://arxiv.org/abs/1611.06387, Akhlaghi [2016]}. The published
+paper cannot under go any updates, but the NoiseChisel software has
+evolved, you can see the major changes in @ref{NoiseChisel changes after
+publication}.
 
 Data is inherently mixed with noise: only mock/simulated datasets are free
 of noise. So this process of separating signal from noise is not
@@ -11950,21 +12002,6 @@ Gnuastro: the first generation of Gnuastro's programs 
were all first part
 of what later became NoiseChisel, afterwards they spinned-off into separate
 programs.
 
-Before using NoiseChisel it is strongly recommended to read
address@hidden://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]} to
-gain a good understanding of what it does and how each parameter influences
-the output. Thanks to that paper, there is no more need to continue this
-introduction any further and we can just dive into the details of running
-NoiseChisel in the following sections. However, the paper cannot undergo
-any further updates, but NoiseChisel will evolve: better algorithms or
-steps will be found, thus options will be added or removed. So this section
-is the definitive guide to the options. Please follow the
address@hidden@footnote{The @file{NEWS} file is in the released Gnuastro
-tarball (see @ref{Release tarball}). You can also see it online at
address@hidden://git.savannah.gnu.org/cgit/gnuastro.git/plain/NEWS}.} file with
-each release to see how they have changed since the publication of that
-paper.
-
 @cindex Erosion
 The name of NoiseChisel is derived from the first thing it does after
 thresholding the dataset: to erode it. In mathematical morphology, erosion
@@ -11979,10 +12016,62 @@ specific option to carve out your signal out of the 
noise more
 successfully.
 
 @menu
+* NoiseChisel changes after publication::  Changes to the software after 
publication.
 * Invoking astnoisechisel::     Options and arguments for NoiseChisel.
 @end menu
 
address@hidden Invoking astnoisechisel,  , NoiseChisel, NoiseChisel
address@hidden NoiseChisel changes after publication, Invoking astnoisechisel, 
NoiseChisel, NoiseChisel
address@hidden NoiseChisel changes after publication
+
+Before using NoiseChisel it is strongly recommended to read
address@hidden://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]} to
+gain a good understanding of what it does and how each parameter influences
+the output. Thanks to that paper, there is no more need to continue this
+introduction any further and we can just dive into the details of running
+NoiseChisel in @ref{Invoking astnoisechisel}. However, the paper cannot
+undergo any further updates, but NoiseChisel will evolve: better algorithms
+or steps will be found, thus options will be added or removed. So this book
+is the final and definitive guide. To make the transition form the paper to
+this book easier (and encourage reading the paper), below you can see the
+major changes since that paper was published.
+
address@hidden
+
address@hidden
address@hidden: to specify a quantile threshold where erosion
+will not apply. This is useful to detect sharper point-like sources that
+will be missed due to too much erosion. To completely ignore this features
+give this option a value of 1 (only the largest valued pixel in the input
+will not be eroded).
+
address@hidden
address@hidden: After dilation, if the signal-to-noise ratio of a
+detection is less than the derived pseudo-detection S/N limit, that
+detection will be discarded. In an ideal/clean noise, a true detection's
+S/N should be larger than its constituent pseudo-detections because its
+area is larger and it also covers more signal. However, on a false
+detections (especially at lower @option{--detquant} values), the increase
+in size can cause a decrease in S/N below that threshold.
+
+This will improve purity and not change completeness (a true detection will
+not be discarded). Because a true detection has flux in its vicinity and
+dilation will catch more of that flux and increase the S/N. So on a true
+detection, the final S/N cannot be less than pseudo-detections.
+
+However, in many real images bad processing creates artifacts that cannot
+be accurately removed by the Sky subtraction. In such cases, this option
+will decrease the completeness (will artificially discard true
+detections). So this feature is not default and should to be explicitly
+called when you know the noise is clean.
+
address@hidden itemize
+
+For a more detailed list of updates in each release, please follow the
address@hidden file. The @file{NEWS} file is in the released Gnuastro tarball
+(see @ref{Release tarball}). You can also see it online at
address@hidden://git.savannah.gnu.org/cgit/gnuastro.git/plain/NEWS}.
+
address@hidden Invoking astnoisechisel,  , NoiseChisel changes after 
publication, NoiseChisel
 @subsection Invoking NoiseChisel
 
 NoiseChisel will detect and segment signal in noise producing a
@@ -12387,19 +12476,20 @@ psudo-detection that is smaller than this area. Use
 @option{--detsnhistnbins} to check if this value is reasonable or not.
 
 @item --checkdetsn
-Save the S/N values of the pseudo-detections into two files ending with
address@hidden and @file{_detsn_det.XXX}. The @file{.XXX} is
-determined from the @option{--tableformat} option (see @ref{Input output
-options}, for example @file{.txt} or @file{.fits}). You can use these to
-inspect the S/N values and their distribution (in combination with the
address@hidden option to see where the pseudo-detections are).
-You can use Gnuastro's @ref{Statistics} to make a histogram of the
-distribution (ready for plotting in a text file, or a crude ASCII-art
-demonstration on the command-line).
-
-With this option, NoiseChisel will abort as soon as the two tables are
+Save the S/N values of the pseudo-detections and dilated detections into
+three files ending with @file{_detsn_sky.XXX}, @file{_detsn_det.XXX}, and
address@hidden The @file{.XXX} is determined from the
address@hidden option (see @ref{Input output options}, for example
address@hidden or @file{.fits}). You can use these to inspect the S/N values
+and their distribution (in combination with the @option{--checkdetection}
+option to see where the pseudo-detections are).  You can use Gnuastro's
address@hidden to make a histogram of the distribution or any other
+analysis you would like for better understanding of the distribution (for
+example through a histogram).
+
+With this option, NoiseChisel will abort as soon as the tables are
 created. This allows you to inspect the steps leading to the final quantile
-threshold, this behavior can be disabled with
+threshold, this behavior (to abort NoiseChisel) can be disabled with
 @option{--continueaftercheck}.
 
 @item -c FLT
@@ -12455,7 +12545,7 @@ In NoiseChisel, segmentation is done by first finding 
the `true' clumps
 over a detection and then expanding those clumps to a certain flux
 limit. True clumps are found in a process very similar to the true
 detections explained in @ref{Detection options}, see
address@hidden://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa 2015} for more
address@hidden://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]} for 
more
 information. If the connections between the grown clumps are weaker than a
 given threshold, the grown clumps are considered to be separate objects.
 
@@ -12638,46 +12728,125 @@ pixel.
 @node MakeCatalog,  , NoiseChisel, Data analysis
 @section MakeCatalog
 
-Detecting and segmenting signal over an image results in labeled
-images where each pixel is labeled with the ID (an integer) that is
-specified by the detector program. But this labeled image by its self
-can hardly be of any scientific use. The job of MakeCatalog is to
-combine the input image, the noise properties and the labels of pixels
-into a catalog (a text file table) which can easily be used for
-high-level scientific interpretations.
-
-NoiseChisel (Gnuastro's signal detection tool, see @ref{NoiseChisel})
-does not produce any catalog of the detected objects by its self. Only
-a labeled FITS image is output, see @ref{NoiseChisel output}. The
-output of NoiseChisel can be directly fed into MakeCatalog to generate
-the catalog. Some of the reasons for making the catalog in a separate
address@hidden existing software that do object detection also
-output a catalog, so this is not a common practice.} are listed below:
+At the lowest level, a dataset (for example an image) is just a collection
+of values, placed after each other in any number of dimensions (for example
+an image is a 2D dataset). Each data-element (pixel) just has two
+properties: its position (relative to the rest) and its value. The entire
+input dataset (a large image for example) is rarely treated as a singular
+entity for higher-level address@hidden low-level
+reduction/preparation of a dataset, you do infact treat a whole image as a
+single entity. You can derive the over-all properties of all the pixels in
+a dataset with Gnuastro's Statistics program (see @ref{Statistics})}. You
+want to know the properties of the scientifically intresting targets that
+are embedded in it. For example the magnitudes, positions and elliptical
+properties of the galaxies that are in the image. MakeCatalog is Gnuastro's
+program to derive higher-level information for @emph{pre-defined} regions
+of a dataset. The role of MakeCatalog in a scientific analysis and the
+benefits of this model of data-analysis (were detection/identification is
+separated from measurement) is discussed in
address@hidden://arxiv.org/abs/1611.06387v1, Akhlaghi [2016]}. We strongly
+recommend reading this short paper for a better understanding of this
+methodology and use MakeCatalog most effectively. However, that paper
+cannot undergo any more change, so this manual is the definitive guide.
+
+As discussed above, you have to define the regions of a dataset that you
+are interested in @emph{before} running MakeCatalog. MakeProfiles currently
+uses labeled dataset(s) for this job. A labeled dataset for a given input
+dataset has the size/dimensions as the input, but its pixels have an
+integer type (see @ref{Numeric data types})@footnote{If the program you
+used to generate the labeled image only outputs floating point types, but
+you know it only has integer valued pixels that are stored in a floating
+point container, you can use Gnuastro's Arithmetic program (see
address@hidden) to change the numerical data type of the image
+(@file{flabel.fits}) to an integer type image (@file{label.fits}) with a
+command like below:@address@hidden astarithmetic flabel.fits int32
+--output=label.fits}}: all pixels with the same label (integers larger and
+equal to one) are used to generate the requested output columns of
+MakeCatalog for the row of their labeled value. For example, the flux
+weighted average position of all the pixels with a label of 42 will be
+considered as the central address@hidden @ref{Measuring elliptical
+parameters} for a discussion on this and the derivation of positional
+parameters.} of the 42nd row of the output catalog. Pixels with labels
+equal to or smaller than zero will be ignored by MakeCatalog. In other
+words, the number of rows of the output catalog will be determined from the
+labeled image.
+
+The labeled image maybe created with any address@hidden example, you can
+even use a graphic user interfase image editing tool like the GNU Image
+Manipulation Program (or GIMP) and use Gnuastro's ConvertType to convert it
+to a FITS file.}. Within Gnuastro you can use these two solutions depending
+on a-priori/parametric knowledge of the targets you want to study:
address@hidden
address@hidden Aperture photometry
address@hidden Photometry, aperture
address@hidden
+When you already know the positions and parametric (for example circular or
+elliptical) properties of the targets, you can use @ref{MakeProfiles} to
+generate a labeled image from another catalog. This is also known as
+aperture photometry (the apertures are defined a-priori).
+
address@hidden
+When the shape of your targets cannot be parametrized accurately (for
+example galaxies), or if you don't know the number/shape of the targets in
+the image, you can use Gnuastro's NoiseChisel program to detect and segment
+(make labeled images of) the @emph{objects} and @emph{clumps} in the input
+image, see @ref{NoiseChisel}.
address@hidden itemize
+
address@hidden
+* Detection and catalog production::  Discussing why/how to treat these 
separately
+* Quantifying measurement limits::  For comparing different catalogs.
+* Measuring elliptical parameters::  Estimating elliptical parameters.
+* Adding new columns to MakeCatalog::  How to add new columns.
+* Invoking astmkcatalog::       Options and arguments to MakeCatalog.
address@hidden menu
+
address@hidden Detection and catalog production, Quantifying measurement 
limits, MakeCatalog, MakeCatalog
address@hidden Detection and catalog production
+
+As discussed above (@ref{MakeCatalog}), NoiseChisel (Gnuastro's signal
+detection tool, see @ref{NoiseChisel}) does not produce any catalog of the
+detected objects. However, most other common tools in astronomical
+data-analysis (for example
address@hidden@url{https://www.astromatic.net/software/sextractor}})
+merge the two processes into one. Gnuastro's modularized methodology is
+therefore new to many experienced astronomers and deserves a short review
+here. Further discussion on the benefits of this methodology can be seen in
address@hidden://arxiv.org/abs/1611.06387v1, Akhlaghi [2016]}.
+
+To simplify catalog production from a raw input image in Gnuastro,
+NoiseChisel's output (see @ref{NoiseChisel output}) can be directly fed
+into MakeCatalog. This is good when no further customization is necessary
+and you want a fast/simple. But the modular approach taken by Gnuastro has
+many benefits that will become more apparent as you get more experienced in
+astronomical data analysis and want to be more creative in using your
+valuable data for the exciting scientific project you are working on. In
+short the reasons for this modularity can be classified as below:
 
 @itemize
 
 @item
-Complexity: Adding in a catalog functionality to the detector program
-will add several more steps (and options) to its processing that can
-equally well be done outside of it. This makes following the code
-harder for a curious reader and also potentially adds bugs.
-
-Another advantage of less complexity is when the parameter you want to
-measure over one profile is not provided by the developers of
-MakeCatalog. You can simply open this tiny little program and add your
-desired calculation easily. In @ref{Adding new columns to
-MakeCatalog}, we have explained all the necessary steps to add a new
-column to MakeCatalog. However, if making a catalog was part of
-NoiseChisel, it would require a lot of energy to understand all the
-steps in order to add desired parameter.
+Complexity of a monolith: Adding in a catalog functionality to the detector
+program will add several more steps (and many more options) to its
+processing that can equally well be done outside of it. This makes
+following what the program does harder for the users and developers, it can
+also potentially add many bugs.
+
+As an example, if the parameter you want to measure over one profile is not
+provided by the developers of MakeCatalog. You can simply open this tiny
+little program and add your desired calculation easily. This process is
+discussed in @ref{Adding new columns to MakeCatalog}. However, if making a
+catalog was part of NoiseChisel, it would require a lot of energy to
+understand all the steps and internal structures of that large program (the
+most complex in Gnuastro) in order to add desired parameter in a catalog.
 
 @item
-Low level nature of Gnuastro: Making catalogs is a separate process
-from labeling (detecting and segmenting) the pixels. A user might want
-to do certain operations on the labeled regions before creating a
-catalog for them. Another user might want the properties of the same
-pixels in another image (possibly from another broadband filter) for
-measuring the colors or SEDs for example.
+Simplicity/robustness of independent, modular tools: making a catalog is a
+logically separate process from labeling (detection and segmentation). A
+user might want to do certain operations on the labeled regions before
+creating a catalog for them. Another user might want the properties of the
+same pixels/objects in another image (another filter for example) to
+measure the colors or SED fittings.
 
 Here is an example of doing both: suppose you have images in various
 broad band filters at various resolutions and orientations. The image
@@ -12685,49 +12854,52 @@ of one color will thus not lie exactly on another or 
even be in the
 same scale. However, it is imperative that the same pixels be used in
 measuring the colors of galaxies.
 
-Therefore NoiseChisel can be run on the reference image and Warp
-(@ref{Warp}) can be applied to the labeled images to find the pixels to use
-in the other image. Then MakeCatalog can generate the final catalog for
-both targets. It is currently customary to warp the images to the same
-pixel grid, however, this is very harmful for the data and creates
-correlated noise. It is much more accurate to do the transformations on the
-labeled image.
+To solve the problem, NoiseChisel can be run on the reference image to
+generate the labeled image. After wards, the labeled image can be warped
+into the grid of the other color (using @ref{Warp}). MakeCatalog will then
+generate the same catalog for both colors (with the different labeled
+images). It is currently customary to warp the images to the same pixel
+grid, however, modification of the scientific dataset is very harmful for
+the data and creates correlated noise. It is much more accurate to do the
+transformations on the labeled image.
 
 @end itemize
 
 
address@hidden
-* Quantifying data limits::     For comparing different catalogs.
-* Measuring elliptical parameters::  Estimating elliptical parameters.
-* Invoking astmkcatalog::       Options and arguments to MakeCatalog.
-* Adding new columns to MakeCatalog::  How to add new columns.
address@hidden menu
 
 
 
 
address@hidden Quantifying data limits, Measuring elliptical parameters, 
MakeCatalog, MakeCatalog
address@hidden Quantifying data limits
+
address@hidden Quantifying measurement limits, Measuring elliptical parameters, 
Detection and catalog production, MakeCatalog
address@hidden Quantifying measurement limits
 
 @cindex Depth
 @cindex Clump magnitude limit
 @cindex Object magnitude limit
 @cindex Limit, object/clump magnitude
 @cindex Magnitude, object/clump detection limit
-Different datasets (images in the case of MakeCatalog) have different noise
-properties and different detection methods (or one method with a different
-set of parameters) will have different abilities to detect or measure
-certain kinds of objects in an image. Therefore it is very important to
-quantify our ability to detect and measure signal in noise. In this section
-we discuss these limits that are very important in any analysis.
-
-In astronomy, it is common to use the magnitude (a unit-less scale) and
+A meaningful scientific analysis requires knowledge of the limits of your
+dataset and detection/measurement tools: different datasets (images in the
+case of MakeCatalog) have different noise properties and different
+detection methods (or one method that is run with a different set of
+parameters) will have different abilities to detect or measure certain
+kinds of objects and their properties in an image. Therefore quantifying
+the target detection and measurement limitations with the particular
+dataaset and analysis tools will be the most crutial aspect of any
+analysis.
+
+In this section we discuss some of the most general limits that are very
+important in any astronomical data analysis and how MakeCatalog makes it
+easy to find them. Depending on the higher-level analysis, there are more
+tests that must be done, but these are usually necessary in any case. In
+astronomy, it is common to use the magnitude (a unit-less scale) and
 physical units, see @ref{Flux Brightness and magnitude}. Therefore all the
-measured discussed here are commonly defined in units of magnitudes.
+measurements discussed here are defined in units of magnitudes.
 
 @table @asis
 
address@hidden Depth
address@hidden Dataset's depth
 As we make more observations on one region of the sky and add the images
 over each other, we are able to decrease the standard deviation of the
 noise in each address@hidden is true for any noisy data, not just
@@ -12751,7 +12923,7 @@ you wait. The summits of hills begin to appear. As the 
depth of clear water
 increases, the parts of the hills with lower heights (less surface
 brightness) can be seen more clearly.
 
address@hidden Depth
address@hidden Data's depth
 The outputs of NoiseChisel include the Sky standard deviation
 (@mymath{\sigma}) on every group of pixels (a mesh) that were calculated
 from the undetected pixels in that mesh, see @ref{Tessellation} and
@@ -12779,7 +12951,41 @@ a single orbit image, this same field has a depth of 
31.32. Recall that a
 larger magnitude corresponds to less brightness, see @ref{Flux Brightness
 and magnitude}.
 
address@hidden Upper limit magnitude
address@hidden Target magnitude measurement error
+Any measurement has an error and this includes the derived magnitude for an
+object. Note that this value is only meaningful when the object's magnitude
+is brighter than the upper-limit magnitude (see the next items in this
+list). As discussed in @ref{Flux Brightness and magnitude}, the magnitude
+(@mymath{M}) of an object with brightness @mymath{B} and Zeropoint
+magnitude @mymath{Z} can be written as:
+
address@hidden(B)+Z}
+
address@hidden
+Calculating the derivative with respect to @mymath{B}, we get:
+
address@hidden dB} = {-2.5\over {B\times ln(10)}}}
+
address@hidden
+From the Tailor series (@mymath{\Delta{M}=dM/dB\times\Delta{B}}), we can
+write:
+
address@hidden = \left|{-2.5\over ln(10)}\right|\times{\Delta{B}\over{B}}}
+
address@hidden
+But, @mymath{\Delta{B}/B} is just the inverse of the Signal-to-noise
+ratio (@mymath{S/N}), so we can write the error in magnitude in terms of
+the signal-to-noise ratio:
+
address@hidden \Delta{M} = {2.5\over{S/N\times ln(10)}} }
+
+MakeCatalog uses this relation to estimate the magnitude errors. The
+signal-to-noise ratio is calculated in different ways for clumps and
+objects (see @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa
+[2015]}), but this single equation can be used to estimate the measured
+magnitude error afterwards for any type of target.
+
address@hidden Target's upper limit magnitude
 Due to the noisy nature of data, it is possible to get arbitrarily low
 values for a faint object's brightness (or arbitrarily high
 magnitudes). Given the scatter caused by the noise, such small values are
@@ -12826,7 +13032,7 @@ certain patters, so the shape of the object can also 
affect the result. So
 in MakeCatalog, the upper limit magnitude is found for each object in the
 image separately. Not one value for the whole image.
 
address@hidden Completeness limit
address@hidden Object's completeness limit
 @cindex Completeness
 As the surface brightness of the objects decreases, the ability to detect
 them will also decrease. An important statistic is thus the fraction of
@@ -12860,126 +13066,120 @@ model profiles as similar to the target of interest 
as possible.
 
 
 
address@hidden Measuring elliptical parameters, Invoking astmkcatalog, 
Quantifying data limits, MakeCatalog
address@hidden Measuring elliptical parameters, Adding new columns to 
MakeCatalog, Quantifying measurement limits, MakeCatalog
 @subsection Measuring elliptical parameters
 
-One of the most important class of parameters that are important for
-astronomical image analysis is the shape or morphology of the
-objects. In the case of galaxies (with a very rich variety of
-morphologies) studying the morphology of the detected objects is its
-self a is very complicated research subject which is a very active
-field of research now. So in this section, we will just discuss how
-the most basic morphological parameters are estimated: the elliptical
-parameters for a set of labeled pixels (that will be studied together
-and define our object of interest). These are: the major axis, the
-minor axis and the position angle along with the central position of
-the profile. The derivations below follow the SExtractor manual
-derivations but include more explanations.
+The shape or morphology of a target is one of the most commonly desired
+paramters of a target. Here, we will review the derivation of the most
+basic/simple morphological parameters are estimated: the elliptical
+parameters for a set of labeled pixels. The elliptical parameters are: the
+(semi-)major axis, the (semi-)minor axis and the position angle along with
+the central position of the profile. The derivations below follow the
+SExtractor manual derivations with some added explanations for easier
+reading.
 
 @cindex Moments
 Let's begin with one dimension for simplicity: Assume we have a set of
address@hidden values (brightness for example) @mymath{B_i}, each at
-position @mymath{x_i}. The simplest parameter we can define is the
-geometric center of the object (@mymath{x_g}) (ignoring the brightness
-values): @mymath{x_g=(\sum_ix_i)/N}. @emph{Moments} are defined to
-incorporate both the value (brightness) and position of the data. The
-first moment can be written as:
address@hidden values @mymath{B_i} (keeping the spatial distribution of
+brightness for example), each at position @mymath{x_i}. The simplest
+parameter we can define is the geometric center of the object
+(@mymath{x_g}) (ignoring the brightness values):
address@hidden(\sum_ix_i)/N}. @emph{Moments} are defined to incorporate both
+the value (brightness) and position of the data. The first moment can be
+written as:
 
 @dispmath{\overline{x}={\sum_iB_ix_i \over \sum_iB_i}}
 
 @cindex Variance
 @cindex Second moment
 @noindent
-This is essentially the weighted (by @mymath{B_i}) average
-position. The geometric center (@mymath{x_g}, defined above) is a
-special case of this with all @mymath{B_i=1}. The second moment is
-essentially the variance of the distributions:
+This is essentially the weighted (by @mymath{B_i}) mean position. The
+geometric center (@mymath{x_g}, defined above) is a special case of this
+with all @mymath{B_i=1}. The second moment is essentially the variance of
+the distribution:
 
address@hidden(B_ix_i-\overline{x})^2 \over
-        \sum_iB_i} ={\sum_iB_i^2x_i^2 \over \sum_iB_i} -
address@hidden(x_i-\overline{x})^2 \over
+        \sum_iB_i} = {\sum_iB_ix_i^2 \over \sum_iB_i} -
         2\overline{x}{\sum_iB_ix_i\over\sum_iB_i} + \overline{x}^2
-        ={\sum_iB_i^2x_i^2 \over \sum_iB_i} - \overline{x}^2}
+        ={\sum_iB_ix_i^2 \over \sum_iB_i} - \overline{x}^2}
 
 @cindex Standard deviation
 @noindent
-The last step was done from the definition of
address@hidden So the square root of @mymath{\overline{x^2}}
-is the positional standard deviation (along the one-dimensional) of
-this particular brightness distribution (@mymath{B_i}). Crudely (or
-qualitatively), you can think of its square root as the distance (from
address@hidden) which contains a specific amount of the flux
-(depending on the @mymath{B_i} distribution). Similar to the first
-moment, the geometric second moment can be found by setting all
address@hidden So while the first moment quantified the position of
-the brightness distribution, the second moment quantifies how that
-brightness is dispersed about the first moment. In other words, it
-quantifies how ``sharp'' the object's image is.
+The last step was done from the definition of @mymath{\overline{x}}. Hence,
+the square root of @mymath{\overline{x^2}} is the spatial standard
+deviation (along the one-dimensional) of this particular brightness
+distribution (@mymath{B_i}). Crudely (or qualitatively), you can think of
+its square root as the distance (from @mymath{\overline{x}}) which contains
+a specific amount of the flux (depending on the @mymath{B_i}
+distribution). Similar to the first moment, the geometric second moment can
+be found by setting all @mymath{B_i=1}. So while the first moment
+quantified the position of the brightness distribution, the second moment
+quantifies how that brightness is dispersed about the first moment. In
+other words, it quantifies how ``sharp'' the object's image is.
 
 @cindex Floating point error
-Before continuing to two dimensions and the derivation of the
-elliptical parameters we will pause for an implementation
-technicality. You can ignore this paragraph if you don't want to
-implement these concepts. The basic definition (first fraction for
address@hidden) can be used without any major
-problem. However, using this fraction requires two runs over the data:
-one run to specify @mymath{\overline{x}} and one run for
address@hidden, this can be slow. However, using the last
-fraction above, we can estimate both the first and second moments in
-one run (since the @mymath{-\overline{x}^2} term can easily be added
-later). Using the last form creates a major technical issue however:
-due to the floating point accuracy. Suppose the object is located
-between pixels 10000 and 10020. While the object pixels are only
-distributed over 20 pixels (with a standard deviation @mymath{<20}),
-the mean has a value of @mymath{\sim10000}. The
address@hidden will go to very very large values while the
-individual pixel differences will be much smaller, this will lower the
-accuracy of our calculation due to the limited accuracy of floating
-point operations. Since the variance only depends on the distance of
-each point from the mean, and for a constant/arbitrary @mymath{K},
address@hidden, we can calculate the second
-order moment using:
-
address@hidden(x_i-K)^2 \over \sum_iB_i} -
-             (\overline{x}-K)^2}
+Before continuing to two dimensions and the derivation of the elliptical
+parameters, let's pause for an important implementation technicality. You
+can ignore this paragraph if you don't want to implement these
+concepts. The basic definition (first fraction for @mymath{\overline{x^2}})
+can be used without any major problem. However, using this fraction
+requires two runs over the data: one run to find @mymath{\overline{x}} and
+one run to find @mymath{\overline{x^2}}, this can be slow. However, using
+the last fraction above, we can estimate both the first and second moments
+in one run (since the @mymath{-\overline{x}^2} term can easily be added
+later). The logarithmic nature of floating point number digitization
+creates a complication in this approach: suppose the object is located
+between pixels 10000 and 10020. Hence the target's pixels are only
+distributed over 20 pixels (with a standard deviation @mymath{<20}), while
+the mean has a value of @mymath{\sim10000}. The @mymath{\sum_iB_i^2x_i^2}
+will go to very very large values while the individual pixel differences
+will be much smaller, this will lower the accuracy of our calculation due
+to the limited accuracy of floating point operations. The variance only
+depends on the distance of each point from the mean, so we can shift all
+position by a constant/arbitrary @mymath{K} which is much closer to the
+mean: @mymath{\overline{x-K}=\overline{x}-K}. Hence we can calculate the
+second order moment using:
+
address@hidden \overline{x^2}={\sum_iB_i(x_i-K)^2 \over \sum_iB_i} -
+           (\overline{x}-K)^2 }
 
 @noindent
-The closer @mymath{K} is to @mymath{\overline{x}}, the better (the
-sums of squares will involve smaller numbers), as long as @mymath{K}
-is within the object limits (in the example above:
address@hidden), the floating point error induced in
-our calculation will be negligible. For the most simplest
-implementation, in order to find the second moments (variance) in each
-dimension, MakeCatalog takes @mymath{K} to be the position of the
-first pixel that is assigned to the object under each dimension. Since
address@hidden is arbitrary and an implementation/technical detail, we
-will ignore it for the remainder of this discussion.
+The closer @mymath{K} is to @mymath{\overline{x}}, the better (the sums of
+squares will involve smaller numbers), as long as @mymath{K} is within the
+object limits (in the example above: @mymath{10000\leq{K}\leq10020}), the
+floating point error induced in our calculation will be negligible. For the
+most simplest implementation, MakeCatalog takes @mymath{K} to be the
+smallest position of the object in each dimension. Since @mymath{K} is
+arbitrary and an implementation/technical detail, we will ignore it for the
+remainder of this discussion.
 
 In two dimensions, the mean and variances can be written as:
 
 @dispmath{\overline{x}={\sum_iB_ix_i\over B_i}, \quad
-          \overline{x^2}={\sum_iB_i^2x_i^2 \over \sum_iB_i} -
+          \overline{x^2}={\sum_iB_ix_i^2 \over \sum_iB_i} -
           \overline{x}^2}
 @dispmath{\overline{y}={\sum_iB_iy_i\over B_i}, \quad
-          \overline{y^2}={\sum_iB_i^2y_i^2 \over \sum_iB_i} -
+          \overline{y^2}={\sum_iB_iy_i^2 \over \sum_iB_i} -
           \overline{y}^2}
 @dispmath{\quad\quad\quad\quad\quad\quad\quad\quad\quad
-          \overline{xy}={\sum_iB_i^2x_iy_i \over \sum_iB_i} -
+          \overline{xy}={\sum_iB_ix_iy_i \over \sum_iB_i} -
           \overline{x}\times\overline{y}}
 
-If an elliptical profile's major axis lies exactly along the
address@hidden axis, then @mymath{\overline{x^2}} will be directly
-proportional with the major axis, @mymath{\overline{y^2}} with its
-minor axis and @mymath{\overline{xy}=0}. However, in reality we are
-not that lucky and (assuming galaxies can be parametrized as an
-ellipse) the major axis of galaxies can be in any direction on the
-image (in fact this is one of the core principles behind weak-lensing
-by shear estimation). So the purpose of the remainder of this section
-is to define a strategy to measure the position angle and axis ratio
-of some randomly positioned ellipses in an image, using the raw second
-moments that we have calculated above in our image coordinates.
-
-Let's assume we have rotated the galaxy by @mymath{\theta}, then the
-new second order moments can be written as:
+If an elliptical profile's major axis exactly lies along the @mymath{x}
+axis, then @mymath{\overline{x^2}} will be directly proportional with the
+profile's major axis, @mymath{\overline{y^2}} with its minor axis and
address@hidden However, in reality we are not that lucky and
+(assuming galaxies can be parametrized as an ellipse) the major axis of
+galaxies can be in any direction on the image (in fact this is one of the
+core principles behind weak-lensing by shear estimation). So the purpose of
+the remainder of this section is to define a strategy to measure the
+position angle and axis ratio of some randomly positioned ellipses in an
+image, using the raw second moments that we have calculated above in our
+image coordinates.
+
+Let's assume we have rotated the galaxy by @mymath{\theta}, the new second
+order moments are:
+
 @dispmath{\overline{x_\theta^2} = \overline{x^2}\cos^2\theta +
            \overline{y^2}\sin^2\theta -
            2\overline{xy}\cos\theta\sin\theta }
@@ -12989,9 +13189,11 @@ new second order moments can be written as:
 @dispmath{\overline{xy_\theta} = \overline{x^2}\cos\theta\sin\theta -
            \overline{y^2}\cos\theta\sin\theta +
            \overline{xy}(\cos^2\theta-\sin^2\theta)}
-We can now find the best @mymath{\theta} (@mymath{\theta_0}) such that
-the major axis lies along the @mymath{x_\theta} axis. This can be
-found by setting:
+
address@hidden
+The best @mymath{\theta} (@mymath{\theta_0}, where major axis lies
+along the @mymath{x_\theta} axis) can be found by:
+
 @dispmath{\left.{\partial \overline{x_\theta^2} \over \partial 
\theta}\right|_{\theta_0}=0}
 Taking the derivative, we get:
 @dispmath{2\cos\theta_0\sin\theta_0(\overline{y^2}-\overline{x^2}) +
@@ -12999,6 +13201,7 @@ Taking the derivative, we get:
 @mymath{\overline{x^2}\neq\overline{y^2}}, we can write:
 @dispmath{\tan2\theta_0 =
 2{\overline{xy} \over \overline{x^2}-\overline{y^2}}.}
+
 @cindex Position angle
 @noindent
 MakeCatalog uses the standard C math library's @code{atan2} function
@@ -13020,17 +13223,112 @@ get the semi-major (@mymath{A}) and semi-minor 
(@mymath{B}) lengths:
 @dispmath{B^2\equiv\overline{y_{\theta_0}^2}= {\overline{x^2} +
 \overline{y^2} \over 2} - \sqrt{\left({\overline{x^2}-\overline{y^2} \over 
2}\right)^2 + \overline{xy}^2}}
 
-So as a summary, it is important to remember that the units of
address@hidden and @mymath{B} are in pixels (same as the standard
-deviation) and that they represent the spatial light distribution of
-the object in both image dimensions if the object could be
-approximated as an ellipse. When the object cannot be represented as
-an ellipse, this interpretation breaks down:
address@hidden and
address@hidden will not be the direction of
-minimum variance.
-
address@hidden Invoking astmkcatalog, Adding new columns to MakeCatalog, 
Measuring elliptical parameters, MakeCatalog
+As a summary, it is important to remember that the units of @mymath{A} and
address@hidden are in pixels (the standard deviation of a positional
+distribution) and that they represent the spatial light distribution of the
+object in both image dimensions (rotated by @mymath{\theta_0}). When the
+object cannot be represented as an ellipse, this interpretation breaks
+down: @mymath{\overline{xy_{\theta_0}}\neq0} and
address@hidden will not be the direction of minimum
+variance.
+
+
+
+
+
address@hidden Adding new columns to MakeCatalog, Invoking astmkcatalog, 
Measuring elliptical parameters, MakeCatalog
address@hidden Adding new columns to MakeCatalog
+
+MakeCatalog is designed to allow easy addition of different measurements
+over a labeled image (see @url{https://arxiv.org/abs/1611.06387v1, Akhlaghi
+[2016]}). A check-list style description of necessary steps to do that is
+described in this section. The common development characteristics of
+MakeCatalog and other Gnuastro programs is explained in
address@hidden We strongly encourage you to have a look at that chapter
+to greatly simplify your navigation in the code. After adding and testing
+your column, you are most welcome (and encouraged) to share it with us so
+we can add to the next release of Gnuastro for everyone else to also
+benefit from your efforts.
+
+MakeCatalog will first pass over each label's pixels two times and do
+necessary raw/internal calculations. Once the passes are done, it will use
+the raw information for filling the final catalog's columns. In the first
+pass it will gather mainly object information and in the second run, it
+will mainly focus on the clumps, or any other measurement that needs an
+output from the first pass. These two passes are designed to be raw
+summations: no extra processing. This will allow parallel processing and
+simplicity/clarity. So if your new calculation, needs new raw information
+from the pixels, then you will need to also modify the respective
address@hidden and @code{mkcatalog_second_pass} functions
+(both in @file{bin/mkcatalog/mkcatalog.c}) and define new raw table columns
+in @file{main.h} (hopefully the comments in the code are clear enough).
+
+In all these different places, the final columns are sorted in the same
+order (same order as @ref{Invoking astmkcatalog}). This allows a particular
+column/option to be easily found in all steps. Therefore in adding your new
+option, be sure to keep it in the same relative place in the list in all
+the separate places (it doesn't necessarily have to be in the end), and
+near conceptually similar options.
+
address@hidden @file
+
address@hidden main.h
+The @code{objectcols} and @code{clumpcols} enumerated variables
+(@code{enum}) define the raw/internal calculation columns. If your new
+column requires new raw calculations, add a row to the respective list. If
+your calculation requires any other settings paramters, you should add a
+variable to the @code{mkcatalogparams} structure.
+
address@hidden ui.h
+The @code{option_keys_enum} associates a unique value for each option to
+MakeProfiles. The options that have a short option version, the single
+character short comment is used for the value. Those that don't have a
+short option version, get a large integer automatically. You should add a
+variable here to identify your desired column.
+
address@hidden GNU C library
address@hidden args.h
+This file specifies all the parameters for the GNU C library, Argp
+structure that is in charge of reading the user's options. To define your
+new column, just copy an existing set of parameters and change the first,
+second and 5th values (the only ones that differ between all the columns),
+you should use the macro you defined in @file{ui.h} here.
+
address@hidden ui.c
+If your column includes any particular settings (you added a variable to
+the @code{mkcatalogparams} structure in @file{main.h}), you should do the
+sanity checks and preparations for it here. Otherwise, you can ignore this
+file.
+
address@hidden columns.c
+This file will contain the main defintion and high-level calculation of
+your new column through the @code{columns_define_alloc} and
address@hidden functions. In the first, you specify the basic
+information about the column: its name, units, comments, type (see
address@hidden data types}) and how it should be printed if the output is a
+text file. You should also specify the raw/internal columns that are
+necessary for this column here as the many existing examples show. Through
+the types for objects and rows, you can specify if this column is only for
+clumps, objects or both.
+
+The second main function (@code{columns_fill}) writes the final value into
+the appropriate column for each object and clump. As you can see in the
+many existing examples, you can define your processing on the raw/internal
+calculations here and save them in the output.
+
address@hidden mkcatalog.c
+As described before, this file contains the two main MakeCatalog
+work-horses: @code{mkcatalog_first_pass} and @code{mkcatalog_second_pass},
+their names are descriptive enough and their internals are also clear and
+heavily commented.
+
address@hidden table
+
+
+
+
+
address@hidden Invoking astmkcatalog,  , Adding new columns to MakeCatalog, 
MakeCatalog
 @subsection Invoking MakeCatalog
 
 MakeCatalog will make a catalog from an input image and at least on labeled
@@ -13046,98 +13344,147 @@ One line examples:
 
 @example
 $ astmkcatalog -mdri input.fits
-$ astmkcatalog --floatprecision=5 input.fits
 $ astmkcatalog --output=cat input_labeled.fits
-$ astmkcatalog --objlabs=K_labeled.fits --objhdu=1 \
-               --clumplabs=K_labeled.fits --clumphdu=2 i_band.fits
+$ astmkcatalog --config=columns.conf input_labeled.fits
+$ astmkcatalog --objectsfile=K_labeled.fits --objectshdu=1    \
+               --clumpsfile=K_labeled.fits --clumpshdu=2 i_band.fits
 @end example
 
 @cindex Gaussian
 @noindent
 If MakeCatalog is to do processing, an input image should be provided with
-the recognized extensions as input data, see @ref{Arguments}. Optionally a
-mask file can be specified to ignore some of the pixels in the image. The
-options common to all Gnuastro programs are explained in @ref{Common
-options}.
+the recognized extensions as input data, see @ref{Arguments}. The options
+described in this section are those that are only particular to
+MakeProfiles. For operations that MakeProfiles shares with other programs
+(mainly involving input/output or general processing steps), see
address@hidden options}. Also see @ref{Common program behavior} for some
+general characteristics of all Gnuastro programs including MakeCatalog.
 
 MakeCatalog needs 4 (or 5) images as input. These images can be separate
 extensions in one file (NoiseChisel's default output), or each can have its
-own file and its own extension. The full 5 images are listed in
address@hidden output}. However, the clump labels image is not mandatory
-(when no clump catalog is required, for example in aperture
-photometry). When inspecting the object labels image, MakeProfiles will
-look for a @code{WCLUMPS} (short for with-clumps) header keyword. If that
-keyword is present and has a value of @code{yes} (not case sensitive, so
address@hidden, or @code{YES} are also acceptable) then a clump image must also
-be provided and a clump catalog will be made. When @code{WCLUMPS} isn't
-present or has any other value, only an object catalog will be created and
-all clump related options will be ignored.
+own file and its own extension. See @ref{NoiseChisel output} for the
+list. The clump labels image is not mandatory (when no clump catalog is
+required, for example in aperture photometry). When inspecting the object
+labels image, MakeProfiles will look for a @code{WCLUMPS} (short for
+with-clumps) header keyword. If that keyword is present and has a value of
address@hidden, @code{1}, or @code{y} (case insensitive) then a clump image
+must also be provided and a clump catalog will be made. When @code{WCLUMPS}
+isn't present or has any other value, only an object catalog will be
+created and all clump related options/columns will be ignored.
 
 @cindex Photometry, aperture
 @cindex Aperture photometry
 For example, if you only need an object catalog from NoiseChisel's output,
-you can use Fits (see @ref{Fits}) to modify or remove the
address@hidden keyword in the objects HDU, then run MakeCatalog on
+you can use Gnuastro's Fits program (see @ref{Fits}) to modify or remove
+the @code{WCLUMPS} keyword in the objects HDU, then run MakeCatalog on
 it. Another example can be aperture photometry: let's assume you have made
 your labeled image (defining the apertures) with MakeProfiles. Clumps are
 not defined in this context, so besides the input and labeled image, you
-only need NoiseChisel's Sky and Sky standard deviation images. Since
-MakeProfile's output doesn't contain the @code{WCLUMPS} keyword, you just
-have to specify your labeled image with the @option{--objlabs} option and
-also set its HDU and no clumps catalog will be created. Note that labeled
-images have to be an integer type. Therefore, when using MakeProfiles to
-define the apertures/labels, you can use @option{--type=long} for example.
+only need NoiseChisel's Sky and Sky standard deviation images (run
+NoiseChisel with the @option{--onlydetection} option). Since MakeProfile's
+output doesn't contain the @code{WCLUMPS} keyword, you just have to specify
+your labeled image with the @option{--objectsfile} option and also set its
+HDU. Note that labeled images have to be an integer type. Therefore, if you
+are using MakeProfiles to define the apertures/labels, you can use its
address@hidden for example, see @ref{Input output options} and
address@hidden data types}.
 
 When a clump catalog is also desired, two catalogs will be made: one for
-the objects (suffixed with @file{_o.txt}) and another for the clumps
-(suffixed with @file{_c.txt}). Therefore if any value is given to the
address@hidden option, MakeCatalogs will replace these two suffixes with
-any existing suffix in the given value. If no output value is given,
-MakeCatalog will use the input name, see @ref{Automatic output}. When only
-building an object catalog, any suffix in the value to output will just be
-replaced with @file{.txt}.
-
-The first set of options specify the properties of the inputs. If no name
-is specified for the other necessary input images, their HDU is checked and
-if that differs from the input HDU, then there is no need to specify a file
-name for them. The object and column label images or segmentation maps
-should not be of a floating point type (@code{BITPIX}).
+the objects (suffixed with @file{_o.txt} or @file{_o.fits}) and another for
+the clumps (suffixed with @file{_c.txt} or @file{_c.fits}). Therefore if
+any value is given to the @option{--output} option, MakeCatalogs will
+replace these two suffixes with any existing suffix in the given value. If
+no output value is given, MakeCatalog will use the input name, see
address@hidden output}. The format of the output table is specified with
+the @option{--tableformat} option, see @ref{Input output options}.
+
+When MakeCatalog is run on multiple threads, the clumps catalog rows will
+not be sorted by object since each object is processed independently by one
+thread and threaded applications are asynchronous. The clumps in each
+object will be sorted based on their labels, but you will find lower-index
+objects come after higher-index ones (especially if they have more clumps
+and thus take more time). If the order is very important for you, you can
+run the following command to sort the rows by object ID (and clump ID with
+each object):
 
address@hidden @option
address@hidden
+$ awk '!/^#/' out_c.txt | sort -g -k1,1 -k2,2
address@hidden example
 
address@hidden -O STR
address@hidden --objlabs=STR
-The file name of the object labels, if in the same file as input, it is not
-mandatory.
address@hidden
+* MakeCatalog input files::     Specifying the different input files.
+* MakeCatalog general settings::  Options for general column settings.
+* Upper-limit magnitude settings::  Necessary to define upper-limit magnitudes.
+* MakeCatalog output columns::  How to specify the columns in the output.
address@hidden menu
 
address@hidden --objhdu=STR
-The HDU of the object labels image, the header keyword @code{NOBJS} must be
-present in this extension. The value to this keyword is used as the final
-number of objects and the number of rows in the output objects
-catalog. Only pixels with values above zero will be considered.
address@hidden MakeCatalog input files, MakeCatalog general settings, Invoking 
astmkcatalog, Invoking astmkcatalog
address@hidden MakeCatalog input files
 
address@hidden -c STR
address@hidden --clumplabs=STR
-Similar to @option{--objlabs} but for the labels of the clumps.
+MakeCatalog needs multiple images as input: a values image, one (or two)
+labeled images and Sky and Sky standandard deviation images. The options
+described in this section allow you to identify them. If you use the
+default output of NoiseChisel (see @ref{NoiseChisel output}) you don't have
+to worry about any of these options and just give NoiseChisel's output file
+to MakeCatalog as described in @ref{Invoking astmkcatalog}.
 
address@hidden --clumphdu=STR
-Similar to @option{--objhdu}, but for the clumps. The @code{NCLUMPS}
-keyword in this header specifies the number of recognized clumps.
address@hidden @option
+
address@hidden -O STR
address@hidden --objectsfile=STR
+The file name of the object labels image, if the image is in another
+extension of the input file, calling this option is not mandatory, just
+specify the extension/HDU with the @option{--objectshdu} option.
+
address@hidden --objectshdu=STR
+The HDU/extension of the object labels image. Only pixels with values above
+zero will be considered. The objects label image has to be an integer data
+type (see @ref{Numeric data types}) and only pixels with a value larger
+than zero will be used. If this extension contains teh @code{WCLUMPS}
+keyword with a value of @code{yes}, @code{1}, or @code{y} (not case
+sensitive), then MakeCatalog will also build a clumps catalog, see
address@hidden astmkcatalog}.
+
address@hidden -C STR
address@hidden --clumpsfile=STR
+Similar to @option{--objlabs} but for the labels of the clumps. This is
+only necessary if the image containing clump labels is not in the input
+file and the objects image has a @code{WCLUMPS} keyword, see
address@hidden
+
address@hidden --clumpshdu=STR
+The HDU/extension of the object labels image. Only pixels with values above
+zero will be considered. The objects label image has to be an integer data
+type (see @ref{Numeric data types}) and only pixels with a value larger
+than zero will be used.
 
 @item -s STR
address@hidden --skyfilename=STR
address@hidden --skyfile=STR
 File name of an image keeping the Sky value for each pixel.
 
 @item --skyhdu=STR
 The HDU of the Sky value image.
 
 @item -t STR
address@hidden --stdfilename=STR
address@hidden --stdfile=STR
 File name of image keeping the Sky value standard deviation for each pixel.
 
 @item --stdhdu=STR
 The HDU of the Sky value standard deviation image.
 
address@hidden table
+
+
+
address@hidden MakeCatalog general settings, Upper-limit magnitude settings, 
MakeCatalog input files, Invoking astmkcatalog
address@hidden MakeCatalog general settings
+
+Some of the columns require particular settings (for example the zero point
+magnitdue for measuring magnitudes), the options in this section can be
+used for such configurations.
+
address@hidden @option
+
 @item -z FLT
 @itemx --zeropoint=FLT
 The zero point magnitude for the input image, see @ref{Flux Brightness and
@@ -13145,9 +13492,9 @@ magnitude}.
 
 @item -E
 @itemx --skysubtracted
-If the image has already been sky subtracted by another program, then
-you need to notify MakeCatalog through this option. Note that this is
-only relevant when the Signal to noise ratio is to be calculated.
+If the image has already been sky subtracted by another program, then you
+need to notify MakeCatalog through this option. Note that this is only
+relevant when the Signal to noise ratio is to be calculated.
 
 @item -T FLT
 @itemx --threshold=FLT
@@ -13182,106 +13529,72 @@ C library's @code{strtod} function to read floats, 
which is not
 case-sensative in reading NaN values. But to be consistent, it is good
 practice to only use @option{nan}.
 
address@hidden table
-
address@hidden
-Through the next group of options, you can customize the general
-output plain text catalog. The basic idea behind the options about the
-width and precision of the different types is the fact that some
-columns don't need too much space, while some do. The width is the
-number of text columns given to data of each type in the output plain
-text catalog.
-
-The precision is the number of digits to show after the decimal point
-in floating point numbers. We have defined two types of floating point
-numbers here, one is for less accurate precision, like magnitude,
-while the other is used when more accuracy is necessary. A common
-example of the latter is right ascension and declination. These
-variables usually need to be printed with more than 6 point
-accuracy. Note that all the values are calculated and stored
-internally as double precision floating point numbers, the distinction
-made here is only for printing them.
-
address@hidden @option
-
 @item --nsigmag=FLT
-The magnitude of the value to this option multiplied by the maximum
-standard deviation over the objects or clumps is reported in the output
-catalog. This value is a per-pixel value, not per object and is not found
-over an area or aperture, like the common @mymath{5\sigma} values that are
-commonly reported as a measure of depth. They are based on a certain area
-and are relics from the time of analog data collection and
-processing. Modern tools use digital imaging detectors and the area is that
-of a pixel.
-
address@hidden --intwidth=INT
-The width of printing the integer values. In MakeCatalog, all IDs, numbers
-(counters) and areas are considered to be an integer.
-
address@hidden --floatwidth=INT
-The width of a normal precision floating point column. Any column that is
-not designated in @option{--intwidth} or @option{--accuwidth} is considered
-to be a normal precision floating point.
-
address@hidden --accuwidth=INT
-The width columns to be printed with extra accuracy. In MakeCatalog the
-following columns are printed with extra accuracy: right ascension,
-declination, brightness, river pixel averages (see Akhlaghi and Ichikawa
-2015 for the definition of river pixels), the sky and the sky standard
-deviation.
+The median standard deviation (from the standard deviation image) will be
+multiplied by the value to this option and its magnitude will be reported
+in the comments of the output catalog. This value is a per-pixel value, not
+per object/clump and is not found over an area or aperture, like the common
address@hidden values that are commonly reported as a measure of depth or
+the upper-limit measurements (see @ref{Quantifying measurement
+limits}).
 
address@hidden --floatprecision=INT
-The number of digits to the right of the decimal point in normal floating
-point display.
address@hidden table
 
address@hidden --accuprecision=INT
-The number of digits to the right of the decimal point in more accurate
-floating point display.
 
address@hidden table
address@hidden Upper-limit magnitude settings, MakeCatalog output columns, 
MakeCatalog general settings, Invoking astmkcatalog
address@hidden Upper-limit magnitude settings
+
 
-The upper limit magnitude was discussed in @ref{Quantifying data
+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
+for upper-limit, except for @option{--envseed} that is also present in
 other programs and is general for any job requiring random number
-generation.
+generation (see @ref{Generating random numbers}).
 
 @cindex Reproducibility
 One very important consideration in Gnuastro is reproducibility. Therefore,
 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. One of
-those parameters is the number of threads used in this run of MakeCatalog.
-This might seem irrelevant.
-
-The job of sampling a large number of positions over the image is an
-``embarrasingly parallel'' kind of problem which can greatly benefit from
-threads. For maximum efficiency, the objects in the catalog are divided
-between the threads and the random number generator is used independently
-on each thread. So even when the random number generator type and seed are
-identical, if the number of threads differ, you will get different
-results. Ofcourse, the difference is due to scatter and is statistically
-negligible, but it is not exactly reproducible (which is important in some
-contexts).
+final catalog when the upper limit magnitude column is desired. The random
+seed that is used to define the random positionings 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
address@hidden) 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).
 
 @table @option
 
address@hidden --upmask=STR
address@hidden --upmaskfile=STR
 File name of mask image to use for upper-limit calculation. In some cases
 (especially when doing matched photometry), the object labels specified in
 the main input and mask image might not be adequate. In other words they do
 not necessarily have to cover @emph{all} detected objects: the user might
-have selected only a few of the objects in their labeled image. All the
-non-zero pixels of the image specified by this option (in the
address@hidden extension) will be set as blank for the upper limit
-magnitude calculation.
+have selected only a few of the objects in their labeled image. This option
+can be used to ignore regions in the image in these situations when
+estimating the upper-limit magnitude. All the non-zero pixels of the image
+specified by this option (in the @option{--upmaskhdu} extension) will be
+ignored in the upper-limit magnitude measurements.
 
 For example, when you are using labels from another image, you can give
 NoiseChisel's objects image output for this image as the value to this
 option. In this way, you can be sure that regions with data do not harm
-your distribution. See @ref{Quantifying data limits} for more on the upper
+your distribution. See @ref{Quantifying measurement limits} for more on the 
upper
 limit magnitude.
 
 @item --upmaskhdu=STR
@@ -13294,90 +13607,64 @@ also slow down the process. When a randomly 
positioned sample overlaps with
 a detected/masked pixel it is not counted and another random position is
 found until the object completely lies over an undetected region. So you
 can be sure that for each object, this many samples over undetected objects
-are made. See the upper limit magnitude discussion in @ref{Quantifying data
-limits} for more.
+are made. See the upper limit magnitude discussion in @ref{Quantifying
+measurement limits} for more.
 
 @item --envseed
 Read the random number generator type and seed value from the environment
 (see @ref{Generating random numbers}). Random numbers are used in
 calculating the random positions of different samples of each object.
 
address@hidden --upsclipmultip=FLT
-The multiple of @mymath{\sigma} to use in @mymath{\sigma}-clipping the
-final distribution (see @ref{Sigma clipping}). The images might have some
-artifacts or some regions with signal in the image might not have been
-removed correctly. If a random sampling falls over such regions, they can
-significantly bias the final standard deviation. To avoid the effect of
-such outliers, after the distribution is found, it is
address@hidden (by convergence).
-
address@hidden --upsclipaccu=FLT
-Relative difference between two successive @mymath{\sigma} measurements to
-halt the @mymath{\sigma}-clipping process by convergence, see the
-explanations for @option{--upsclipmultip} for more.
address@hidden --upsigmaclip=FLT,FLT
+The raw distribution of random values will not be used to find the
+upper-limit magnitude, it will first be @mymath{\sigma}-clipped (see
address@hidden clipping}) to avoid outliers in the distribution (mainly the
+faint undetected wings of bright/large objects in the image). This option
+takes two values: the first is the multiple of @mymath{\sigma}, and the
+second is theh termination criteria. If the latter is larger than 1, it is
+read as an integer number and will be the number of times to clip. If it is
+smaller than 1, it is interpretted as the tollerance level to stop
+clipping. See @ref{Sigma clipping} for a complete explanation.
 
 @item --upnsigma=FLT
-The multiple of the standard deviation (or @mymath{\sigma}) used to measure
-the magnitude. Note that this is the final @mymath{\sigma} produced after
-the @mymath{\sigma}-clipping.
+The multiple of the final (@mymath{\sigma}-clipped) standard deviation (or
address@hidden) used to measure the upper-limit brightness or
+magnitude.
 
 @end table
 
 
address@hidden MakeCatalog output columns,  , Upper-limit magnitude settings, 
Invoking astmkcatalog
address@hidden MakeCatalog output columns
 
-
-The final group of options particular to MakeCatalog are those that
-specify which columns should be displayed in the output catalogs. For
-each column there is an option, if it has been called on the command
-line or in any of the configuration files, it will included as a
-column in the output catalog. Some of the options apply to both
-objects and clumps and some are particular to only one of them. The
-latter cases are explicitly marked with [Objects] or [Clumps] to
-specify the catalog they will be placed in.
-
-The order of the columns in the output catalog is the inverse of the
-order their options are read in. For example see the following
address@hidden command is practically identical to the first
-command in the one-line examples, see @ref{Options} for an explanation
-of the concatenation of the on/off options.}:
-
address@hidden
-$ astmkcatalog --magnitude --dec --ra --id input.fits
address@hidden example
-
address@hidden
-In this example, if no columns are specified in any of the
-configuration files (see @ref{Configuration files}), then the columns
-in the catalogs produced by this command will have the following
-order: ID, RA, Dec and Magnitude. Contrary to what it looks like, this
-is done to make life easier for the users. The configuration files can
-also keep any of the columns (so you don't have to specify your
-desired columns every time). This inverse ordering thus comes from
-their precedence, see @ref{Configuration file precedence}.
-
-For example catalogs usually have at least an ID column and position
-columns (in the image and/or the world coordinate system). By reading
-the order of the columns in reverse you can have your fixed set of
-columns in your system wide configuration file and in any particular
-run, if you want some other information about objects or clumps, you
-can add those columns on the command-line. Through the user and
-current directory configuration files, you can also have custom
-catalogs in each of your working directories, without bothering to
-specify the columns every time you run MakeCatalog in those
-directories.
+The final group of options particular to MakeCatalog are those that specify
+which columns should be written into the final output table. For each
+column there is an option, if it has been called on the command line or in
+any of the configuration files, it will included as a column in the output
+catalog in the same order (see @ref{Configuration file precedence}). Some
+of the columns apply to both objects and clumps and some are particular to
+only one of them. The latter cases are explicitly marked with [Objects] or
+[Clumps] to specify the catalog they will be placed in.
 
 @table @option
 
 @item --i
address@hidden --id
-The ID of the clump or object.
address@hidden --ids
+This is a unique option it can add multiple columns to the final
+catalog(s). Calling this option will put the object IDs (@option{--objid})
+in the objects catalog and host-object-ID (@option{--hostobjid}) and
+ID-in-host-object (@option{--idinhostobj}) into the clumps catalog. Hence
+if only object catalogs are required, it has the same effect as
address@hidden
+
address@hidden --objid
+[Objects] ID of this object.
 
 @item -j
address@hidden hostobjid
address@hidden --hostobjid
 [Clumps] The ID of the object which hosts this clump.
 
address@hidden -I
address@hidden --idinhostobj
address@hidden --idinhostobj
 [Clumps] The ID of this clump in its host object.
 
 @item -C
@@ -13393,20 +13680,18 @@ The area (number of pixels) in any clump or object.
 
 @item -x
 @itemx --x
-The flux weighted center of all objects and clumps along the first
-FITS axis (horizontal when viewed in SAO ds9). The weight has to have
-a positive value (pixel value larger than the Sky value) to be
-meaningful! Specially when doing matched photometry, this might not
-happen: no pixel value might be above the Sky value. In such cases,
-where there was no pixel with a value larger than the Sky in the
-object or clump, the geometric center (ignoring pixel values, only
-their positions) over the object or clump is used instead of the flux
-weighted center (see @option{--geox}).
+The flux weighted center of all objects and clumps along the first FITS
+axis (horizontal when viewed in SAO ds9), see @mymath{\overline{x}} in
address@hidden elliptical parameters}. The weight has to have a positive
+value (pixel value larger than the Sky value) to be meaningful! Specially
+when doing matched photometry, this might not happen: no pixel value might
+be above the Sky value. In such cases, you can use the geometric center,
+see @option{--geox}.
 
 @item -y
 @itemx --y
-The flux weighted center of all objects and clumps along the second
-FITS axis (vertical when viewed in SAO ds9). See @option{--x}.
+The flux weighted center of all objects and clumps along the second FITS
+axis (vertical when viewed in SAO ds9). See @option{--x}.
 
 @item --geox
 The geometric center of all objects and clumps along the first FITS
@@ -13435,8 +13720,7 @@ the second FITS axis. See @option{--geox}.
 
 @item -r
 @itemx --ra
-Flux weighted right ascension of all objects or clumps, see
address@hidden
+Flux weighted right ascension of all objects or clumps, see @option{--x}.
 
 @item -d
 @itemx --dec
@@ -13508,11 +13792,10 @@ The magnitude of clumps or objects, see 
@option{--brightness}.
 @item -e
 @itemx --magnitudeerr
 The magnitude error of clumps or objects. The magnitude error is calculated
-from the signal-to-noise ratio (see @option{--sn}, abbreviated as
address@hidden): @mymath{\Delta{M}=2.5/(S\times\ln{10})}. Note that until now
-this error assumes un-correlated pixel values and also does not include the
-error in estimating the aperture (or error in generating the labeled
-image).
+from the signal-to-noise ratio (see @option{--sn} and @ref{Quantifying
+measurement limits}). Note that until now this error assumes un-correlated
+pixel values and also does not include the error in estimating the aperture
+(or error in generating the labeled image).
 
 For now these factors have to be found by other means.
 @url{https://savannah.gnu.org/task/index.php?14124, Task 14124} has been
@@ -13522,13 +13805,19 @@ defined for work on adding these sources of error too.
 [Objects] The magnitude of all clumps in this object, see
 @option{--clumpbrightness}.
 
address@hidden --upperlimit
+The upper limit value (in units of the input image) for this object or
+clump. See @ref{Quantifying measurement limits} and @ref{Upper-limit
+magnitude settings} for a complete explanation. This is very important for
+the fainter and smaller objects in the image where the measured magnitudes
+are not reliable.
+
+
 @item --upperlimitmag
-[Objects] The upper limit magnitude for this object. The object's footprint
-is used over randomly positioned parts of the image that do not cover a
-detected object. The standard deviation of the final distribution is then
-the upper limit magnitude, see @ref{Quantifying data limits} for a complete
-explanation. This is very important for the fainter objects in the image
-where the measured magnitudes are not reliable.
+The upper limit magnitude for this object or clump. See @ref{Quantifying
+measurement limits} and @ref{Upper-limit magnitude settings} for a complete
+explanation. This is very important for the fainter and smaller objects in
+the image where the measured magnitudes are not reliable.
 
 @item --riverave
 [Clumps] The average brightness of the river pixels around this
@@ -13562,18 +13851,18 @@ input sky standard deviation image pixels that lie 
over this object.
 
 @item -A
 @itemx --semimajor
-The pixel-value weighted semi-major axis of the profile (assuming it
-is an ellipse) in units of pixels.
+The pixel-value weighted semi-major axis of the profile (assuming it is an
+ellipse) in units of pixels. See @ref{Measuring elliptical parameters}.
 
 @item -B
 @itemx --semiminor
-The pixel-value weighted semi-minor axis of the profile (assuming it
-is an ellipse) in units of pixels.
+The pixel-value weighted semi-minor axis of the profile (assuming it is an
+ellipse) in units of pixels. See @ref{Measuring elliptical parameters}.
 
 @item -p
 @itemx --positionangle
-The pixel-value weighted angle of the semi-major axis with the first
-FITS axis in degrees.
+The pixel-value weighted angle of the semi-major axis with the first FITS
+axis in degrees. See @ref{Measuring elliptical parameters}.
 
 @item --geosemimajor
 The geometric (ignoring pixel values) semi-major axis of the profile,
@@ -13593,105 +13882,6 @@ with the first FITS axis in degrees.
 
 
 
address@hidden Adding new columns to MakeCatalog,  , Invoking astmkcatalog, 
MakeCatalog
address@hidden Adding new columns to MakeCatalog
-
-The common development characteristics of MakeCatalog and other
-Gnuastro programs is explained in @ref{Developing}. This section
-might be more clearly understood after reading that
-chapter. MakeCatalog has been designed to allow easy addition of new
-columns, here we will give a fast over-view of the steps you need to
-take to define a new output column. After adding and testing your
-column, you are most welcome (and encouraged) to share it with us so
-we can add to the next release of Gnuastro for everyone else to also
-benefit from your efforts.
-
-MakeCatalog will first have two passes over the input pixels: in the
-first pass it will gather mainly object information and in the second
-run, it will mainly focus on the clumps, or any other measurement that
-needs an output from the first pass. These two passes are designed to
-be raw summations: no extra processing. This will allow parallel
-processing and simplicity/clarity. Once the passes are done, depending
-on the order of options that the user has defined the appropriate
-function will be called to add a processed column from the raw
-calculations to the output array (which will be printed). So if your
-new calculation, needs new raw information from the pixels, then you
-will need to also modify the respective @code{firstpass} and
address@hidden functions and define new information table columns
-in @file{main.h}.
-
-For all the steps below, it is easiest to just copy and paste an existing
-option and change the variables in each step. In all these different
-places, the options are sorted in the same order (same order as
address@hidden astmkcatalog}). This allows a particular column/option to be
-easily found in all steps. Therefore in adding your new option, be sure to
-keep it in the same relative place in the list in all the separate places
-(it doesn't necessarily have to be in the end), and near conceptually
-similar options.
-
address@hidden @file
-
address@hidden main.h
-The @code{objectcols} and @code{clumpcols} enumerated variables
-(@code{enum}) define the labels for the separate information table columns
-(the raw pixel calculations). If your new calculation requires new raw
-columns, the add new macros for them. The @code{outcols} enumerated
-variables define the recognized output columns. You will need to add a new
-name for your desired column here, then add a row for your new parameter in
-the @code{uiparams} structure, the name should be the same as the one you
-used in @code{outcols} (in small caps).
-
address@hidden GNU C library
address@hidden args.h
-This file specifies all the parameters for the GNU C library, Argp
-structure that is in charge of reading the user's options. It is best
-that the option name be the same as the name you put in @code{outcols}
-(in @file{main.h}), like the other options that are already
-defined.
-
-Define a new option in @code{argp_option} (rows between the @address@hidden and
address@hidden@}}). The first is the name of the option, the second is its short
-option name (see @ref{Options}). The next two values should be @code{0} and
-the last should be a short description of your option/column. For the
-second element is best to use a number (so no short option is defined for
-the user) which in Gnuastro is defined to be a number larger than 500. The
-range of used numbers (and short option names) are shown in the comment
-immediately above @code{argp_option} structure, increase that number by one
-and use that number for your new option.
-
-Go down to the @code{parse_opt} function and add a @code{case} for your
-specific option (using the same number you specified above), then update
-the contents, based on the new macros you defined @code{main.h}.
-
address@hidden ui.c
-In the @code{readconfig} function, add an @code{else if} for your option
-(easiest to just copy and paste, then update). In the @code{printvalues}
-function's @code{switch} structure, add a @code{case} for your
-option. Finally, in the @code{preparearrays} function, add a @code{case}
-for your new output column. Note that if the column is to be used for both
-objects and clumps, you need to set both @code{p->objcols} and
address@hidden>clumpcols}.
-
address@hidden mkcatalog.c
-If your procedure needs new raw calculations, add a new line to
-appropriate parts of the @code{firstpass}, and/or @code{secondpass}
-functions, they should be clear and are fully commented. Then add a
address@hidden for your column in the @code{makeoutput} function, here
-you will need to call a function (which should be defined in
address@hidden).
-
address@hidden columns.c
-Define the function that will write your desired column in the output
-column using the raw input calculations here. There are a lot of
-functions in this file which you can use to model and they are
-commented, in short you have to specify a header (commented column),
-the units of your variable, the possible accuracy to print and finally
-the actual columns.
-
address@hidden table
-
-
-
 
 
 
@@ -17977,13 +18167,6 @@ small program was compiled and run on your system when 
you ran
 @command{make check}. You can use it as a template to easily create small
 multi-threaded programs and efficiently use your powerful CPU.
 
address@hidden Macro GAL_THREADS_NON_THRD_INDEX
-This value will be used in the output of @code{gal_threads_dist_in_threads}
-as a non-index. It plays the same role as a string's null character:
address@hidden': as soon as the parser sees this value, it will stop
-continuing.
address@hidden deffn
-
 @deftypefun void gal_threads_attr_barrier_init (pthread_attr_t @code{*attr}, 
pthread_barrier_t @code{*b}, size_t @code{limit})
 @cindex Detached threads
 Initialize the general thread attribute @code{attr} and the barrier
@@ -18018,7 +18201,7 @@ The purpose of this function is to do what we explained 
above: each row in
 the @code{outthrds} array contains the indexs of actions which must be done
 by one thread. @code{outthrds} contains @code{outthrdcols} columns. In
 using @code{outthrds}, you don't have to know the number of columns. The
address@hidden macro has a role very similar to a
address@hidden macro has a role very similar to a
 string's @code{\0}: every row finishes with this macro, so can easily stop
 parsing the indexes in the row when you confront it. Please see the example
 program in @file{tests/lib/multithread.c} for a demonstration.
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 93c5d0b..0658d15 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -66,15 +66,14 @@ libgnuastro_la_SOURCES = arithmetic.c arithmetic-binary.c   
               \
 # in the $(headersdir) directory. Some of the header files don't need to be
 # installed.
 headersdir=$(top_srcdir)/lib/gnuastro
-pkginclude_HEADERS = gnuastro/config.h $(headersdir)/arithmetic.h        \
-  $(headersdir)/binary.h $(headersdir)/blank.h $(headersdir)/box.h       \
-  $(headersdir)/convolve.h $(headersdir)/data.h $(headersdir)/fits.h     \
-  $(headersdir)/git.h $(headersdir)/interpolate.h                        \
-  $(headersdir)/linkedlist.h $(headersdir)/dimension.h                   \
-  $(headersdir)/permutation.h $(headersdir)/polygon.h                    \
-  $(headersdir)/qsort.h $(headersdir)/statistics.h $(headersdir)/table.h \
-  $(headersdir)/threads.h $(headersdir)/tile.h $(headersdir)/txt.h       \
-  $(headersdir)/type.h $(headersdir)/wcs.h
+pkginclude_HEADERS = gnuastro/config.h $(headersdir)/arithmetic.h         \
+  $(headersdir)/binary.h $(headersdir)/blank.h $(headersdir)/box.h        \
+  $(headersdir)/convolve.h $(headersdir)/data.h $(headersdir)/dimension.h  \
+  $(headersdir)/fits.h $(headersdir)/git.h $(headersdir)/interpolate.h    \
+  $(headersdir)/linkedlist.h $(headersdir)/permutation.h                  \
+  $(headersdir)/polygon.h $(headersdir)/qsort.h $(headersdir)/statistics.h \
+  $(headersdir)/table.h $(headersdir)/threads.h $(headersdir)/tile.h      \
+  $(headersdir)/txt.h $(headersdir)/type.h $(headersdir)/wcs.h
 
 
 
@@ -86,10 +85,10 @@ pkginclude_HEADERS = gnuastro/config.h 
$(headersdir)/arithmetic.h        \
 # will not distributed, so we need to explicitly tell Automake to
 # distribute them here.
 internaldir=$(top_srcdir)/lib/gnuastro-internal
-EXTRA_DIST = gnuastro.pc.in $(headersdir)/README                        \
+EXTRA_DIST = gnuastro.pc.in $(headersdir)/README $(internaldir)/README   \
   $(internaldir)/arithmetic-binary.h $(internaldir)/arithmetic-onlyint.h \
-  $(internaldir)/arithmetic-other.h $(internaldir)/config.h.in          \
-  $(internaldir)/checkset.h $(internaldir)/fixedstringmacros.h          \
+  $(internaldir)/checkset.h $(internaldir)/commonopts.h                  \
+  $(internaldir)/config.h.in $(internaldir)/fixedstringmacros.h          \
   $(internaldir)/options.h $(internaldir)/timing.h
 
 
diff --git a/lib/arithmetic.c b/lib/arithmetic.c
index f1b92c6..54c5464 100644
--- a/lib/arithmetic.c
+++ b/lib/arithmetic.c
@@ -563,7 +563,7 @@ arithmetic_binary_function_flt(int operator, unsigned char 
flags,
     OT b, *o=out->array, *of=o+out->size;                            \
     if(iftrue->size==1)                                              \
       {                                                              \
-        if( gal_blank_present(iftrue) )                              \
+        if( gal_blank_present(iftrue, 0) )                           \
           {                                                          \
             gal_blank_write(&b, out->type);                          \
             do { *o = *c++ ? b : *o;        } while(++o<of);         \
@@ -979,7 +979,7 @@ arithmetic_multioperand(int operator, unsigned char flags, 
gal_data_t *list)
      list element or not. */
   hasblank=gal_data_malloc_array(GAL_TYPE_UINT8, dnum);
   for(tmp=list;tmp!=NULL;tmp=tmp->next)
-    hasblank[i++]=gal_blank_present(tmp);
+    hasblank[i++]=gal_blank_present(tmp, 0);
 
 
   /* Start the operation. */
diff --git a/lib/binary.c b/lib/binary.c
index da6ce4c..51c0b21 100644
--- a/lib/binary.c
+++ b/lib/binary.c
@@ -400,8 +400,8 @@ gal_binary_connected_components(gal_data_t *binary, 
gal_data_t **out,
      array, then give them the blank labeled array. Note that since
      their value will not be 0, they will also not be labeled. */
   l=lab->array;
-  bf=(b=binary->array)+binary->size;
-  if( gal_blank_present(binary) )
+  bf=(b=binary->array)+binary->size; /* Library must have no side effect,   */
+  if( gal_blank_present(binary, 0) ) /* So blank flag should not be changed.*/
     do *l++ = *b==GAL_BLANK_UINT8 ? GAL_BLANK_INT32 : 0; while(++b<bf);
 
 
diff --git a/lib/blank.c b/lib/blank.c
index 05889c6..617ae8f 100644
--- a/lib/blank.c
+++ b/lib/blank.c
@@ -144,8 +144,8 @@ gal_blank_alloc_write(uint8_t type)
           af = ( a = start + increment ) + input->dsize[input->ndim-1]; \
                                                                         \
         /* Check for blank values. */                                   \
-        if(b==b) do if(*a==b)  return 1; while(++a<af);                 \
-        else     do if(*a!=*a) return 1; while(++a<af);                 \
+        if(b==b) do if(*a==b)  { hasblank=1; break; } while(++a<af);    \
+        else     do if(*a!=*a) { hasblank=1; break; } while(++a<af);    \
                                                                         \
         /* Necessary when we are on a tile. */                          \
         if(input!=block)                                                \
@@ -155,8 +155,9 @@ gal_blank_alloc_write(uint8_t type)
       }                                                                 \
   }
 int
-gal_blank_present(gal_data_t *input)
+gal_blank_present(gal_data_t *input, int updateflag)
 {
+  int hasblank=0;
   char **str, **strf;
   size_t increment=0, num_increment=1;
   gal_data_t *block=gal_tile_block(input);
@@ -208,14 +209,22 @@ gal_blank_present(gal_data_t *input)
             "`gal_blank_present'");
 
     default:
-      error(EXIT_FAILURE, 0, "a bug! type value (%d) not recognized "
+      error(EXIT_FAILURE, 0, "type value (%d) not recognized "
             "in `gal_blank_present'", block->type);
     }
 
+  /* Update the flag if requested. */
+  if(updateflag)
+    {
+      input->flag |= GAL_DATA_FLAG_BLANK_CH;
+      if(hasblank) input->flag |= GAL_DATA_FLAG_HASBLANK;
+      else         input->flag &= ~GAL_DATA_FLAG_HASBLANK;
+    }
+
   /* If there was a blank value, then the function would have returned with
      a value of 1. So if it reaches here, then we can be sure that there
      was no blank values, hence, return 0. */
-  return 0;
+  return hasblank;
 }
 
 
@@ -242,7 +251,7 @@ gal_blank_flag(gal_data_t *input)
   gal_data_t *out;
   char **str=input->array, **strf=str+input->size;
 
-  if( gal_blank_present(input) )
+  if( gal_blank_present(input, 0) )
     {
       /* Allocate a non-cleared output array, we are going to parse the
          input and fill in each element. */
@@ -323,7 +332,7 @@ gal_blank_remove(gal_data_t *input)
           "%zu dimensions", input->ndim);
 
   /* If the dataset doesn't have blank values, then just get the size. */
-  if( gal_blank_present(input) )
+  if( gal_blank_present(input, 0) )
     {
       /* Shift all non-blank elements to the start of the array. */
       switch(input->type)
@@ -348,6 +357,10 @@ gal_blank_remove(gal_data_t *input)
   /* Adjust the size elements of the dataset. */
   input->ndim=1;
   input->dsize[0]=input->size=num;
+
+  /* Set the flags to mark that there is no blanks. */
+  input->flag |= GAL_DATA_FLAG_BLANK_CH;
+  input->flag &= ~GAL_DATA_FLAG_HASBLANK;
 }
 
 
diff --git a/lib/convolve.c b/lib/convolve.c
index 699932f..92e9cfe 100644
--- a/lib/convolve.c
+++ b/lib/convolve.c
@@ -479,7 +479,7 @@ convolve_spatial_on_thread(void *inparam)
 
 
   /* Go over all the tiles given to this thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* Set this tile's pointer into this thread's parameters. */
       pprm->tile = &cprm->tiles[ tprm->indexs[i] ];
diff --git a/lib/data.c b/lib/data.c
index 83aed5b..1b7d766 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -485,6 +485,9 @@ gal_data_string_fixed_alloc_size(gal_data_t *data)
 void
 gal_data_free_contents(gal_data_t *data)
 {
+  size_t i;
+  char **strarr;
+
   if(data==NULL)
     error(EXIT_FAILURE, 0, "the input data structure to "
           "`gal_data_free_contents' was a NULL pointer");
@@ -501,8 +504,7 @@ gal_data_free_contents(gal_data_t *data)
      actual array. */
   if(data->type==GAL_TYPE_STRING && data->array)
     {
-      size_t i;
-      char **strarr=data->array;
+      strarr=data->array;
       for(i=0;i<data->size;++i) free(strarr[i]);
     }
 
@@ -586,6 +588,7 @@ gal_data_array_calloc(size_t size)
       out[i].nwcs       = 0;
       out[i].wcs        = NULL;
       out[i].mmapname   = NULL;
+      out[i].next       = NULL;
       out[i].name = out[i].unit = out[i].comment = NULL;
       out[i].disp_fmt = out[i].disp_width = out[i].disp_precision = -1;
     }
diff --git a/lib/fits.c b/lib/fits.c
index a456b3f..6fbe8d3 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -659,45 +659,90 @@ gal_fits_hdu_open_type(char *filename, char *hdu, 
unsigned char img0_tab1)
 /**************************************************************/
 /**********            Header keywords             ************/
 /**************************************************************/
+/* CFITSIO doesn't remove the two single quotes around the string value, so
+   the strings it reads are like: 'value ', or 'some_very_long_value'. To
+   use the value, it is commonly necessary to remove the single quotes (and
+   possible extra spaces). This function fill modify the string in its own
+   allocated space. You can use this to later free the original string (if
+   it was allocated). */
+void
+gal_fits_key_clean_str_value(char *string)
+{
+  int end;       /* Has to be int because size_t is always >=0. */
+  char *c, *cf;
+
+  /* Start from the second last character (the last is a single quote) and
+     go down until you hit a non-space character. This will also work when
+     there is no space characters between the last character of the value
+     and ending single-quote: it will be set to '\0' after this loop. */
+  for(end=strlen(string)-2;end>=0;--end)
+    if(string[end]!=' ')
+      break;
+
+  /* Shift all the characters after the first one (which is a `'' back by
+     one and put the string ending characters on the `end'th element. */
+  cf=(c=string)+end; do *c=*(c+1); while(++c<cf);
+  *cf='\0';
+}
+
+
+
+
 
-/* Each keyword name, value, comment, type is kept within a Gnuastro data
-   structure. The input should be an array of such data structures,
-   either statically allocated like:
+/* Read the keyword values from a FITS pointer. The input should be a
+   linked list of `gal_data_t'. Before calling this function, you just have
+   to set the `name' and desired `type' values of each element in the list
+   to the keyword you want it to keep the value of. The given `name' value
+   will be directly passed to CFITSIO to read the desired keyword. This
+   function will allocate space to keep the value. Here is one example of
+   using this function:
 
-        gal_data_t keys[2];
+      gal_data_t *keysll=gal_data_array_calloc(N);
 
-   or dynamically allocated like:
+      for(i=0;i<N-2;++i) keysll[i]->next=keysll[i+1];
 
-        gal_data_t *keys;
-        keys=malloc(2*sizeof *keys);
+      \\ Put a name and type for each element.
 
-   Before calling this function, you just have to set the `title' and
-   `type' values of the data structure. The given title value will be
-   directly passed to CFITSIO to read the desired keyword.
+      gal_fits_key_read_from_ptr(fptr, keysll, 0, 0);
+
+      \\ use the values as you like.
+
+      gal_data_array_free(keysll, N, 1);
+
+   If the `array' pointer of each keyword's dataset is not NULL, then it is
+   assumed that the space has already been allocated. If it is NULL, then
+   space will be allocated internally here.
+
+   Strings need special consideration: the reason is that generally,
+   `gal_data_t' needs to also allow for array of strings (as it supports
+   arrays of integers for example). Hence two allocations will be done here
+   (one if `array!=NULL') and `keysll[i].array' must be interpretted as
+   `char **': one allocation for the pointer, one for the actual
+   characters. You don't have to worry about the freeing,
+   `gal_data_array_free' will free both allocations. So to read a string,
+   one easy way would be the following:
+
+      char *str, **strarray;
+      strarr = keysll[i].array;
+      str    = strarray[0];
+
+   If CFITSIO is unable to read a keyword for any reason the `status'
+   element of the respective `gal_data_t' will be non-zero. You can check
+   the successful reading of the keyword from the `status' value in each
+   keyword's `gal_data_t'. If it is zero, then the keyword was found and
+   succesfully read. Otherwise, it a CFITSIO status value. You can use
+   CFITSIO's error reporting tools or `gal_fits_io_error' for reporting the
+   reason. A tip: when the keyword doesn't exist, then CFITSIO's status
+   value will be `KEY_NO_EXIST'.
 
    CFITSIO will start searching for the keywords from the last place in the
    header that it searched for a keyword. So it is much more efficient if
    the order that you ask for keywords is based on the order they are
    stored in the header.
-
-   ABOUT THE STRING VALUES:
-
-   The space for a string value is dynamically allocated within the
-   `gal_fits_key' structure (to be `FLEN_VALUE' characters, `FLEN_VALUE' is
-   defined by CFITSIO and is the largest possible number of characters).
-
-   This function will not abort if CFITSIO is unable to read the keyword
-   due to any reason. You can check the successful reading of the keyword
-   from the `status' value in each keyword's data structure. If its zero,
-   then the keyword was found and read as expected. Otherwise, this a
-   CFITSIO status, so you use its error reporting tools or
-   `gal_fits_io_error' for reporting the reason. If you have an alternative
-   to the keyword, or its not mandatory, and the keyword doesn't exist,
-   then the status value will be KEY_NO_EXIST (from CFITSIO).
  */
 void
 gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t *keysll,
-                            int readcomment, int readunit)
+                           int readcomment, int readunit)
 {
   void *valueptr;
   char **strarray;
@@ -710,6 +755,13 @@ gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t 
*keysll,
         /* Initialize the status: */
         tmp->status=0;
 
+        /* For each keyword, this function stores one value currently. So
+           set the size and ndim to 1. But first allocate dsize if it
+           wasn't already allocated. */
+        if(tmp->dsize==NULL)
+          tmp->dsize=gal_data_malloc_array(GAL_TYPE_SIZE_T, 1);
+        tmp->ndim=tmp->size=tmp->dsize[0]=1;
+
         /* When the type is a string, `tmp->array' is an array of pointers
            to a separately allocated piece of memory. So we have to
            allocate that space here. If its not a string, then the
@@ -718,7 +770,9 @@ gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t 
*keysll,
           {
           case GAL_TYPE_STRING:
             errno=0;
-            strarray=tmp->array;
+            tmp->array=strarray=( tmp->array
+                                  ? tmp->array
+                                  : gal_data_malloc_array(tmp->type, 1) );
             valueptr=strarray[0]=malloc(FLEN_VALUE * sizeof *strarray[0]);
             if(strarray[0]==NULL)
               error(EXIT_FAILURE, errno, "%zu bytes for strarray[0] in "
@@ -727,7 +781,9 @@ gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t 
*keysll,
             break;
 
           default:
-            valueptr=tmp->array;
+            tmp->array=valueptr=( tmp->array
+                                  ? tmp->array
+                                  : gal_data_malloc_array(tmp->type, 1) );
           }
 
         /* Allocate space for the keyword comment if necessary.*/
@@ -773,39 +829,11 @@ gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t 
*keysll,
         /* If the comment was empty, free the space and set it to zero. */
         if(tmp->comment && tmp->comment[0]=='\0')
           {free(tmp->comment); tmp->comment=NULL;}
-      }
-}
-
-
-
-
-
-/* CFITSIO doesn't remove the two single quotes around the string value, so
-   the strings it reads are like: 'value ', or 'some_very_long_value'. To
-   use the value, it is commonly necessary to remove the single quotes (and
-   possible extra spaces). This function fill modify the string in its own
-   allocated space. You can use this to later free the original string (if
-   it was allocated). */
-char *
-gal_fits_key_clean_str_value(char *string)
-{
-  size_t i;
-  char *out=string+1;
-
-  /* Start from the second last character (the last is a single quote) and
-     go down until you hit a non-space character. This will also work when
-     there is no space characters between the last character of the value
-     and ending single-quote: it will be set to '\0' after this loop. */
-  for(i=strlen(out)-2;i>0;--i)
-    if(out[i]!=' ')
-      break;
 
-  /* If the string is empty, it will stop at the first character that is a
-     single quote. So no need to check further. */
-  out[i+1]='\0';
+        /* Strings need to be cleaned (CFITSIO puts `'' around them with
+           some (possiblly) extra space on the two ends of the string. */
 
-  /* Return the new string. */
-  return out;
+      }
 }
 
 
@@ -1432,7 +1460,7 @@ gal_fits_img_write_to_ptr(gal_data_t *input, char 
*filename)
 
   /* If we have blank pixels, we need to define a BLANK keyword when we are
      dealing with integer types. */
-  if(gal_blank_present(towrite))
+  if(gal_blank_present(towrite, 0))
     switch(towrite->type)
       {
       case GAL_TYPE_FLOAT32:
@@ -1812,7 +1840,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
   long long *tzero;
   gal_data_t *allcols;
   int status=0, datatype, *tscal;
-  char keyname[FLEN_KEYWORD]="XXXXXXXXXXXXX", value[FLEN_VALUE], *val;
+  char keyname[FLEN_KEYWORD]="XXXXXXXXXXXXX", value[FLEN_VALUE];
 
 
   /* Open the FITS file and get the basic information. */
@@ -1852,9 +1880,10 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
 
       /* For string valued keywords, CFITSIO's function above, keeps the
          single quotes around the value string, one before and one
-         after. The latter single-quote will be automatically be removed
-         with the `remove_trailign_space' function.*/
-      val = value[0]=='\'' ? gal_fits_key_clean_str_value(value) : value;
+         after. `gal_fits_key_clean_str_value' will remove these single
+         quotes and any possible trailing space within the allocated
+         space.*/
+      if(value[0]=='\'') gal_fits_key_clean_str_value(value);
 
       /* COLUMN DATA TYPE. According the the FITS standard, the value of
          TFORM is most generally in this format: `rTa'. `T' is actually a
@@ -1877,9 +1906,9 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
               /* The FITS standard's value to this option for FITS ASCII
                  and binary files differ. */
               if(*tabletype==GAL_TABLE_FORMAT_AFITS)
-                fits_ascii_tform(val, &datatype, NULL, NULL, &status);
+                fits_ascii_tform(value, &datatype, NULL, NULL, &status);
               else
-                fits_binary_tform(val, &datatype, &repeat, NULL, &status);
+                fits_binary_tform(value, &datatype, &repeat, NULL, &status);
 
               /* Write the type into the data structure. */
               allcols[index].type=gal_fits_datatype_to_type(datatype, 1);
@@ -1890,12 +1919,12 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
                 {
                   if(*tabletype==GAL_TABLE_FORMAT_AFITS)
                     {
-                      repeat=strtol(val+1, &tailptr, 0);
+                      repeat=strtol(value+1, &tailptr, 0);
                       if(*tailptr!='\0')
                         error(EXIT_FAILURE, 0, "%s (hdu: %s): the value to "
                               "keyword `%s' (`%s') is not in `Aw' format "
                               "(for strings) as required by the FITS "
-                              "standard", filename, hdu, keyname, val);
+                              "standard", filename, hdu, keyname, value);
                     }
                   allcols[index].disp_width=repeat;
                 }
@@ -1908,11 +1937,11 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
             {
-              tscal[index]=strtol(val, &tailptr, 0);
+              tscal[index]=strtol(value, &tailptr, 0);
               if(*tailptr!='\0')
                 error(EXIT_FAILURE, 0, "%s (hdu: %s): value to %s keyword "
                       "(`%s') couldn't be read as a number", filename, hdu,
-                      keyname, val);
+                      keyname, value);
             }
         }
 
@@ -1922,11 +1951,11 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
             {
-              tzero[index]=strtoll(val, &tailptr, 0);
+              tzero[index]=strtoll(value, &tailptr, 0);
               if(*tailptr!='\0')
                 error(EXIT_FAILURE, 0, "%s (hdu: %s): value to %s keyword "
                       "(`%s') couldn't be read as a number", filename, hdu,
-                      keyname, val);
+                      keyname, value);
             }
         }
 
@@ -1939,7 +1968,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
         {
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
-            gal_checkset_allocate_copy(val, &allcols[index].name);
+            gal_checkset_allocate_copy(value, &allcols[index].name);
         }
 
       /* COLUMN UNITS. */
@@ -1947,7 +1976,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
         {
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
-            gal_checkset_allocate_copy(val, &allcols[index].unit);
+            gal_checkset_allocate_copy(value, &allcols[index].unit);
         }
 
       /* COLUMN COMMENTS */
@@ -1955,7 +1984,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
         {
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
-            gal_checkset_allocate_copy(val, &allcols[index].comment);
+            gal_checkset_allocate_copy(value, &allcols[index].comment);
         }
 
       /* COLUMN BLANK VALUE. Note that to interpret the blank value the
@@ -1973,7 +2002,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
                         "blank value cannot be deduced", filename, hdu,
                         keyname, index+1);
               else
-                gal_table_read_blank(&allcols[index], val);
+                gal_table_read_blank(&allcols[index], value);
             }
         }
 
@@ -1982,7 +2011,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
         {
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields)
-            set_display_format(val, &allcols[index], filename, hdu,
+            set_display_format(value, &allcols[index], filename, hdu,
                                keyname);
         }
 
@@ -2121,7 +2150,7 @@ fits_table_prepare_arrays(gal_data_t *cols, size_t 
numcols, int tabletype,
                expected width or not. Its initial width is set the output
                of the function above, but if the value is larger,
                `asprintf' (which is used) will make it wider. */
-            blank = ( gal_blank_present(col)
+            blank = ( gal_blank_present(col, 0)
                       ? gal_blank_as_string(col->type, col->disp_width)
                       : NULL );
 
@@ -2327,7 +2356,7 @@ gal_fits_tab_write(gal_data_t *cols, struct 
gal_linkedlist_stll *comments,
 
       /* Set the blank pointer if its necessary, note that strings don't
          need a blank pointer in a FITS ASCII table.*/
-      blank = ( gal_blank_present(col)
+      blank = ( gal_blank_present(col, 0)
                 ? gal_blank_alloc_write(col->type) : NULL );
       if(tabletype==GAL_TABLE_FORMAT_AFITS && col->type==GAL_TYPE_STRING)
         { if(blank) free(blank); blank=NULL; }
diff --git a/lib/gnuastro-internal/timing.h b/lib/gnuastro-internal/timing.h
index 6ea3714..c29f827 100644
--- a/lib/gnuastro-internal/timing.h
+++ b/lib/gnuastro-internal/timing.h
@@ -52,7 +52,7 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 #define GAL_TIMING_VERB_MSG_LENGTH_V     50
 #define GAL_TIMING_VERB_MSG_LENGTHS_2_V  65
 
-int64_t
+long
 gal_timing_time_based_rng_seed();
 
 void
diff --git a/lib/gnuastro/blank.h b/lib/gnuastro/blank.h
index 44c424a..db3c7f0 100644
--- a/lib/gnuastro/blank.h
+++ b/lib/gnuastro/blank.h
@@ -27,6 +27,18 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    must be included before the C++ preparations below */
 #include <gnuastro/data.h>
 
+/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+   defined. In the build process, installation information (in particular
+   `GAL_CONFIG_SIZEOF_SIZE_T' that we need below) is kept in
+   `config.h'. When building a user's programs, this information is kept in
+   `gnuastro/config.h'. Note that all `.c' files in Gnuastro's source must
+   start with the inclusion of `config.h' and that `gnuastro/config.h' is
+   only created at installation time (not present during the building of
+   Gnuastro). */
+#ifndef IN_GNUASTRO_BUILD
+#include <gnuastro/config.h>
+#endif
+
 /* C++ Preparations */
 #undef __BEGIN_C_DECLS
 #undef __END_C_DECLS
@@ -63,7 +75,11 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 #define GAL_BLANK_FLOAT64    NAN
 #define GAL_BLANK_STRING     "n/a"
 
-
+#if GAL_CONFIG_SIZEOF_SIZE_T == 4
+#define GAL_BLANK_SIZE_T     GAL_BLANK_UINT32
+#else
+#define GAL_BLANK_SIZE_T     GAL_BLANK_UINT64
+#endif
 
 
 /* Functions. */
@@ -80,7 +96,7 @@ char *
 gal_blank_as_string(uint8_t type, int width);
 
 int
-gal_blank_present(gal_data_t *data);
+gal_blank_present(gal_data_t *input, int updateflag);
 
 gal_data_t *
 gal_blank_flag(gal_data_t *data);
diff --git a/lib/gnuastro/fits.h b/lib/gnuastro/fits.h
index 3a7010b..96a39e2 100644
--- a/lib/gnuastro/fits.h
+++ b/lib/gnuastro/fits.h
@@ -166,12 +166,15 @@ gal_fits_hdu_open_type(char *filename, char *hdu, 
unsigned char img0_tab1);
 /**********            Header keywords             ************/
 /**************************************************************/
 void
+gal_fits_key_clean_str_value(char *string);
+
+void
 gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t *keysll,
                            int readcomment, int readunit);
 
 void
 gal_fits_key_read(char *filename, char *hdu, gal_data_t *keysll,
-                       int readcomment, int readunit);
+                  int readcomment, int readunit);
 
 void
 gal_fits_key_add_to_ll(struct gal_fits_key_ll **list, uint8_t type,
@@ -197,9 +200,6 @@ void
 gal_fits_key_write_version(fitsfile *fptr, struct gal_fits_key_ll *headers,
                            char *program_string);
 
-char *
-gal_fits_key_clean_str_value(char *string);
-
 
 
 
diff --git a/lib/gnuastro/linkedlist.h b/lib/gnuastro/linkedlist.h
index a67adff..6ffb636 100644
--- a/lib/gnuastro/linkedlist.h
+++ b/lib/gnuastro/linkedlist.h
@@ -226,6 +226,9 @@ gal_linkedlist_pop_from_ill(struct gal_linkedlist_ill 
**list, int *value);
 void
 gal_linkedlist_reverse_ill(struct gal_linkedlist_ill **list);
 
+size_t
+gal_linkedlist_num_in_ill(struct gal_linkedlist_ill *list);
+
 void
 gal_linkedlist_print_ill(struct gal_linkedlist_ill *list);
 
diff --git a/lib/gnuastro/threads.h b/lib/gnuastro/threads.h
index 5943f01..d9c80f6 100644
--- a/lib/gnuastro/threads.h
+++ b/lib/gnuastro/threads.h
@@ -26,6 +26,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Include other headers if necessary here. Note that other header files
    must be included before the C++ preparations below */
 #include <pthread.h>
+#include <gnuastro/blank.h>
 
 /* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
@@ -57,9 +58,6 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 
-/* Constant to use for non-existant index */
-#define GAL_THREADS_NON_THRD_INDEX (size_t)(-1)
-
 
 
 /*****************************************************************/
diff --git a/lib/gnuastro/tile.h b/lib/gnuastro/tile.h
index 1856c4d..5cdf0e7 100644
--- a/lib/gnuastro/tile.h
+++ b/lib/gnuastro/tile.h
@@ -26,6 +26,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Include other headers if necessary here. Note that other header files
    must be included before the C++ preparations below */
 #include <gnuastro/data.h>
+#include <gnuastro/fits.h>
 
 /* C++ Preparations */
 #undef __BEGIN_C_DECLS
@@ -62,6 +63,15 @@ gal_tile_start_end_ind_inclusive(gal_data_t *tile, 
gal_data_t *work,
 
 
 
+/***********************************************************************/
+/**************           Series of tiles             ******************/
+/***********************************************************************/
+gal_data_t *
+gal_tile_series_from_minmax(gal_data_t *block, size_t *minmax, size_t number);
+
+
+
+
 
 /***********************************************************************/
 /**************           Allocated block         **********************/
@@ -138,7 +148,8 @@ gal_tile_full_permutation(struct gal_tile_two_layer_params 
*tl);
 void
 gal_tile_full_values_write(gal_data_t *tilevalues,
                            struct gal_tile_two_layer_params *tl,
-                           char *filename, char *program_string);
+                           char *filename, struct gal_fits_key_ll *keys,
+                           char *program_string);
 
 gal_data_t *
 gal_tile_full_values_smooth(gal_data_t *tilevalues,
@@ -367,7 +378,7 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
     size_t increment=0, num_increment=1;                                \
     gal_data_t *iblock = gal_tile_block(IN);                            \
     gal_data_t *oblock = OUT ? gal_tile_block(OUT) : NULL;              \
-    int hasblank = CHECK_BLANK ? gal_blank_present(IN) : 0;             \
+    int hasblank = CHECK_BLANK ? gal_blank_present(IN, 0) : 0;          \
     size_t s_e_ind[2]={0,iblock->size-1}; /* -1: this is INCLUSIVE */   \
                                                                         \
     /* A small sanity check. */                                         \
diff --git a/lib/gnuastro/type.h b/lib/gnuastro/type.h
index 4454e77..3a1c605 100644
--- a/lib/gnuastro/type.h
+++ b/lib/gnuastro/type.h
@@ -35,7 +35,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    `gnuastro/config.h'. Note that all `.c' files in Gnuastro's source must
    start with the inclusion of `config.h' and that `gnuastro/config.h' is
    only created at installation time (not present during the building of
-   Gnuastro).*/
+   Gnuastro). */
 #ifndef IN_GNUASTRO_BUILD
 #include <gnuastro/config.h>
 #endif
diff --git a/lib/gnuastro/wcs.h b/lib/gnuastro/wcs.h
index d82c93e..278666d 100644
--- a/lib/gnuastro/wcs.h
+++ b/lib/gnuastro/wcs.h
@@ -106,6 +106,10 @@ void
 gal_wcs_world_to_img(struct wcsprm *wcs, double *ra, double *dec,
                      double **x, double **y, size_t size);
 
+void
+gal_wcs_img_to_world(struct wcsprm *wcs, double *x, double *y,
+                     double **ra, double **dec, size_t size);
+
 
 
 
diff --git a/lib/interpolate.c b/lib/interpolate.c
index d543a7f..8043ea9 100644
--- a/lib/interpolate.c
+++ b/lib/interpolate.c
@@ -121,7 +121,7 @@ interpolate_close_neighbors_on_thread(void *in_prm)
 
 
   /* Go over all the points given to this thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* For easy reading. */
       fullind=tprm->indexs[i];
diff --git a/lib/linkedlist.c b/lib/linkedlist.c
index cdba40a..e67183b 100644
--- a/lib/linkedlist.c
+++ b/lib/linkedlist.c
@@ -775,6 +775,20 @@ gal_linkedlist_reverse_ill(struct gal_linkedlist_ill 
**list)
 
 
 
+size_t
+gal_linkedlist_num_in_ill(struct gal_linkedlist_ill *list)
+{
+  size_t num=0;
+  struct gal_linkedlist_ill *tmp;
+  for(tmp=list;tmp!=NULL;tmp=tmp->next)
+    ++num;
+  return num;
+}
+
+
+
+
+
 void
 gal_linkedlist_print_ill(struct gal_linkedlist_ill *list)
 {
diff --git a/lib/statistics.c b/lib/statistics.c
index eae030b..7ee242a 100644
--- a/lib/statistics.c
+++ b/lib/statistics.c
@@ -63,7 +63,7 @@ gal_statistics_number(gal_data_t *input)
 
   /* If there is no blank values in the input, then the total number is
      just the size. */
-  if(gal_blank_present(input))
+  if(gal_blank_present(input, 0))
     { GAL_TILE_PARSE_OPERATE({++(*o);}, input, out, 0, 1) }
   else
     *((uint64_t *)(out->array)) = input->size;
@@ -1198,17 +1198,21 @@ gal_statistics_no_blank_sorted(gal_data_t *input, int 
inplace)
 
   /* Make sure there is no blanks in the array that will be used. After
      this step, we won't be dealing with `input' any more, but with
-     `noblank'.*/
-  if( gal_blank_present(contig) )
+     `noblank'. */
+  if( gal_blank_present(contig, inplace) )
     {
       /* See if we should allocate a new dataset to remove blanks or if we
          can use the actual contiguous patch of memory. */
       noblank = inplace ? contig : gal_data_copy(contig);
       gal_blank_remove(noblank);
 
-      /* If we are working in place, then remove the blank flag. */
+      /* If we are working in place, then mark that there are no blank
+         pixels. */
       if(inplace)
-        noblank->flag &= ~GAL_DATA_FLAG_HASBLANK;
+        {
+          noblank->flag |= GAL_DATA_FLAG_BLANK_CH;
+          noblank->flag &= ~GAL_DATA_FLAG_HASBLANK;
+        }
     }
   else noblank=contig;
 
@@ -1671,10 +1675,7 @@ gal_statistics_cfp(gal_data_t *data, gal_data_t *bins, 
int normalize)
 /****************************************************************
  *****************         Outliers          ********************
  ****************************************************************/
-/* Return a float32 type data structure with an array of four values: the
-   final number of points used, the median, average and standard
-   deviation. The number of clips is put into the `status' element of the
-   data structure.
+/* Sigma-cilp a given distribution:
 
    Inputs:
 
@@ -1687,6 +1688,13 @@ gal_statistics_cfp(gal_data_t *data, gal_data_t *bins, 
int normalize)
          - param>=1.0 and an integer: a specific number of times to do the
            clippping.
 
+   Output elements (type FLOAT32):
+
+     - 0: Number of points used.
+     - 1: Median.
+     - 2: Mean.
+     - 3: Standard deviation.
+
   The way this function works is very simple: first it will sort the input
   (if it isn't sorted). Afterwards, it will recursively change the starting
   point of the array and its size, calcluating the basic statistics in each
diff --git a/lib/threads.c b/lib/threads.c
index faf78e5..b72a077 100644
--- a/lib/threads.c
+++ b/lib/threads.c
@@ -199,7 +199,7 @@ gal_threads_dist_in_threads(size_t numactions, size_t 
numthreads,
 
   /* Initialize all the elements to NONINDEX. */
   fp=(sp=thrds)+numthreads*thrdcols;
-  do *sp=GAL_THREADS_NON_THRD_INDEX; while(++sp<fp);
+  do *sp=GAL_BLANK_SIZE_T; while(++sp<fp);
 
   /* Distribute the labels in the threads.  */
   for(i=0;i<numactions;++i)
@@ -211,7 +211,7 @@ gal_threads_dist_in_threads(size_t numactions, size_t 
numthreads,
       size_t j;
       printf("\n\n############################\n");
       printf("THREAD %zu: \n", i);
-      for(j=0;thrds[i*thrdcols+j]!=GAL_THREADS_NON_THRD_INDEX;j++)
+      for(j=0;thrds[i*thrdcols+j]!=GAL_BLANK_SIZE_T;j++)
         printf("%zu, ", thrds[i*thrdcols+j]);
       printf("\b\b.\n");
     }
@@ -286,7 +286,7 @@ gal_threads_attr_barrier_init(pthread_attr_t *attr, 
pthread_barrier_t *b,
 
        size_t i;
 
-       for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+       for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
        {
 
            THE INDEX OF THE TARGET IS NOW AVAILABLE AS
@@ -381,7 +381,7 @@ gal_threads_spin_off(void *(*function)(void *), void 
*caller_params,
 
       /* Spin off the threads: */
       for(i=0;i<numthreads;++i)
-        if(indexs[i*thrdcols]!=GAL_THREADS_NON_THRD_INDEX)
+        if(indexs[i*thrdcols]!=GAL_BLANK_SIZE_T)
           {
             prm[i].id=i;
             prm[i].b=&b;
diff --git a/lib/tile.c b/lib/tile.c
index 2e2c406..3b999f9 100644
--- a/lib/tile.c
+++ b/lib/tile.c
@@ -207,6 +207,89 @@ gal_tile_start_end_ind_inclusive(gal_data_t *tile, 
gal_data_t *work,
 
 
 /***********************************************************************/
+/**************           Series of tiles             ******************/
+/***********************************************************************/
+/* Construct a list of tile(s) given positional minimum(s) and maximum(s).
+   The output is an allocated an allocated array that can later be freed
+   with `gal_data_array_free'. The minimum and maximums are assumed to be
+   inclusive.
+
+   The array keeping the minmium and maximum coordinates for each label
+   will have the following format:
+
+       | min0_d0 | min0_d1 | max0_d0 | max0_d1 | ...
+
+                       ... | minN_d0 | minN_d1 | maxN_d0 | maxN_d1 |   */
+gal_data_t *
+gal_tile_series_from_minmax(gal_data_t *block, size_t *minmax, size_t number)
+{
+  size_t ndim=block->ndim;
+
+  size_t *min, *max;
+  size_t i, d, ind, size, width=2*ndim;
+  gal_data_t *tiles=gal_data_array_calloc(number);
+
+  /* Fill the tile information.  */
+  for(i=0;i<number;++i)
+    {
+      /* To make things more readable. */
+      min = &minmax[ i * width        ];
+      max = &minmax[ i * width + ndim ];
+
+      /* Tile types should be invalid (we shouldn't use tiles directly),
+         also se the other simple values. */
+      tiles[i].flag  = 0;
+      tiles[i].block = block;
+      tiles[i].type  = GAL_TYPE_INVALID;
+      tiles[i].next  = i==number-1 ? NULL : &tiles[i+1];
+
+      /* Set the size related constants. */
+      size = 1;
+      tiles[i].ndim  = ndim;
+      tiles[i].dsize = gal_data_malloc_array(GAL_TYPE_SIZE_T, ndim);
+      for(d=0;d<ndim;++d) size *= tiles[i].dsize[d] = max[d] - min[d] + 1;
+      tiles[i].size  = size;
+
+      /* Tile's array pointer. */
+      ind=gal_dimension_coord_to_index(ndim, block->dsize, min);
+      tiles[i].array = gal_data_ptr_increment(block->array, ind, block->type);
+    }
+
+  /* For a check (put all the objects in an extension of a test file).
+  {
+    gal_data_t *copy;
+    for(i=0;i<number;++i)
+      {
+        copy=gal_data_copy(&tiles[i]);
+        gal_fits_img_write(copy, "tiles.fits", NULL, NULL);
+      }
+  }
+  */
+
+  /* Return the final pointer. */
+  return tiles;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/***********************************************************************/
 /**************        Allocated block of memory      ******************/
 /***********************************************************************/
 /* When you are working on an array, it important to know the size of the
@@ -995,7 +1078,8 @@ gal_tile_full_permutation(struct gal_tile_two_layer_params 
*tl)
 void
 gal_tile_full_values_write(gal_data_t *tilevalues,
                            struct gal_tile_two_layer_params *tl,
-                           char *filename, char *program_string)
+                           char *filename, struct gal_fits_key_ll *keys,
+                           char *program_string)
 {
   gal_data_t *disp;
 
@@ -1023,7 +1107,7 @@ gal_tile_full_values_write(gal_data_t *tilevalues,
 
 
   /* Write the array as a file and then clean up (if necessary). */
-  gal_fits_img_write(disp, filename, NULL, program_string);
+  gal_fits_img_write(disp, filename, keys, program_string);
   if(disp!=tilevalues) gal_data_free(disp);
 }
 
@@ -1146,10 +1230,10 @@ tile_full_blank_flag(void *in_prm)
   gal_data_t *tile;
 
   /* Check all the tiles given to this thread. */
-  for(i=0; tprm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       tile=&tile_ll[ tprm->indexs[i] ];
-      if( gal_blank_present(tile) )
+      if( gal_blank_present(tile, 0) )
         tile->flag |= GAL_DATA_FLAG_HASBLANK;
     }
 
diff --git a/lib/timing.c b/lib/timing.c
index 98f1228..54f8a1d 100644
--- a/lib/timing.c
+++ b/lib/timing.c
@@ -29,10 +29,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* Used to generate random numbers, since the type of `tv_sec' and
-   `tv_usec' is `long int' (from the GNU C Library manual), this function
-   will also return a signed 64-bit integer. */
-int64_t
+/* Micro-second based timer, which can be used to generate random numbers.
+   The type of `tv_sec' and `tv_usec' is `long int' (from the GNU C Library
+   manual), so this function will also return long. */
+long
 gal_timing_time_based_rng_seed()
 {
   struct timeval tv;
diff --git a/lib/txt.c b/lib/txt.c
index 8d4df11..7025307 100644
--- a/lib/txt.c
+++ b/lib/txt.c
@@ -1040,7 +1040,7 @@ make_fmts_for_printf(gal_data_t *datall, int leftadjust, 
size_t *len)
 
       /* If we have a blank value, get the blank value as a string and
          adjust the width */
-      if(gal_blank_present(data)==0)
+      if(gal_blank_present(data, 0)==0)
         fmts[i*FMTS_COLS+2]=NULL;
       else
         {
diff --git a/lib/wcs.c b/lib/wcs.c
index 55d2643..acc4b45 100644
--- a/lib/wcs.c
+++ b/lib/wcs.c
@@ -490,56 +490,16 @@ gal_wcs_pixel_area_arcsec2(struct wcsprm *wcs)
 
 
 /**************************************************************/
-/**********              Conversion                ************/
+/**********            Array conversion            ************/
 /**************************************************************/
-/* Use the X and Y columns in a larger array to fill the RA and Dec columns
-   in that same array. `xy' points to the first element in the X column and
-   `radec' points to the first element in the RA column. The columns for Y
-   and Dec have to be immediately after X and RA.
-
-   It appears that WCSLIB can only deal with static allocation. At least in
-   its tests it only uses static allocation. I tried dynamic allocation,
-   but it didn't work. So I can't use the vector functionalities of WCSLIB
-   and have to translate each point separately.
-*/
-void
-gal_wcs_xy_array_to_radec(struct wcsprm *wcs, double *xy, double *radec,
-                          size_t number, size_t stride)
-{
-  size_t i;
-  double imgcrd[2], phi, theta;
-  int status=0, ncoord=1, nelem=2;
-
-  for(i=0;i<number;++i)
-    {
-      if(isnan(xy[i*stride]) || isnan(xy[i*stride+1]))
-        radec[i*stride]=radec[i*stride+1]=NAN;
-      else
-        {
-          wcsp2s(wcs, ncoord, nelem, xy+i*stride, imgcrd, &phi,
-                 &theta, radec+i*stride, &status);
-          if(status)
-            error(EXIT_FAILURE, 0, "wcsp2s ERROR %d: %s", status,
-                  wcs_errmsg[status]);
-
-          /* For a check:
-             printf("(%f, %f) --> (%f, %f)\n", xy[i*stride], xy[i*stride+1],
-                    radec[i*stride], radec[i*stride+1]);
-          */
-        }
-    }
-}
-
-
-
-
-
 /* Convert an array of world coordinates to image coordinates. Note that in
    Gnuastro, each column is treated independently, so the inputs are
    separate. If `*x==NULL', or `*y==NULL', then space will be allocated for
    them, otherwise, it is assumed that space has already been
-   allocated. Note that they must be a 1 dimensional array (recall that in
-   Gnuastro columns are treated independently).*/
+   allocated. Note that they must each be a 1 dimensional array.
+
+   You can do the conversion in place: just pass the same array as you give
+   to RA and Dec to X and Y. */
 void
 gal_wcs_world_to_img(struct wcsprm *wcs, double *ra, double *dec,
                      double **x, double **y, size_t size)
@@ -549,14 +509,14 @@ gal_wcs_world_to_img(struct wcsprm *wcs, double *ra, 
double *dec,
   double *phi, *theta, *world, *pixcrd, *imgcrd;
 
   /* Allocate all the necessary arrays. */
-  stat=gal_data_calloc_array(GAL_TYPE_INT32, size);
-  phi=gal_data_malloc_array(GAL_TYPE_FLOAT64, size);
-  theta=gal_data_malloc_array(GAL_TYPE_FLOAT64, size);
-  world=gal_data_malloc_array(GAL_TYPE_FLOAT64, 2*size);
-  imgcrd=gal_data_malloc_array(GAL_TYPE_FLOAT64, 2*size);
-  pixcrd=gal_data_malloc_array(GAL_TYPE_FLOAT64, 2*size);
-
-  /* Put in the values. */
+  phi    = gal_data_malloc_array( GAL_TYPE_FLOAT64, size   );
+  stat   = gal_data_calloc_array( GAL_TYPE_INT32,   size   );
+  theta  = gal_data_malloc_array( GAL_TYPE_FLOAT64, size   );
+  world  = gal_data_malloc_array( GAL_TYPE_FLOAT64, 2*size );
+  imgcrd = gal_data_malloc_array( GAL_TYPE_FLOAT64, 2*size );
+  pixcrd = gal_data_malloc_array( GAL_TYPE_FLOAT64, 2*size );
+
+  /* Write the values into the allocated contiguous array. */
   for(i=0;i<size;++i) { world[i*2]=ra[i]; world[i*2+1]=dec[i]; }
 
   /* Use WCSLIB's `wcss2p'. */
@@ -573,8 +533,8 @@ gal_wcs_world_to_img(struct wcsprm *wcs, double *ra, double 
*dec,
   */
 
   /* Allocate the output arrays if they were not already allocated. */
-  if(*x==NULL) *x=gal_data_calloc_array(GAL_TYPE_FLOAT64, size);
-  if(*y==NULL) *y=gal_data_calloc_array(GAL_TYPE_FLOAT64, size);
+  if(*x==NULL) *x=gal_data_malloc_array(GAL_TYPE_FLOAT64, size);
+  if(*y==NULL) *y=gal_data_malloc_array(GAL_TYPE_FLOAT64, size);
 
   /* Put the values into the output arrays. */
   for(i=0;i<size;++i)
@@ -590,3 +550,60 @@ gal_wcs_world_to_img(struct wcsprm *wcs, double *ra, 
double *dec,
   free(world);
   free(pixcrd);
 }
+
+
+
+
+
+/* Similar to `gal_wcs_world_to_img' but converts image coordinates into
+   world coordinates. */
+void
+gal_wcs_img_to_world(struct wcsprm *wcs, double *x, double *y,
+                     double **ra, double **dec, size_t size)
+{
+  size_t i;
+  int status, *stat, ncoord=size, nelem=2;
+  double *phi, *theta, *world, *pixcrd, *imgcrd;
+
+  /* Allocate all the necessary arrays. */
+  phi    = gal_data_malloc_array( GAL_TYPE_FLOAT64, size   );
+  stat   = gal_data_calloc_array( GAL_TYPE_INT32,   size   );
+  theta  = gal_data_malloc_array( GAL_TYPE_FLOAT64, size   );
+  world  = gal_data_malloc_array( GAL_TYPE_FLOAT64, 2*size );
+  imgcrd = gal_data_malloc_array( GAL_TYPE_FLOAT64, 2*size );
+  pixcrd = gal_data_malloc_array( GAL_TYPE_FLOAT64, 2*size );
+
+  /* Write the values into the allocated contiguous array. */
+  for(i=0;i<size;++i) { pixcrd[i*2]=x[i]; pixcrd[i*2+1]=y[i]; }
+
+  /* Use WCSLIB's wcsp2s for the conversion. */
+  status=wcsp2s(wcs, ncoord, nelem, pixcrd, imgcrd, phi, theta, world, stat);
+  if(status)
+    error(EXIT_FAILURE, 0, "wcsp2s ERROR %d: %s", status,
+          wcs_errmsg[status]);
+
+  /* For a sanity check:
+  printf("\n\ngal_wcs_img_to_world sanity check:\n");
+  for(i=0;i<size;++i)
+    printf("img (%f, %f) --> world (%f, %f), [stat: %d]\n",
+           pixcrd[i*2], pixcrd[i*2+1], world[i*2], world[i*2+1], stat[i]);
+  */
+
+  /* Allocate the output arrays if they were not already allocated. */
+  if(*ra==NULL)  *ra  = gal_data_malloc_array(GAL_TYPE_FLOAT64, size);
+  if(*dec==NULL) *dec = gal_data_malloc_array(GAL_TYPE_FLOAT64, size);
+
+  /* Put the values into the output arrays. */
+  for(i=0;i<size;++i)
+    {
+      (*ra)[i]  = stat[i] ? NAN : world[i*2];
+      (*dec)[i] = stat[i] ? NAN : world[i*2+1];
+    }
+
+  /* Clean up. */
+  free(phi);
+  free(stat);
+  free(theta);
+  free(world);
+  free(pixcrd);
+}
diff --git a/tests/during-dev.sh b/tests/during-dev.sh
index 4c2a798..36f6f26 100755
--- a/tests/during-dev.sh
+++ b/tests/during-dev.sh
@@ -68,7 +68,7 @@
 # symbolic link in `tmpfs-config-make', it is also assumed in the version
 # controlled version of this script. Note, if your directory names have
 # space characters in them, quote the full value
-numthreads=8
+numjobs=8
 builddir=build
 outdir=
 
@@ -94,8 +94,8 @@ options=
 # 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"$numthreads" = x ]; then echo "numthreads is not set.";  exit 1; fi
 
 
 # If builddir is relative, then append the current directory to make it
@@ -125,7 +125,7 @@ if [ -f "$utility" ]; then rm "$utility"; fi
 # Before actually running put a copy of the configuration file in the
 # output directory and also add the onlydirconf option so user or system
 # wide configuration files don't interfere.
-if make -j$numthreads -C "$builddir"; then
+if make -j$numjobs -C "$builddir"; then
 
     # Change to the output directory.
     cd "$outdir"
diff --git a/tests/lib/multithread.c b/tests/lib/multithread.c
index 98c1816..13f1b95 100644
--- a/tests/lib/multithread.c
+++ b/tests/lib/multithread.c
@@ -55,7 +55,7 @@ worker(void *inparam)
   size_t i;
 
   /* Go over the jobs indexed for this thread: */
-  for(i=0; prm->indexs[i] != GAL_THREADS_NON_THRD_INDEX; ++i)
+  for(i=0; prm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* The indexes of the actions will make it possible to point to
          whatever data structure or input you want. So in this test, we
@@ -127,7 +127,7 @@ main(void)
 
       /* Spin off the threads: */
       for(i=0;i<numthreads;++i)
-        if(indexs[i*thrdcols]!=GAL_THREADS_NON_THRD_INDEX)
+        if(indexs[i*thrdcols]!=GAL_BLANK_SIZE_T)
           {
             prm[i].id=i;
             prm[i].b=&b;
diff --git a/tests/mkcatalog/aperturephot.sh b/tests/mkcatalog/aperturephot.sh
index 55363a9..38571ec 100755
--- a/tests/mkcatalog/aperturephot.sh
+++ b/tests/mkcatalog/aperturephot.sh
@@ -23,7 +23,7 @@
 # basic checks to see if the executable is made or if the defaults
 # file exists (basicchecks.sh is in the source tree).
 prog=mkcatalog
-objimg=inputascanvas.fits
+objimg=clearcanvas.fits
 execname=../bin/$prog/ast$prog
 img=convolve_spatial_noised_labeled.fits
 
@@ -41,7 +41,9 @@ img=convolve_spatial_noised_labeled.fits
 #
 #   - The input data was not made (for example the test that created the
 #     data file failed).
-if [ ! -f $execname ] || [ ! -f $img ]; then exit 77; fi
+if [ ! -f $execname ]; then echo "$execname doesn't exist."; exit 77; fi
+if [ ! -f $img ];      then echo "$img doesn't exist.";      exit 77; fi
+if [ ! -f $objimg ];   then echo "$objimg doesn't exist";    exit 77; fi
 
 
 
@@ -49,5 +51,6 @@ if [ ! -f $execname ] || [ ! -f $img ]; then exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --objlabs=$objimg --objhdu=0 --output=aperturephot.txt  \
-          --sn --magnitude --dec --ra --y --x
+$execname $img --objectsfile=$objimg --objectshdu=1 \
+          --output=aperturephot.txt                 \
+          --ids --x --y --ra --dec --magnitude --sn
diff --git a/tests/noisechisel/noisechisel.sh b/tests/noisechisel/noisechisel.sh
index 05d0b05..d527c09 100755
--- a/tests/noisechisel/noisechisel.sh
+++ b/tests/noisechisel/noisechisel.sh
@@ -48,4 +48,5 @@ if [ ! -f $execname ] || [ ! -f $img ]; then exit 77; fi
 
 # Actual test script
 # ==================
-$execname $img --checkdetection --checksegmentation --continueaftercheck
+$execname $img --cleandilated --checkdetection --checksegmentation \
+          --continueaftercheck
diff --git a/tmpfs-config-make b/tmpfs-config-make
index cec5f2a..019de25 100755
--- a/tmpfs-config-make
+++ b/tmpfs-config-make
@@ -115,27 +115,15 @@ cd $build_dir
 #
 # FOR DEBUGGING: uncomment the second half of this line. Gnuastro uses GNU
 # Libtool to build shared libraries for highly portable and maintainable
-# usage on a wide variety of systems. While this is great for binaries, it
-# can be a pain when trying to debug. For this reason, compilation of
-# shared libraries can be turned off by specifying the --disable-shared
-# option to configure. With static libraries, compilation (the `make'
-# command) will also significantly speed up. Also, by default (in
-# `configure.ac'), we have set optimization flags which have to be
+# usage on a wide variety of systems. While this is great for binaries,
+# shared libraries can be a pain when debuggin. For this reason,
+# compilation of shared libraries can be turned off by specifying the
+# --disable-shared option to configure. With static libraries, compilation
+# (the `make' command) will also significantly speed up. Also, by default
+# (in `configure.ac'), we have set optimization flags which have to be
 # cancelled in debugging.
-#
-# ####################################
-#
-# Correct this configure command after the datastruct branch is complete
-# and fully merged with master.
-#
-# ####################################
 if [ ! -f Makefile ]; then
-    $srcdir/configure --srcdir=$srcdir                                        \
-                 --enable-arithmetic --enable-convertt   --enable-convolve    \
-                 --enable-cosmiccal  --enable-crop       --enable-fits        \
-                 --enable-mknoise    --enable-mkprof     --enable-noisechisel \
-                 --enable-statistics --enable-table      --enable-warp        \
-                 #CFLAGS="-g -O0" --disable-shared
+    $srcdir/configure --srcdir=$srcdir #CFLAGS="-g -O0" --disable-shared
 fi
 
 



reply via email to

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