gnuastro-commits
[Top][All Lists]
Advanced

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

[gnuastro-commits] master 7988b46: New matching program and library


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 7988b46: New matching program and library
Date: Sat, 2 Dec 2017 14:35:05 -0500 (EST)

branch: master
commit 7988b46d80cd04d993904084cd508ae18a16ca35
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    New matching program and library
    
    A new matching library and program have been added to Gnuastro. Currently
    it only works on catalogs. In the future, it will also return the rotation
    matrix to match images or give the matrix to register two catalogs. So this
    is just the start of an exciting new tool in Gnuastro.
    
    Other changes that were done with this commit:
    
     - We were using custom functions to parse comma-separated floating point
       numbers (possibly as fractions) for each program (in Crop and
       MakeProfiles until this commit). Since we needed a similar operation in
       Match, a new `gal_options_parse_csv_float64' function was defined and is
       now used in those programs is well.
    
     - In the book, in the description of `gal_table_read', we claim that if
       NULL is given to `cols', then the whole table will be returned. But it
       was not behaving like this. It would return NULL when `cols==NULL'. This
       behavior is now corrected to be as described in the book.
    
     - Following the change above, in the Table program, we now pring the
       column in formation cleanly in a separate function.
    
     - The new `gal_box_bound_ellipse_extent' is added to the library to give
       the exact (floating point) extent of an ellipse. `gal_box_bound_ellipse'
       also uses this function now.
    
     - `gal_checkset_dataset_name' is now available to return the file name
       (and possible HDU if its a FITS file) as a string. This is useful in
       error messages where the input can be plain text or FITS.
---
 Makefile.am                                        |  11 +-
 NEWS                                               |  14 +
 README                                             |   3 +
 bin/TEMPLATE/astTEMPLATE.conf                      |   2 +-
 bin/TEMPLATE/ui.c                                  |   3 +-
 bin/crop/args.h                                    |   4 +-
 bin/crop/ui.c                                      |  84 +--
 bin/match/Makefile.am                              |  41 ++
 bin/match/args.h                                   | 145 +++++
 .../astTEMPLATE.conf => match/astmatch.conf}       |  19 +-
 bin/match/authors-cite.h                           |  38 ++
 bin/match/main.c                                   |  58 ++
 bin/match/main.h                                   |  73 +++
 bin/match/match.c                                  | 175 ++++++
 bin/match/match.h                                  |  29 +
 bin/match/ui.c                                     | 667 ++++++++++++++++++++
 bin/match/ui.h                                     |  69 +++
 bin/mkprof/args.h                                  |   8 +-
 bin/mkprof/main.h                                  |   2 +-
 bin/mkprof/ui.c                                    |  57 --
 bin/table/ui.c                                     |  90 ++-
 configure.ac                                       |  12 +-
 doc/announce-acknowledge.txt                       |   3 +-
 doc/gnuastro.texi                                  | 185 +++++-
 doc/release-checklist.txt                          |  36 +-
 lib/Makefile.am                                    |  25 +-
 lib/box.c                                          |  26 +-
 lib/checkset.c                                     |  22 +
 lib/data.c                                         |  12 +-
 lib/gnuastro-internal/checkset.h                   |   5 +-
 lib/gnuastro-internal/options.h                    |   4 +
 lib/gnuastro/box.h                                 |   4 +
 lib/gnuastro/fits.h                                |   1 +
 lib/gnuastro/{box.h => match.h}                    |  35 +-
 lib/match.c                                        | 669 +++++++++++++++++++++
 lib/options.c                                      |  59 ++
 lib/permutation.c                                  |   1 +
 lib/table.c                                        | 288 ++++-----
 lib/txt.c                                          |   3 +
 tests/Makefile.am                                  |  20 +-
 tests/match/positions-1.txt                        |   9 +
 tests/match/positions-2.txt                        |   5 +
 tests/match/positions.sh                           |  52 ++
 tests/prepconf.sh                                  |   5 +-
 44 files changed, 2661 insertions(+), 412 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 2d2308d..80a01b4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -68,6 +68,9 @@ endif
 if COND_FITS
   MAYBE_FITS = bin/fits
 endif
+if COND_MATCH
+  MAYBE_MATCH = bin/match
+endif
 if COND_MKCATALOG
   MAYBE_MKCATALOG = bin/mkcatalog
 endif
@@ -109,10 +112,10 @@ endif
 ## having an uncommented `MAYBE_TEMPLATE' as a value in `SUBDIRS'.
 SUBDIRS = bootstrapped/lib $(MAYBE_GNULIBCHECK) lib $(MAYBE_ARITHMETIC)  \
   $(MAYBE_BUILDPROG) $(MAYBE_CONVERTT) $(MAYBE_CONVOLVE)                 \
-  $(MAYBE_COSMICCAL) $(MAYBE_CROP) $(MAYBE_FITS) $(MAYBE_MKCATALOG)      \
-  $(MAYBE_MKNOISE) $(MAYBE_MKPROF) $(MAYBE_NOISECHISEL)                  \
-  $(MAYBE_STATISTICS) $(MAYBE_TABLE) $(MAYBE_TEMPLATE) $(MAYBE_WARP) doc \
-  tests
+  $(MAYBE_COSMICCAL) $(MAYBE_CROP) $(MAYBE_FITS) $(MAYBE_MATCH)          \
+  $(MAYBE_MKCATALOG) $(MAYBE_MKNOISE) $(MAYBE_MKPROF)                    \
+  $(MAYBE_NOISECHISEL) $(MAYBE_STATISTICS) $(MAYBE_TABLE)                \
+  $(MAYBE_TEMPLATE) $(MAYBE_WARP) doc tests
 
 
 
diff --git a/NEWS b/NEWS
index 160ef2a..32d9e58 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,17 @@ GNU Astronomy Utilities NEWS                          -*- 
outline -*-
 
 ** New features
 
+  Manual/Book: An extended tutorial is added showing some general
+  applications of almost all the programs. This may be a good place to get
+  a feeling of how Gnuastro is intended to be used and some of the
+  programs.
+
+  New Program and library: Match is a new program that will match two given
+  inputs (currently catalogs). Its output is the re-arranged inputs with
+  the same number of rows/records such that all the rows match. The main
+  work is also done with the new `gal_match_catalog' library function which
+  can also be used in other contexts.
+
   All programs: a value of `0' to the `--numthreads' option will use the
   number of threads available to the system at run time.
 
@@ -87,6 +98,9 @@ GNU Astronomy Utilities NEWS                          -*- 
outline -*-
   `gal_txt_write' and `gal_fits_tab_write' now accept an extension name as
   argument to allow a name for the FITS extension they write.
 
+  `gal_box_bound_ellipse_extent' will return the maximum extent of an
+  ellipse along each axis from the ellipse center in floating point.
+
 ** Removed features
 
   MakeCatalog: `--zeropoint' option doesn't have a short option name any
diff --git a/README b/README
index 5d52a6b..45e258e 100644
--- a/README
+++ b/README
@@ -61,6 +61,9 @@ context under categories/chapters.
 
   - Header (astheader): Print and manipulate the header data of a FITS file.
 
+  - Match (astmatch): Given two input catalogs, find the rows that match
+    with each other within a given aperture (may be an ellipse).
+
   - MakeCatalog (astmkcatalog): Make catalog of labeled image (output of
     NoiseChisel). The catalogs are highly customizable and adding new
     calculations/columns is very streightforward.
diff --git a/bin/TEMPLATE/astTEMPLATE.conf b/bin/TEMPLATE/astTEMPLATE.conf
index 3e94701..3fe97e1 100644
--- a/bin/TEMPLATE/astTEMPLATE.conf
+++ b/bin/TEMPLATE/astTEMPLATE.conf
@@ -1,5 +1,5 @@
 # Default parameters (System) for TEMPLATE.
-# TEMPLATE is part of GNU Astronomy Utitlies.
+# TEMPLATE is part of GNU Astronomy Utilities.
 #
 # Use the long option name of each parameter followed by a value. The name
 # and value should be separated by atleast one white-space character (for
diff --git a/bin/TEMPLATE/ui.c b/bin/TEMPLATE/ui.c
index f2f6e4f..8f11ebe 100644
--- a/bin/TEMPLATE/ui.c
+++ b/bin/TEMPLATE/ui.c
@@ -252,7 +252,7 @@ ui_check_options_and_arguments(struct TEMPLATEparams *p)
 /**************************************************************/
 /***************       Preparations         *******************/
 /**************************************************************/
-void
+static void
 ui_preparations(struct TEMPLATEparams *p)
 {
 
@@ -279,7 +279,6 @@ ui_preparations(struct TEMPLATEparams *p)
 /**************************************************************/
 /************         Set the parameters          *************/
 /**************************************************************/
-
 void
 ui_read_check_inputs_setup(int argc, char *argv[], struct TEMPLATEparams *p)
 {
diff --git a/bin/crop/args.h b/bin/crop/args.h
index 114228e..e2e9c3a 100644
--- a/bin/crop/args.h
+++ b/bin/crop/args.h
@@ -150,7 +150,7 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      ui_parse_width_and_center
+      gal_options_parse_csv_float64
     },
     {
       "center",
@@ -164,7 +164,7 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      ui_parse_width_and_center
+      gal_options_parse_csv_float64
     },
 
 
diff --git a/bin/crop/ui.c b/bin/crop/ui.c
index af52d5e..c30b958 100644
--- a/bin/crop/ui.c
+++ b/bin/crop/ui.c
@@ -240,75 +240,6 @@ ui_parse_coordinate_mode(struct argp_option *option, char 
*arg,
 
 
 
-/* Parse the width and center coordinates from the comman-line or
-   configuration files. */
-void *
-ui_parse_width_and_center(struct argp_option *option, char *arg,
-                          char *filename, size_t lineno, void *junk)
-{
-  size_t i, nc;
-  double *darray;
-  gal_data_t *values;
-  char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
-
-  /* We want to print the stored values. */
-  if(lineno==-1)
-    {
-      /* Set the value pointer to the correct type. */
-      values = *(gal_data_t **)(option->value);
-      darray = values->array;
-
-      /* Write the values into a string. */
-      nc=0;
-      for(i=0;i<values->size;++i)
-        {
-          if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
-            error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
-                  "can address the problem. The number of necessary "
-                  "characters in the statically allocated string has become "
-                  "too close to %d", __func__, PACKAGE_BUGREPORT,
-                  GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
-          nc += sprintf(sstr+nc, "%g,", darray[i]);
-        }
-      sstr[nc-1]='\0';
-
-      /* Copy the string into a dynamically allocated space, because it
-         will be freed later.*/
-      gal_checkset_allocate_copy(sstr, &str);
-      return str;
-    }
-  else
-    {
-      /* If the option is already set, then ignore it. */
-      if(option->set) return NULL;
-
-      /* Read the values. */
-      values=gal_options_parse_list_of_numbers(arg, filename, lineno);
-      *(gal_data_t **)(option->value) = values;
-
-      /* If we are on the width option, then make sure none of the values
-         are negative or zero. */
-      if(!strcmp(option->name, "width"))
-        {
-          darray=values->array;
-          for(i=0;i<values->size;++i)
-            if(darray[i]<=0.0f)
-              error_at_line(EXIT_FAILURE, 0, filename, lineno, "%g is <=0. "
-                            "The values to the `--width' option must be "
-                            "larger than zero. The complete input to this "
-                            "option was `%s' (%g is input number %zu)",
-                            darray[i], arg, darray[i], i+1);
-        }
-
-      /* The return value is only for printing mode. */
-      return NULL;
-    }
-}
-
-
-
-
-
 
 
 
@@ -332,7 +263,8 @@ ui_parse_width_and_center(struct argp_option *option, char 
*arg,
 static void
 ui_read_check_only_options(struct cropparams *p)
 {
-  int checksum;
+  double *darray;
+  int i, checksum;
 
   /* Make sure that only one of the crop definitions is given. */
   checksum = ( (p->center!=NULL)
@@ -362,6 +294,18 @@ ui_read_check_only_options(struct cropparams *p)
     }
 
 
+  /* The width values must not be negative. */
+  if(p->width)
+    {
+      darray=p->width->array;
+      for(i=0;i<p->width->size;++i)
+        if(darray[i]<=0.0f)
+          error(EXIT_FAILURE, 0, "%g is <=0. The values to the `--width' "
+                "option must be larger than zero. %g is input number %d to "
+                "this option", darray[i], darray[i], i+1);
+    }
+
+
   /* Section is currentlyl only defined in Image mode. */
   if(p->section && p->mode!=IMGCROP_MODE_IMG)
     error(EXIT_FAILURE, 0, "The `--section' option is only available in "
diff --git a/bin/match/Makefile.am b/bin/match/Makefile.am
new file mode 100644
index 0000000..59595bc
--- /dev/null
+++ b/bin/match/Makefile.am
@@ -0,0 +1,41 @@
+## Process this file with automake to produce Makefile.inx
+##
+## Original author:
+##     Mohammad Akhlaghi <address@hidden>
+## Contributing author(s):
+## Copyright (C) 2017, 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 Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Gnuastro is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## 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/>.
+
+
+## Pre-processer flags (for Gnulib's headers). Recall that the compiled
+## Gnulib library was statically linked to (copied in) Gnuastro's library.
+AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib
+
+
+
+## Program definition (name, linking, sources and headers)
+bin_PROGRAMS = astmatch
+
+astmatch_LDADD = -lgnuastro
+
+astmatch_SOURCES = main.c ui.c match.c
+
+EXTRA_DIST = main.h authors-cite.h args.h ui.h match.h
+
+
+
+## The configuration file (distribute and install).
+## NOTE: the man page is created in doc/Makefile.am
+dist_sysconf_DATA = astmatch.conf
diff --git a/bin/match/args.h b/bin/match/args.h
new file mode 100644
index 0000000..979870f
--- /dev/null
+++ b/bin/match/args.h
@@ -0,0 +1,145 @@
+/*********************************************************************
+Match - A program to match catalogs and WCS warps
+Match is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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 ARGS_H
+#define ARGS_H
+
+
+
+
+
+
+/* Array of acceptable options. */
+struct argp_option program_options[] =
+  {
+    /* Input file parameters. */
+    {
+      "hdu2",
+      GAL_OPTIONS_KEY_HDU,
+      "STR/INT",
+      0,
+      "Extension name or number of second input.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->hdu2,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+
+
+
+
+    /* Outputs. */
+    {
+      "logasoutput",
+      UI_KEY_LOGASOUTPUT,
+      0,
+      0,
+      "No rearranging of inputs, output is log file",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->logasoutput,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+
+
+
+
+    /* Catalog matching. */
+    {
+      0, 0, 0, 0,
+      "Catalog matching",
+      UI_GROUP_CATALOGMATCH
+    },
+    {
+      "ccol1",
+      UI_KEY_CCOL1,
+      "STR",
+      0,
+      "Column name/number of first catalog.",
+      UI_GROUP_CATALOGMATCH,
+      &p->ccol1,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "ccol2",
+      UI_KEY_CCOL2,
+      "STR",
+      0,
+      "Column name/number of second catalog.",
+      UI_GROUP_CATALOGMATCH,
+      &p->ccol2,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "aperture",
+      UI_KEY_APERTURE,
+      "FLT[,FLT[,FLT]]",
+      0,
+      "Acceptable aperture for matching.",
+      UI_GROUP_CATALOGMATCH,
+      &p->aperture,
+      GAL_TYPE_FLOAT64,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_float64
+    },
+
+
+    {0}
+  };
+
+
+
+
+
+/* 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}
+};
+
+/* Set all the necessary argp parameters. */
+struct argp
+thisargp = {program_options, parse_opt, args_doc, doc, children, NULL, NULL};
+#endif
diff --git a/bin/TEMPLATE/astTEMPLATE.conf b/bin/match/astmatch.conf
similarity index 60%
copy from bin/TEMPLATE/astTEMPLATE.conf
copy to bin/match/astmatch.conf
index 3e94701..5946c69 100644
--- a/bin/TEMPLATE/astTEMPLATE.conf
+++ b/bin/match/astmatch.conf
@@ -1,5 +1,5 @@
-# Default parameters (System) for TEMPLATE.
-# TEMPLATE is part of GNU Astronomy Utitlies.
+# Default parameters (System) for Match.
+# Match is part of GNU Astronomy Utilities.
 #
 # Use the long option name of each parameter followed by a value. The name
 # and value should be separated by atleast one white-space character (for
@@ -7,12 +7,21 @@
 #
 # For more information, please run these commands:
 #
-#  $ astTEMPLATE --help                  # Full list of options, short doc.
-#  $ astTEMPLATE -P                      # Print all options and used values.
-#  $ info astTEMPLATE                    # All options and input/output.
+#  $ astmatch --help                     # Full list of options, short doc.
+#  $ astmatch -P                         # Print all options and used values.
+#  $ info astmatch                       # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
 # this notice are preserved.  This file is offered as-is, without any
 # warranty.
+
+# Input
+ hdu2            1
+
+# Catalog matching
+ ccol1           2
+ ccol1           3
+ ccol2           2
+ ccol2           3
\ No newline at end of file
diff --git a/bin/match/authors-cite.h b/bin/match/authors-cite.h
new file mode 100644
index 0000000..8a5efbd
--- /dev/null
+++ b/bin/match/authors-cite.h
@@ -0,0 +1,38 @@
+/*********************************************************************
+Match - A program to match catalogs and WCS warps
+Match is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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 AUTHORS_CITE_H
+#define AUTHORS_CITE_H
+
+/* When any specific citation is necessary, please add its BibTeX (from ADS
+   hopefully) to this variable along with a title decribing what this
+   paper/book does for the progarm in a short line. In the following line
+   put a row of `-' with the same length and then put the BibTeX.
+
+   See the `gnuastro_bibtex' variable in `lib/options' (from the top
+   Gnuastro source code directory) as an example.*/
+
+#define PROGRAM_BIBTEX ""
+
+#define PROGRAM_AUTHORS "Mohammad Akhlaghi"
+
+#endif
diff --git a/bin/match/main.c b/bin/match/main.c
new file mode 100644
index 0000000..8186ecd
--- /dev/null
+++ b/bin/match/main.c
@@ -0,0 +1,58 @@
+/*********************************************************************
+Match - A program to match catalogs and WCS warps
+Match is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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/>.
+**********************************************************************/
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <gnuastro-internal/timing.h>
+
+#include "main.h"
+
+#include "ui.h"
+#include "match.h"
+
+
+/* Main function */
+int
+main (int argc, char *argv[])
+{
+  struct timeval t1;
+  struct matchparams p={{{0},0},0};
+
+  /* Set they starting time. */
+  time(&p.rawtime);
+  gettimeofday(&t1, NULL);
+
+  /* Read the input parameters. */
+  ui_read_check_inputs_setup(argc, argv, &p);
+
+  /* Run MakeProfiles */
+  match(&p);
+
+  /* Free all non-freed allocations. */
+  ui_free_report(&p, &t1);
+
+  /* Return successfully.*/
+  return EXIT_SUCCESS;
+}
diff --git a/bin/match/main.h b/bin/match/main.h
new file mode 100644
index 0000000..f0becfe
--- /dev/null
+++ b/bin/match/main.h
@@ -0,0 +1,73 @@
+/*********************************************************************
+Match - A program to match catalogs and WCS warps
+Match is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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 MAIN_H
+#define MAIN_H
+
+/* Include necessary headers */
+#include <gnuastro/data.h>
+
+#include <gnuastro-internal/options.h>
+
+/* Progarm names.  */
+#define PROGRAM_NAME   "Match"    /* Program full name.       */
+#define PROGRAM_EXEC   "astmatch" /* Program executable name. */
+#define PROGRAM_STRING PROGRAM_NAME" (" PACKAGE_NAME ") " PACKAGE_VERSION
+
+
+
+enum match_modes
+{
+  MATCH_MODE_INVALID,           /* ==0 by default. */
+  MATCH_MODE_WCS,
+  MATCH_MODE_CATALOG,
+};
+
+
+
+
+/* Main program parameters structure */
+struct matchparams
+{
+  /* From command-line */
+  struct gal_options_common_params     cp; /* Common parameters.        */
+  char            *input1name;  /* First input filename.                */
+  char            *input2name;  /* Second input filename.               */
+  char                  *hdu2;  /* Second input's HDU.                  */
+  gal_list_str_t       *ccol1;  /* Column names/numbers of first cat.   */
+  gal_list_str_t       *ccol2;  /* Column names/numbers of first cat.   */
+  gal_data_t        *aperture;  /* Acceptable matching aperture.        */
+  uint8_t         logasoutput;  /* Don't rearrange inputs, out is log.  */
+
+  /* Internal */
+  int                    mode;  /* Mode of operation: image or catalog. */
+  gal_data_t           *cols1;  /* Column values of first input.        */
+  gal_data_t           *cols2;  /* Column values of second input.       */
+  char               *logname;  /* Name of log file.                    */
+  char              *out1name;  /* Name of first matched output.        */
+  char              *out2name;  /* Name of second matched output.       */
+
+  /* Output: */
+  time_t              rawtime;  /* Starting time of the program.        */
+};
+
+#endif
diff --git a/bin/match/match.c b/bin/match/match.c
new file mode 100644
index 0000000..9c533d2
--- /dev/null
+++ b/bin/match/match.c
@@ -0,0 +1,175 @@
+/*********************************************************************
+Match - A program to match catalogs and WCS warps
+Match is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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/>.
+**********************************************************************/
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gnuastro/match.h>
+#include <gnuastro/table.h>
+
+#include <main.h>
+
+
+
+
+
+/* Read the catalog in the given file and use the given permutation to keep
+   the proper columns. */
+static void
+match_catalog_write(struct matchparams *p, char *filename, char *hdu,
+                    gal_data_t *permutation, char *outname, char *extname)
+{
+  size_t i, *perm=permutation->array;
+  gal_data_t *tmp, *cat, *newcol, *newcat=NULL;
+
+  /* Read the full table. */
+  cat=gal_table_read(filename, hdu, NULL,p->cp.searchin, p->cp.ignorecase,
+                     p->cp.minmapsize);
+
+  /* Go over each column and make a new copy. */
+  for(tmp=cat; tmp!=NULL; tmp=tmp->next)
+    {
+      /* Allocate space for the new column. */
+      newcol=gal_data_alloc(NULL, tmp->type, 1, &permutation->size,
+                            NULL, 0, p->cp.minmapsize, tmp->name,
+                            tmp->unit, tmp->comment);
+
+      /* Copy the elements from the old column to the new one. We can't use
+         the permute functions because the number of elements in
+         `permutation', might (will probably) be different from the number
+         of elements in the columns.*/
+      for(i=0;i<permutation->size;++i)
+        memcpy( gal_data_ptr_increment(newcol->array, i,       tmp->type),
+                gal_data_ptr_increment(tmp->array,    perm[i], tmp->type),
+                gal_type_sizeof(tmp->type) );
+
+      /* Add the new column to the new catalog. */
+      gal_list_data_add(&newcat, newcol);
+    }
+
+  /* Reverse the columns to match the input, free the input catalog and
+     return.*/
+  gal_list_data_reverse(&newcat);
+
+  /* Write the catalog to the output. */
+  gal_table_write(newcat, NULL, p->cp.tableformat, outname, extname);
+
+  /* Clean up. */
+  gal_list_data_free(cat);
+  gal_list_data_free(newcat);
+}
+
+
+
+
+
+static void
+match_catalog(struct matchparams *p)
+{
+  uint32_t *u, *uf;
+  gal_data_t *tmp, *mcols;
+
+  /* Find the matching coordinates. */
+  mcols=gal_match_coordinates(p->cols1, p->cols2, p->aperture, 0, 1,
+                              p->cp.minmapsize);
+
+  /* Read all the first catalog columns. */
+  if(p->logasoutput==0)
+    {
+      match_catalog_write(p, p->input1name, p->cp.hdu, mcols,
+                          p->out1name, "INPUT_1");
+      match_catalog_write(p, p->input2name, p->cp.hdu, mcols->next,
+                          p->out2name, "INPUT_2");
+    }
+
+  /* Write the raw information in a log file if necessary.  */
+  if(p->logname)
+    {
+      /* Note that unsigned 64-bit integers are not recognized in FITS
+         tables. So if the log file is a FITS table, covert the two
+         index columns to uint32. */
+      tmp=gal_data_copy_to_new_type(mcols, GAL_TYPE_UINT32);
+      tmp->next=mcols->next;
+      gal_data_free(mcols);
+      mcols=tmp;
+
+      /* We also want everything to be incremented by one. In a C program,
+         counting starts with zero, so `gal_match_coordinates' will return
+         indexs starting from zero. But outside a C program, on the
+         command-line people expect counting to start from 1 (for example
+         with AWK). */
+      uf = (u=mcols->array) + tmp->size; do (*u)++; while(++u<uf);
+
+      /* Same for the second set of indexs. */
+      tmp=gal_data_copy_to_new_type(mcols->next, GAL_TYPE_UINT32);
+      uf = (u=tmp->array) + tmp->size; do (*u)++; while(++u<uf);
+      tmp->next=mcols->next->next;
+      gal_data_free(mcols->next);
+      mcols->next=tmp;
+
+      /* Correct the comments. */
+      free(mcols->comment);
+      mcols->comment="Row index in first catalog (counting from 1).";
+      free(mcols->next->comment);
+      mcols->next->comment="Row index in second catalog (counting from 1).";
+
+      /* Write them into the table. */
+      gal_table_write(mcols, NULL, p->cp.tableformat, p->logname, "LOG_INFO");
+
+      /* Set the comment pointer to NULL: they weren't allocated. */
+      mcols->comment=NULL;
+      mcols->next->comment=NULL;
+    }
+  gal_list_data_free(mcols);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*******************************************************************/
+/*************            Top level function           *************/
+/*******************************************************************/
+void
+match(struct matchparams *p)
+{
+  if(p->mode==MATCH_MODE_CATALOG)
+    match_catalog(p);
+}
diff --git a/bin/match/match.h b/bin/match/match.h
new file mode 100644
index 0000000..6911dc4
--- /dev/null
+++ b/bin/match/match.h
@@ -0,0 +1,29 @@
+/*********************************************************************
+Match - A program to match catalogs and WCS warps
+Match is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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 MATCH_H
+#define MATCH_H
+
+void
+match(struct matchparams *p);
+
+#endif
diff --git a/bin/match/ui.c b/bin/match/ui.c
new file mode 100644
index 0000000..433213b
--- /dev/null
+++ b/bin/match/ui.c
@@ -0,0 +1,667 @@
+/*********************************************************************
+Match - A program to match catalogs and WCS warps
+Match is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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/>.
+**********************************************************************/
+#include <config.h>
+
+#include <argp.h>
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+
+#include <gnuastro/fits.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 "ui.h"
+#include "authors-cite.h"
+
+
+
+
+
+/**************************************************************/
+/*********      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" matches catalogs of objects "
+  "or returns the warping matrix necessary to match two images.\n"
+  GAL_STRINGS_MORE_HELP_INFO
+  /* After the list of options: */
+  "\v"
+  PACKAGE_NAME" home page: "PACKAGE_URL;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/*********    Initialize & Parse command-line    **************/
+/**************************************************************/
+static void
+ui_initialize_options(struct matchparams *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->poptions           = program_options;
+  cp->program_name       = PROGRAM_NAME;
+  cp->program_exec       = PROGRAM_EXEC;
+  cp->program_bibtex     = PROGRAM_BIBTEX;
+  cp->program_authors    = PROGRAM_AUTHORS;
+  cp->coptions           = gal_commonopts_options;
+
+
+  /* Modify common options. */
+  for(i=0; !gal_options_is_last(&cp->coptions[i]); ++i)
+    {
+      /* Select individually. */
+      switch(cp->coptions[i].key)
+        {
+        case GAL_OPTIONS_KEY_HDU:
+          cp->coptions[i].doc="Extension name or number of first input.";
+          break;
+        }
+
+      /* Select by group. */
+      switch(cp->coptions[i].group)
+        {
+        case GAL_OPTIONS_GROUP_TESSELLATION:
+          cp->coptions[i].doc=NULL; /* Necessary to remove title. */
+          cp->coptions[i].flags=OPTION_HIDDEN;
+          break;
+        }
+    }
+}
+
+
+
+
+
+/* Parse a single option: */
+error_t
+parse_opt(int key, char *arg, struct argp_state *state)
+{
+  struct matchparams *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->input1name)
+        {
+          if(p->input2name)
+            argp_error(state, "only two arguments (input files) should be "
+                       "given");
+          else
+            p->input2name=arg;
+        }
+      else
+        p->input1name=arg;
+      break;
+
+
+    /* This is an option, set its value. */
+    default:
+      return gal_options_set_from_key(key, arg, p->cp.poptions, &p->cp);
+    }
+
+  return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/***************       Sanity Check         *******************/
+/**************************************************************/
+/* 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 matchparams *p)
+{
+
+}
+
+
+
+
+
+static void
+ui_check_options_and_arguments(struct matchparams *p)
+{
+  /* Make sure two input file names were given and if they a FITS file,
+     that a HDU is also given for each. */
+  if(p->input1name)
+    {
+      if( gal_fits_name_is_fits(p->input1name) && p->cp.hdu==NULL )
+        error(EXIT_FAILURE, 0, "no HDU for first input. 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");
+    }
+  else
+    error(EXIT_FAILURE, 0, "no input file is specified: two inputs are "
+          "necessary");
+
+  if(p->input2name)
+    {
+      if( gal_fits_name_is_fits(p->input2name) && p->hdu2==NULL )
+        error(EXIT_FAILURE, 0, "no HDU for second input. Please use the "
+              "`--hdu2' (`-H') option and give it the HDU number (starting "
+              "from zero), extension name, or anything acceptable by "
+              "CFITSIO");
+    }
+  else
+    error(EXIT_FAILURE, 0, "second input file not specified: two inputs are "
+          "necessary");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/***************       Preparations         *******************/
+/**************************************************************/
+static void
+ui_set_mode(struct matchparams *p)
+{
+  /* Check if we are in image or catalog mode. We will base the mode on the
+     first input, then check with the second. */
+  if( gal_fits_name_is_fits(p->input1name) )
+    p->mode = ( (gal_fits_hdu_format(p->input1name, p->cp.hdu) == IMAGE_HDU)
+                ? MATCH_MODE_WCS
+                : MATCH_MODE_CATALOG );
+  else
+    p->mode=MATCH_MODE_CATALOG;
+
+  /* Now that the mode is set, check the second input's type. */
+  if( gal_fits_name_is_fits(p->input2name) )
+    {
+      if(gal_fits_hdu_format(p->input2name, p->hdu2) == IMAGE_HDU)
+        {
+          if( p->mode==MATCH_MODE_CATALOG)
+            error(EXIT_FAILURE, 0, "%s is a catalog, while %s is an image. "
+                  "Both inputs have to be images or catalogs",
+                  gal_checkset_dataset_name(p->input1name, p->cp.hdu),
+                  gal_checkset_dataset_name(p->input2name, p->hdu2) );
+        }
+      else
+        {
+          if( p->mode==MATCH_MODE_WCS)
+            error(EXIT_FAILURE, 0, "%s is an image, while %s is a catalog. "
+                  "Both inputs have to be images or catalogs",
+                  gal_checkset_dataset_name(p->input1name, p->cp.hdu),
+                  gal_checkset_dataset_name(p->input2name, p->hdu2));
+        }
+    }
+  else
+    if(p->mode==MATCH_MODE_WCS)
+      error(EXIT_FAILURE, 0, "%s is an image, while %s is a catalog! Both "
+            "inputs have to be images or catalogs",
+            gal_checkset_dataset_name(p->input1name, p->cp.hdu),
+            gal_checkset_dataset_name(p->input2name, p->hdu2));
+}
+
+
+
+
+
+/* The final aperture must have the following values:
+
+       p->aperture[0]: Major axis length.
+       p->aperture[1]: Axis ratio.
+       p->aperture[2]: Position angle (relative to first dim).     */
+static void
+ui_read_columns_aperture(struct matchparams *p, size_t numcols)
+{
+  size_t apersize=3;
+  gal_data_t *newaper=NULL;
+  double *naper, *oaper=p->aperture->array;
+
+  /* A general sanity check: the first two elements of aperture cannot be
+     zero or negative. */
+  if( oaper[0]<=0 )
+    error(EXIT_FAILURE, 0, "the first value of `--aperture' cannot be "
+          "zero or negative");
+  if( p->aperture->size>1 && oaper[1]<=0 )
+    error(EXIT_FAILURE, 0, "the second value of `--aperture' cannot be "
+          "zero or negative");
+
+  /* Will be needed in more than one case. */
+  if(p->aperture->size!=3)
+    {
+      newaper=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &apersize, NULL,
+                             0, -1, NULL, NULL, NULL);
+      naper=newaper->array;
+    }
+
+  /* Different based on  */
+  switch(p->aperture->size)
+    {
+    case 1:
+      naper[0]=oaper[0];
+      naper[1]=1;
+      naper[2]=0;
+      break;
+
+    case 2:
+      naper[0] = oaper[0]>oaper[1] ? oaper[0]          : oaper[1];
+      naper[1] = oaper[0]>oaper[1] ? oaper[1]/oaper[0] : oaper[0]/oaper[1];
+      naper[2] = oaper[0]>oaper[1] ? 0                 : 90;
+      break;
+
+    case 3:
+      if(oaper[1]>1)
+        error(EXIT_FAILURE, 0, "second value to `--aperture' is larger "
+              "than one. When three numbers are given to this option, the "
+              "second is the axis ratio (which must always be less than 1).");
+      break;
+
+    default:
+      error(EXIT_FAILURE, 0, "%zu values given to `--aperture'. This "
+            "option can only take 1, 2, or 3 values", p->aperture->size);
+    }
+
+  /* If a new aperture was defined, then replace it with the exitsting
+     one. */
+  if(newaper)
+    {
+      gal_data_free(p->aperture);
+      p->aperture=newaper;
+    }
+}
+
+
+
+
+
+/* Read catalog columns */
+static void
+ui_read_columns(struct matchparams *p)
+{
+  struct gal_options_common_params *cp=&p->cp;
+  size_t ccol1n=gal_list_str_number(p->ccol1);
+  size_t ccol2n=gal_list_str_number(p->ccol2);
+  char *diff_cols_error="%s: the number of columns matched (%zu) "
+    "differs from the number of usable calls to `--ccol1' (%zu). "
+    "Please give more specific values to `--ccol1' (column "
+    "numberes are the only identifiers guaranteed to be unique).";
+
+  /* First check the number of columns given. It might happen that extra
+     columns are present in configuration files, so we will only abort if
+     the total number isn't enough. When it is too much, we'll just free
+     the rest of the list.*/
+  if(ccol1n<2 || ccol2n<2)
+    error(EXIT_FAILURE, 0, "at least two coordinate columns from each "
+          "catalog must be given for the match. Please use repeated "
+          "calls to `--ccol1' and `--ccol2' to specify the columns by "
+          "name (if they have one) or number (starting from 1).\n\n"
+          "You can use this command to list the column information of "
+          "a table in the N-th extension/HDU of a FITS file:\n\n"
+          "    $ asttable filename.fits -hN -i\n\n"
+          "For more information on selecting table columns in Gnuastro, "
+          "please run the following command:\n\n"
+          "    $ info gnuastro \"selecting table columns\"\n");
+  else if(ccol1n>2 || ccol2n>2)
+    {
+      if(ccol1n>2)
+        {
+          gal_list_str_free(p->ccol1->next->next, 1);
+          p->ccol1->next->next=NULL;
+          ccol1n=2;             /* Will be used later. */
+        }
+      if(ccol2n>2)
+        {
+          gal_list_str_free(p->ccol2->next->next, 1);
+          p->ccol2->next->next=NULL;
+          ccol2n=2;             /* Will be used later. */
+        }
+    }
+
+
+  /* Read/check the aperture values. */
+  if(p->aperture)
+    ui_read_columns_aperture(p, ccol1n);
+  else
+    error(EXIT_FAILURE, 0, "no matching aperture specified. Please use "
+          "the `--aperture' option to define the acceptable aperture for "
+          "matching the coordinates (in the same units as each "
+          "dimension). Please run the following command for more "
+          "information.\n\n    $ info %s\n", PROGRAM_EXEC);
+
+
+  /* Read the columns. */
+  if(cp->searchin)
+    {
+      /* Read the first dataset. */
+      p->cols1=gal_table_read(p->input1name, cp->hdu, p->ccol1, cp->searchin,
+                              cp->ignorecase, cp->minmapsize);
+      if(gal_list_data_number(p->cols1)!=ccol1n)
+        error(EXIT_FAILURE, 0, diff_cols_error,
+              gal_checkset_dataset_name(p->input1name, cp->hdu),
+              gal_list_data_number(p->cols1), ccol1n);
+
+      /* Read the second dataset. */
+      p->cols2=gal_table_read(p->input2name, p->hdu2, p->ccol2, cp->searchin,
+                              cp->ignorecase, cp->minmapsize);
+      if(gal_list_data_number(p->cols2)!=ccol2n)
+        error(EXIT_FAILURE, 0, diff_cols_error,
+              gal_checkset_dataset_name(p->input2name, p->hdu2),
+              gal_list_data_number(p->cols2), ccol2n);
+    }
+  else
+    error(EXIT_FAILURE, 0, "no `--searchin' option specified. Please run "
+          "the following command for more information:\n\n"
+          "    $ info gnuastro \"selecting table columns\"\n");
+}
+
+
+
+
+
+static void
+ui_preparations_out_name(struct matchparams *p)
+{
+  if(p->logasoutput)
+    {
+      /* Set the logname (as output). */
+      if(p->cp.output)
+        gal_checkset_allocate_copy(p->cp.output, &p->logname);
+      else
+        {
+          if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT)
+            p->logname=gal_checkset_automatic_output(&p->cp, p->input1name,
+                                                     "_matched.txt");
+          else
+            p->logname=gal_checkset_automatic_output(&p->cp, p->input1name,
+                                                     "_matched.fits");
+        }
+
+      /* Make sure a file with this name doesn't exist. */
+      gal_checkset_writable_remove(p->out1name, 0, p->cp.dontdelete);
+    }
+  else
+    {
+      /* Set `p->out1name' and `p->out2name'. */
+      if(p->cp.output)
+        {
+          if( gal_fits_name_is_fits(p->cp.output) )
+            {
+              gal_checkset_allocate_copy(p->cp.output, &p->out1name);
+              gal_checkset_allocate_copy(p->cp.output, &p->out2name);
+            }
+          else
+            {
+              p->out1name=gal_checkset_automatic_output(&p->cp, p->cp.output,
+                                                        "_matched_1.txt");
+              p->out2name=gal_checkset_automatic_output(&p->cp, p->cp.output,
+                                                        "_matched_2.txt");
+            }
+        }
+      else
+        {
+          if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT)
+            {
+              p->out1name=gal_checkset_automatic_output(&p->cp, p->input1name,
+                                                        "_matched_1.txt");
+              p->out2name=gal_checkset_automatic_output(&p->cp, p->input2name,
+                                                        "_matched_2.txt");
+            }
+          else
+            {
+              p->out1name=gal_checkset_automatic_output(&p->cp, p->input1name,
+                                                        "_matched.fits");
+              gal_checkset_allocate_copy(p->out1name, &p->out2name);
+            }
+        }
+
+      /* Make sure no file with these names exists. */
+      gal_checkset_writable_remove(p->out1name, 0, p->cp.dontdelete);
+      gal_checkset_writable_remove(p->out2name, 0, p->cp.dontdelete);
+
+      /* If a log file is necessary, set its name here. */
+      if(p->cp.log)
+        {
+          p->logname = ( p->cp.tableformat==GAL_TABLE_FORMAT_TXT
+                         ? PROGRAM_EXEC".txt"
+                         : PROGRAM_EXEC".fits" );
+          gal_checkset_writable_remove(p->logname, 0, p->cp.dontdelete);
+        }
+    }
+}
+
+
+
+
+
+static void
+ui_preparations(struct matchparams *p)
+{
+  /* Set the mode of the program. */
+  ui_set_mode(p);
+
+  /* Currently Match only works on catalogs. */
+  if(p->mode==MATCH_MODE_WCS)
+    error(EXIT_FAILURE, 0, "currently Match only works on catalogs, we will "
+          "implement the WCS matching routines later");
+  else
+    ui_read_columns(p);
+
+  /* Set the output filename. */
+  ui_preparations_out_name(p);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/************         Set the parameters          *************/
+/**************************************************************/
+void
+ui_read_check_inputs_setup(int argc, char *argv[], struct matchparams *p)
+{
+  struct gal_options_common_params *cp=&p->cp;
+
+
+  /* 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"
+
+
+  /* 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");
+
+
+  /* Read the configuration files and set the common values. */
+  gal_options_read_config_set(&p->cp);
+
+
+  /* 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);
+
+
+  /* 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);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/************      Free allocated, report         *************/
+/**************************************************************/
+void
+ui_free_report(struct matchparams *p, struct timeval *t1)
+{
+  /* Free the allocated arrays: */
+  free(p->cp.hdu);
+  free(p->out1name);
+  free(p->out2name);
+  free(p->cp.output);
+
+  /* Print the final message.
+  if(!p->cp.quiet)
+    gal_timing_report(t1, PROGRAM_NAME" finished in: ", 0);
+  */
+}
diff --git a/bin/match/ui.h b/bin/match/ui.h
new file mode 100644
index 0000000..d5d9d7e
--- /dev/null
+++ b/bin/match/ui.h
@@ -0,0 +1,69 @@
+/*********************************************************************
+Match - A program to match catalogs and WCS warps
+Match is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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 UI_H
+#define UI_H
+
+/* For common options groups. */
+#include <gnuastro-internal/options.h>
+
+
+
+/* Group codes. */
+enum program_args_groups
+{
+  UI_GROUP_CATALOGMATCH = GAL_OPTIONS_GROUP_AFTER_COMMON,
+};
+
+
+
+
+
+/* Available letters for short options:
+
+   b c d e f g i j k m n p r s t u v w x y z
+   A B C E G J L O Q R W X Y
+*/
+enum option_keys_enum
+{
+  /* With short-option version. */
+  UI_KEY_HDU2            = 'H',
+  UI_KEY_APERTURE        = 'a',
+  UI_KEY_LOGASOUTPUT     = 'l',
+
+  /* Only with long version (start with a value 1000, the rest will be set
+     automatically). */
+  UI_KEY_CCOL1           = 1000,
+  UI_KEY_CCOL2,
+};
+
+
+
+
+
+void
+ui_read_check_inputs_setup(int argc, char *argv[], struct matchparams *p);
+
+void
+ui_free_report(struct matchparams *p, struct timeval *t1);
+
+#endif
diff --git a/bin/mkprof/args.h b/bin/mkprof/args.h
index f5e4f6c..57889c4 100644
--- a/bin/mkprof/args.h
+++ b/bin/mkprof/args.h
@@ -472,7 +472,7 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      ui_parse_numbers
+      gal_options_parse_csv_float64
     },
     {
       "crval",
@@ -486,7 +486,7 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      ui_parse_numbers
+      gal_options_parse_csv_float64
     },
     {
       "cdelt",
@@ -500,7 +500,7 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      ui_parse_numbers
+      gal_options_parse_csv_float64
     },
     {
       "pc",
@@ -514,7 +514,7 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      ui_parse_numbers
+      gal_options_parse_csv_float64
     },
     {
       "cunit",
diff --git a/bin/mkprof/main.h b/bin/mkprof/main.h
index 88cfe3a..90906b5 100644
--- a/bin/mkprof/main.h
+++ b/bin/mkprof/main.h
@@ -40,7 +40,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 /* Some constants */
 #define EPSREL_FOR_INTEG   2
-#define DEGREESTORADIANS   M_PI/180.0f
+#define DEGREESTORADIANS   M_PI/180.0
 
 
 /* Modes to interpret coordinates. */
diff --git a/bin/mkprof/ui.c b/bin/mkprof/ui.c
index 8dbc372..897bd58 100644
--- a/bin/mkprof/ui.c
+++ b/bin/mkprof/ui.c
@@ -398,63 +398,6 @@ ui_parse_kernel(struct argp_option *option, char *arg,
 
 
 
-/* Parse options with values of a list of numbers. */
-void *
-ui_parse_numbers(struct argp_option *option, char *arg,
-                 char *filename, size_t lineno, void *junk)
-{
-  int i;
-  size_t nc;
-  double *darr;
-  gal_data_t *values;
-  char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
-
-  /* We want to print the stored values. */
-  if(lineno==-1)
-    {
-      /* Set the pointer to the values dataset. */
-      values = *(gal_data_t **)(option->value);
-
-      /* Write each string into the output string */
-      nc=0;
-      darr=values->array;
-      for(i=0;i<values->size;++i)
-        {
-          if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
-            error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
-                  "can address the problem. The number of necessary "
-                  "characters in the statically allocated string has become "
-                  "too close to %d", __func__, PACKAGE_BUGREPORT,
-                  GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
-          nc += sprintf(sstr+nc, "%g,", darr[i]);
-        }
-      sstr[nc-1]='\0';
-
-      /* Copy the string into a dynamically allocated space, because it
-         will be freed later.*/
-      gal_checkset_allocate_copy(sstr, &str);
-      return str;
-    }
-
-  /* We want to read the user's string. */
-  else
-    {
-      /* If the option is already set, just return. */
-      if(option->set) return NULL;
-
-      /* Read the values. */
-      values=gal_options_parse_list_of_numbers(arg, filename, lineno);
-
-      /* Put the values into the option. */
-      *(gal_data_t **)(option->value) = values;
-      return NULL;
-    }
-}
-
-
-
-
-
 /* Parse the mode to interpret the given coordinates. */
 void *
 ui_parse_coordinate_mode(struct argp_option *option, char *arg,
diff --git a/bin/table/ui.c b/bin/table/ui.c
index 806b307..f60e708 100644
--- a/bin/table/ui.c
+++ b/bin/table/ui.c
@@ -263,68 +263,59 @@ ui_check_options_and_arguments(struct tableparams *p)
 /***************       Preparations         *******************/
 /**************************************************************/
 void
-ui_preparations(struct tableparams *p)
+ui_print_info_exit(struct tableparams *p)
 {
   char *tmp;
   int tableformat;
   gal_data_t *allcols;
   size_t i, numcols, numrows;
-  struct gal_options_common_params *cp=&p->cp;
 
-  /* If there were no columns specified or the user has asked for
-     information on the columns, we want the full set of columns. */
-  if(p->columns==NULL || p->information)
-    {
-      /* Read the table information for the number of columns and rows. */
-      allcols=gal_table_info(p->filename, cp->hdu, &numcols,
-                             &numrows, &tableformat);
+  /* Read the table information for the number of columns and rows. */
+  allcols=gal_table_info(p->filename, p->cp.hdu, &numcols,
+                         &numrows, &tableformat);
 
-      /* If there was no actual data in the file, then inform the user */
-      if(allcols==NULL)
-        error(EXIT_FAILURE, 0, "%s: no usable data rows", p->filename);
+  /* If there was no actual data in the file, then inform the user */
+  if(allcols==NULL)
+    error(EXIT_FAILURE, 0, "%s: no usable data rows", p->filename);
 
 
-      /* If the user just wanted information, then print it. */
-      if(p->information)
-        {
-          /* Print the file information. */
-          printf("--------\n");
-          tmp=gal_fits_name_save_as_string(p->filename, p->cp.hdu);
-          printf("%s\n", tmp);
-          free(tmp);
-
-          /* Print each column's information. */
-          gal_table_print_info(allcols, numcols, numrows);
-        }
+  /* Print the file information. */
+  printf("--------\n");
+  tmp=gal_fits_name_save_as_string(p->filename, p->cp.hdu);
+  printf("%s\n", tmp);
+  free(tmp);
 
 
-      /* Free the information from all the columns. */
-      for(i=0;i<numcols;++i)
-        gal_data_free_contents(&allcols[i]);
-      free(allcols);
+  /* Print each column's information. */
+  gal_table_print_info(allcols, numcols, numrows);
 
 
-      /* If the user just wanted information, then free the allocated
-         spaces and exit. Otherwise, add the number of columns to the list
-         if the user wanted to print the columns (didn't just want their
-         information. */
-      if(p->information)
-        {
-          ui_free_report(p);
-          exit(EXIT_SUCCESS);
-        }
-      else
-        /* The user wants to read the column values, so put all the column
-           numbers into the list (as strings). Note that we will write the
-           column numbers into the list in reverse order. This way, they
-           are read/popped in the proper order. Recall that this is a
-           last-in-first-out list. */
-        for(i=numcols;i>0;--i)
-          {
-            asprintf(&tmp, "%zu", i);
-            gal_list_str_add(&p->columns, tmp, 0);
-          }
-    }
+  /* Free the information from all the columns. */
+  for(i=0;i<numcols;++i)
+    gal_data_free_contents(&allcols[i]);
+  free(allcols);
+
+
+  /* Free the allocated spaces and exit. Otherwise, add the number of
+     columns to the list if the user wanted to print the columns
+     (didn't just want their information. */
+  ui_free_report(p);
+  exit(EXIT_SUCCESS);
+}
+
+
+
+
+
+void
+ui_preparations(struct tableparams *p)
+{
+  struct gal_options_common_params *cp=&p->cp;
+
+  /* If there were no columns specified or the user has asked for
+     information on the columns, we want the full set of columns. */
+  if(p->information)
+    ui_print_info_exit(p);
 
   /* Read in the table columns. */
   p->table=gal_table_read(p->filename, cp->hdu, p->columns, cp->searchin,
@@ -339,6 +330,7 @@ ui_preparations(struct tableparams *p)
   /* Now that the data columns are ready, we can free the string linked
      list. */
   gal_list_str_free(p->columns, 1);
+  p->columns=NULL;
 }
 
 
diff --git a/configure.ac b/configure.ac
index 377c203..455d9eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -559,9 +559,15 @@ AC_ARG_ENABLE([crop],
 AC_ARG_ENABLE([fits],
               [AS_HELP_STRING([--enable-fits],
                     [Install Fits and other enabled programs.])],
-             [AS_IF([test "x$enable_Fits" != xno],
+             [AS_IF([test "x$enable_fits" != xno],
                      [enable_fits=yes; ayes=true])],
               [enable_fits=notset])
+AC_ARG_ENABLE([match],
+              [AS_HELP_STRING([--enable-match],
+                    [Install Match and other enabled programs.])],
+             [AS_IF([test "x$enable_match" != xno],
+                     [enable_match=yes; ayes=true])],
+              [enable_match=notset])
 AC_ARG_ENABLE([mkcatalog],
               [AS_HELP_STRING([--enable-mkcatalog],
                     [Install MakeCatalog and other enabled programs.])],
@@ -628,6 +634,7 @@ AS_IF([test $ayes = true ],
        AS_IF([test $enable_cosmiccal = notset], [enable_cosmiccal=no])
        AS_IF([test $enable_crop = notset], [enable_crop=no])
        AS_IF([test $enable_fits = notset], [enable_fits=no])
+       AS_IF([test $enable_match = notset], [enable_match=no])
        AS_IF([test $enable_mkcatalog = notset], [enable_mkcatalog=no])
        AS_IF([test $enable_mknoise = notset], [enable_mknoise=no])
        AS_IF([test $enable_mkprof = notset], [enable_mkprof=no])
@@ -646,6 +653,7 @@ AS_IF([test $ayes = true ],
        AS_IF([test $enable_cosmiccal = notset], [enable_cosmiccal=yes])
        AS_IF([test $enable_crop = notset], [enable_crop=yes])
        AS_IF([test $enable_fits = notset], [enable_fits=yes])
+       AS_IF([test $enable_match = notset], [enable_match=yes])
        AS_IF([test $enable_mkcatalog = notset], [enable_mkcatalog=yes])
        AS_IF([test $enable_mknoise = notset], [enable_mknoise=yes])
        AS_IF([test $enable_mkprof = notset], [enable_mkprof=yes])
@@ -677,6 +685,7 @@ AM_CONDITIONAL([COND_CONVOLVE], [test $enable_convolve = 
yes])
 AM_CONDITIONAL([COND_COSMICCAL], [test $enable_cosmiccal = yes])
 AM_CONDITIONAL([COND_CROP], [test $enable_crop = yes])
 AM_CONDITIONAL([COND_FITS], [test $enable_fits = yes])
+AM_CONDITIONAL([COND_MATCH], [test $enable_match = yes])
 AM_CONDITIONAL([COND_MKCATALOG], [test $enable_mkcatalog = yes])
 AM_CONDITIONAL([COND_MKNOISE], [test $enable_mknoise = yes])
 AM_CONDITIONAL([COND_MKPROF], [test $enable_mkprof = yes])
@@ -701,6 +710,7 @@ AC_CONFIG_FILES([Makefile
                  bin/fits/Makefile
                  bin/warp/Makefile
                  bin/table/Makefile
+                 bin/match/Makefile
                  bin/mkprof/Makefile
                  bin/mknoise/Makefile
                  bin/convertt/Makefile
diff --git a/doc/announce-acknowledge.txt b/doc/announce-acknowledge.txt
index 67c393c..7b4c947 100644
--- a/doc/announce-acknowledge.txt
+++ b/doc/announce-acknowledge.txt
@@ -1,5 +1,4 @@
-This file is meant to keep the names of the people who's help must be
-acknowledged in the next release.
+People who's help must be acknowledged in the next release.
 
 Leindert Boogaard
 Nicolas Bouché
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index ca50ce3..278ed42 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -423,6 +423,7 @@ Data analysis
 * Statistics::                  Calculate dataset statistics.
 * NoiseChisel::                 Detect objects in an image.
 * MakeCatalog::                 Catalog from input and labeled images.
+* Match::                       Match two datasets.
 
 Statistics
 
@@ -464,6 +465,10 @@ Invoking MakeCatalog
 * Upper-limit magnitude settings::  Necessary to define upper-limit magnitudes.
 * MakeCatalog output columns::  How to specify the columns in the output.
 
+Match
+
+* Invoking astmatch::           Inputs, outputs and options of Match
+
 Modeling and fitting
 
 * MakeProfiles::                Making mock galaxies and stars.
@@ -479,7 +484,7 @@ MakeProfiles
 
 Modeling basics
 
-* Defining an ellipse::         Defining an ellipse in 2D.
+* Defining an ellipse::         An ellipse on a pixelated grid.
 * PSF::                         Radial profiles for the PSF.
 * Stars::                       Making mock star profiles.
 * Galaxies::                    Radial profiles for galaxies.
@@ -12289,6 +12294,7 @@ input dataset (with @ref{MakeCatalog}).
 * Statistics::                  Calculate dataset statistics.
 * NoiseChisel::                 Detect objects in an image.
 * MakeCatalog::                 Catalog from input and labeled images.
+* Match::                       Match two datasets.
 @end menu
 
 @node Statistics, NoiseChisel, Data analysis, Data analysis
@@ -14311,7 +14317,7 @@ multiextension FITS images}.
 
 
 
address@hidden MakeCatalog,  , NoiseChisel, Data analysis
address@hidden MakeCatalog, Match, NoiseChisel, Data analysis
 @section MakeCatalog
 
 At the lowest level, a dataset (for example an image) is just a collection
@@ -15576,8 +15582,152 @@ with the first FITS axis in degrees.
 
 
 
address@hidden Match,  , MakeCatalog, Data analysis
address@hidden Match
 
+Data can come come from different telescopes, filters, software and even
+different configurations for a single software. As a result, one of the
+primary things to do after generating catalogs from each of these sources
+(for example with @ref{MakeCatalog}), is to find which sources in one
+catalog correspond to which in the other(s). In other words, to `match' the
+two catalogs with each other.
 
+Gnuastro's Match program is in charge of such operations. The nearest
+objects in the two catalogs, within the given aperture, will be found and
+given as output. The aperture can be a circle or an ellipse with any
+orientation.
+
address@hidden
+* Invoking astmatch::           Inputs, outputs and options of Match
address@hidden menu
+
address@hidden Invoking astmatch,  , Match, Match
address@hidden Invoking Match
+
+When given two catalogs, Match find which rows correspond to each other
+given an input aperture. The executable name is @file{astmatch} with the
+following general template
+
address@hidden
+$ astmatch [OPTION ...] input-1.fits input-2.fits
address@hidden example
+
address@hidden
+One line examples:
+
address@hidden
+## Match the two catalogs with a circular aperture of width 2.
+## (Units same as given positional columns)
+$ astmatch --aperture=2 input1.fits input2.fits
+
+## Match the two catalogs within an elliptical aperture of 1 and 2
+## arcseconds along RA and Dec respectively.
+$ astmatch --aperture=1/3600,2/3600 in1.fits in2.fits
+
+## Match the RA and DEC columns of the two inputs within an
+## aperture of 0.5 arcseconds.
+$ astmatch --aperture0.5/3600 in1.fits in2.fits             \
+           --ccol1=RA --ccol1=DEC --ccol2=RA --ccol2=DEC
address@hidden example
+
+Two inputs are necessary for Match to produce output(s). By default, the
+output will be the input tables that are re-arranged to match each other
+(both tables will have the same number of rows).  If the
address@hidden option is called, the output will be a single table
+(contents of the log file, see below). Match follows the same basic
+behavior of all Gnuastro programs as fully described in @ref{Common program
+behavior}. If the first input is a FITS file, the common @option{--hdu}
+option (see @ref{Input output options}) should be used to identify the
+extension. For the second input, the extension can be specified with
address@hidden
+
+When the @option{--log} option is called (see @ref{Operating mode
+options}), Match will also create a file named @file{astmatch.log} in the
+directory it is run in. This log table will have three columns. The first
+and second column shows the rows/record number (counting from 1) of the
+first and second input catalogs respectively. The third column is the
+distance between the two matched positions. The units of this distance are
+the same as the given coordinates. When @option{--logasoutput} is called,
+no log file (with a fixed name) will be created. In this case, the output
+file (possibly given by the @option{--output} option) will have the
+contents of this log file.
+
+If no output file name is given with the @option{--output} option, then
+automatic output @ref{Automatic output} will be used to determine the
+output names. Depending on @option{--tableformat} (see @ref{Input output
+options}), the output will then be a FITS table or a plain text table.
+
+When the output name is a FITS file, and @option{--logasoutput} is not
+called, the re-arranged inputs will be two extensions of the output FITS
+file. If the output name is a text file, then two files will be created
+with a @file{_matched_1.txt} and @file{_matched_2.txt} suffix.
+
address@hidden @option
address@hidden -H STR
address@hidden --hdu2=STR
+The extension/HDU of the second input if it is a FITS file. When it isn't a
+FITS file, this option's value is ignored. For the first input, the common
+option @option{--hdu} must be used.
+
address@hidden -l
address@hidden --logasoutput
+The output file will have the contents of the log file: indexs in the two
+catalogs that match with each other along with their distance. See
+description above. When this option is called, a log file called
address@hidden will not be created. With this option, the default
+output behavior (two tables containing the re-arranged inputs) will be
+
address@hidden --ccol1=INT/STR
+The coordinate column of the first input. This option must be called
+multiple times to specify all the coordinate columns of the first
+input.
+
+For example, to match the RA and Dec columns of the first catalog with the
+same named columns of the second, you need to call this option (along with
address@hidden) two times:
+
address@hidden
+$ astmatch --ccol1=RA --ccol1=DEC --ccol2=RA --ccol2=DEC   \
+           input-1.fits input-2.fits
address@hidden example
+
address@hidden --ccol2=INT/STR
+The coordinate column of the second input. This option must be called
+multiple times to specify all the coordinate columns of the second
+input. See the example in @option{--ccol1} for more.
+
address@hidden -a FLT[,FLT[,FLT]]
address@hidden --aperture=FLT[,FLT[,FLT]]
+Parameters of the aperture for matching. The values given to this option
+can be fractions, for example @option{1/3600} can be used to ask for one
+arcsecond. The interpretation of the values depends on how many are given:
+
address@hidden @asis
address@hidden 1 number
+For example @option{--aperture=2}. The aperture will be a circle of the
+given radius. The value will be in the same units as the columns in
address@hidden and @option{--ccol2}).
+
address@hidden 2 numbers
+For example @option{--aperture=3,4e-10}. The aperture will be an ellipse
+(if the two numbers are different) with the respective value along each
+dimension. The numbers are in units of the first and second axis. In the
+example above, the semi-axis value along the first axis will be 3 (in units
+of the first coordinate) and along the second axis will be
address@hidden (in units of the second coordinate). Such values
+can happen if you are comparing catalogs of a spectra for example. If more
+than one object exists in the aperture, the nearest will be found along the
+major axis as described in @ref{Defining an ellipse}.
+
address@hidden 3 numbers
+For example @option{--aperture=2,0.6,30}. The aperture will be an ellipse
+(if the second value is not 1). The first number is the semi-major axis,
+the second is the axis ratio and the third is the position angle (in
+degrees). If multiple matches are found within the ellipse, the distance
+(to find the nearest) is calculated along the major axis in the elliptical
+space, see @ref{Defining an ellipse}.
address@hidden table
address@hidden table
 
 
 
@@ -15688,7 +15838,7 @@ given. You can skip this subsection if you are already 
sufficiently
 familiar with these concepts.
 
 @menu
-* Defining an ellipse::         Defining an ellipse in 2D.
+* Defining an ellipse::         An ellipse on a pixelated grid.
 * PSF::                         Radial profiles for the PSF.
 * Stars::                       Making mock star profiles.
 * Galaxies::                    Radial profiles for galaxies.
@@ -15696,6 +15846,8 @@ familiar with these concepts.
 * Oversampling::                Oversampling the model.
 @end menu
 
+
+
 @node Defining an ellipse, PSF, Modeling basics, Modeling basics
 @subsubsection Defining an ellipse
 
@@ -15703,8 +15855,8 @@ familiar with these concepts.
 @cindex Axis ratio
 @cindex Position angle
 The PSF, see @ref{PSF}, and galaxy radial profiles are generally defined on
-an ellipse so in this section first defining an ellipse on a pixelated 2D
-surface is discussed. Labeling the major axis of an ellipse @mymath{a}, and
+an ellipse. Therefore, in this section we'll start defining an ellipse on a
+pixelated 2D surface. Labeling the major axis of an ellipse @mymath{a}, and
 its minor axis with @mymath{b}, the @emph{axis ratio} is defined as:
 @mymath{q\equiv b/a}. The major axis of an ellipse can be aligned in any
 direction, therefore the angle of the major axis with respect to the
@@ -15716,7 +15868,7 @@ Our aim is to put a radial profile of any functional 
form @mymath{f(r)}
 over an ellipse. Hence we need to associate a radius/distance to every
 point in space. Let's define the radial distance @mymath{r_{el}} as the
 distance on the major axis to the center of an ellipse which is located at
address@hidden and @mymath{i_c} (in other words @mymath{r_{el}\equiv{a}}). We
address@hidden and @mymath{j_c} (in other words @mymath{r_{el}\equiv{a}}). We
 want to find @mymath{r_{el}} of a point located at @mymath{(i,j)} (in the
 image coordinate system) from the center of the ellipse with axis ratio
 @mymath{q} and position angle @mymath{\theta}. First the coordinate system
@@ -19859,15 +20011,15 @@ converting to a smaller type, it is up to you to make 
sure that the values
 fit into the output type.
 
 @deftypefun {gal_data_t *} gal_data_copy (gal_data_t @code{*in})
-Return a new dataset that is a copy of @code{in}, the main meta-data of the
-input is also copied into the output.
+Return a new dataset that is a copy of @code{in}, all of @code{in}'s
+meta-data will also copied into the output, except for @code{block}.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_data_copy_to_new_type (gal_data_t @code{*in}, 
uint8_t @code{newtype})
 Return a copy of the dataset @code{in}, converted to @code{newtype}, see
 @ref{Library data types} for Gnuastro library's type identifiers. The
-returned dataset will have all meta-data accept their type equal to the
-input's metadata.
+returned dataset will have all meta-data except their type and @code{block}
+equal to the input's metadata.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_data_copy_to_new_type_free (gal_data_t 
@code{*in}, uint8_t @code{newtype})
@@ -22696,6 +22848,15 @@ declared in @file{gnuastro/box.h}. All coordinates in 
this header are in
 the FITS format (first axis is the horizontal and the second axis is
 vertical).
 
address@hidden void gal_box_bound_ellipse_extent (double @code{a}, double 
@code{b}, double @code{theta_deg}, double @code{*extent})
+Return the maximum extent along each dimension of the given ellipse from
+the center of the ellipse. Therefore this is half the extent of the box in
+each dimension. @code{a} is the ellipse major axis, @code{b} is the minor
+axis, @code{theta_deg} is the position angle in degrees. The extent in each
+dimension is in floating point format and stored in @code{extent} which
+must already be allocated before this function.
address@hidden deftypefun
+
 @deftypefun void gal_box_bound_ellipse (double @code{a}, double @code{b}, 
double @code{theta_deg}, long @code{*width})
 Any ellipse can be enclosed into a rectangular box. The purpose of this
 function is to give the height and width of that box assuming the center of
@@ -25664,6 +25825,10 @@ image. The central regions of radial profiles are made 
with a configurable
 2D Monte Carlo integration. It can also build the profiles on an
 over-sampled image.
 
address@hidden Match
+(@file{astmatch}, see @ref{Match}) Given two input catalogs, find the rows
+that match with each other within a given aperture (may be an ellipse).
+
 @item NoiseChisel
 (@file{astnoisechisel}, see @ref{NoiseChisel}) Detect and segment signal in
 noise. It uses a technique to detect very faint and diffuse, irregularly
diff --git a/doc/release-checklist.txt b/doc/release-checklist.txt
index 3d78176..8411dfa 100644
--- a/doc/release-checklist.txt
+++ b/doc/release-checklist.txt
@@ -21,23 +21,33 @@ all the commits needed for this release have been completed.
  - Check if README includes all the recent updates and important features.
 
 
- - Check if THANKS is up to date and includes recently updated
-   people/institutions.
-
-
- - Make sure THANKS corresponds to the acknowledgements in the book.
+ - Check if THANKS and the book's Acknowledgments section have everyone in
+   `doc/announce-acknowledge.txt' in them.
 
 
  - [STABLE] Correct the links in the webpage (`doc/gnuastro-top.html' and
    `doc/gnuastro.fr.html').
 
 
- - Commit all these changes and tag the commit:
+ - Run the following commands to keep the list of people who contributed
+   code and those that must be acknowledged for the announcement (`P.P' is
+   the previous version). Finally, if in a stable release, clean
+   `doc/announce-acknowledge.txt' for the next release. We will use
+   `~/people.txt' in the announcement.
+
+     $ git shortlog gnuastro_vP.P...HEAD --numbered --summary > ~/people.txt
+     $ cat doc/announce-acknowledge.txt >> ~/people.txt
+
+     [STABLE]:
+     $ echo "People who's help must be acknowledged in the next release." \
+            > doc/announce-acknowledge.txt
+
+
+ - Commit all these changes:
 
      $ git add -u
      $ git commit
 
-
  - Make sure all the bootstrapping tools are up to date (keep the
    versions in text file to use in the release), then bootstrap and
    rebuild everything:
@@ -100,7 +110,7 @@ all the commits needed for this release have been completed.
        $ cd doc
        $ ./forwebpage /path/to/local/copy/of/webpage
 
-   If any of the files have a `?' infront of them, run these two commands
+   If any of the files have a `?' in front of them, run these two commands
    in the webpage directory:
 
        $ cvs add filename1 filename2 filename3
@@ -131,13 +141,13 @@ all the commits needed for this release have been 
completed.
               --archive-suffix=tar.lz > announcement.txt
 
 
- - After finishing the announcement (adding an intro and NEWS file), run a
-   spell-check on it.
+ - Based on previous announcements, add an intro, the NEWS file and the
+   contents of `~/people.txt' to the announcement.
+
 
+ - Run a spell-check on the announcement and remove `~/people.txt'.
 
- - IMPORTANT: In the announcements, double check the changes in the list of
-   authors and the list in THANKS and make sure everyone's contribution is
-   correctly included.
+     $ rm ~/people.txt
 
 
  - Announce the release on address@hidden', address@hidden'
diff --git a/lib/Makefile.am b/lib/Makefile.am
index c549862..94319df 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -51,10 +51,10 @@ libgnuastro_la_LIBADD = 
$(top_builddir)/bootstrapped/lib/libgnu.la
 
 
 # Specify the library .c files
-libgnuastro_la_SOURCES = arithmetic.c arithmetic-binary.c               \
-  arithmetic-onlyint.c binary.c blank.c box.c checkset.c convolve.c     \
-  cosmology.c data.c fits.c git.c interpolate.c list.c options.c        \
-  permutation.c polygon.c qsort.c dimension.c statistics.c table.c      \
+libgnuastro_la_SOURCES = arithmetic.c arithmetic-binary.c                \
+  arithmetic-onlyint.c binary.c blank.c box.c checkset.c convolve.c      \
+  cosmology.c data.c fits.c git.c interpolate.c list.c match.c options.c \
+  permutation.c polygon.c qsort.c dimension.c statistics.c table.c       \
   tableintern.c threads.c tile.c timing.c txt.c type.c wcs.c
 
 
@@ -69,7 +69,7 @@ pkginclude_HEADERS = gnuastro/config.h 
$(headersdir)/arithmetic.h         \
   $(headersdir)/binary.h $(headersdir)/blank.h $(headersdir)/box.h        \
   $(headersdir)/convolve.h $(headersdir)/cosmology.h $(headersdir)/data.h \
   $(headersdir)/dimension.h $(headersdir)/fits.h $(headersdir)/git.h      \
-  $(headersdir)/interpolate.h $(headersdir)/list.h                        \
+  $(headersdir)/interpolate.h $(headersdir)/list.h $(headersdir)/match.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        \
@@ -154,8 +154,15 @@ gnuastro.pc: Makefile $(srcdir)/gnuastro.pc.in
 
 
 
-# Remove the two local directories we needed during installation
+# Remove the two local directories we needed during installation from the
+# library search paths of the installed `libgnuastro.la'.
+#
+# Note that this hook is executed after every installation command
+# (including `make uninstall'). So we should only do it if the library
+# actually exists.
 install-exec-hook:
-       $(SED) -e 's/ -L..\/lib//g' -e 's/ -L..\/..\/lib//g'       \
-              $(libdir)/libgnuastro.la > libgnuastro.la_tmp
-       mv libgnuastro.la_tmp $(libdir)/libgnuastro.la
+       if [ -f $(libdir)/libgnuastro.la ]; then                   \
+         $(SED) -e 's/ -L..\/lib//g' -e 's/ -L..\/..\/lib//g'     \
+                $(libdir)/libgnuastro.la > libgnuastro.la_tmp;    \
+         mv libgnuastro.la_tmp $(libdir)/libgnuastro.la;          \
+       fi
diff --git a/lib/box.c b/lib/box.c
index 2b3250b..58ef6b5 100644
--- a/lib/box.c
+++ b/lib/box.c
@@ -64,22 +64,36 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    and you will find the distance (about the center of the ellipse
    that encloses the whole ellipse. */
 void
-gal_box_bound_ellipse(double a, double b, double theta_deg, long *width)
+gal_box_bound_ellipse_extent(double a, double b, double theta_deg,
+                             double *extent)
 {
   double t_r=theta_deg*M_PI/180;
-  double max_x, max_y, ct=cos(t_r), st=sin(t_r);
+  double ct=cos(t_r), st=sin(t_r);
   double t_x=atan(b/a*tan(t_r)), t_y=atan(-1.0f*b/a/tan(t_r));
 
   /* Calculate the maxima along each direction. */
-  max_x = a*cos(t_x)*ct    + b*sin(t_x)*st;
-  max_y = -1*a*cos(t_y)*st + b*sin(t_y)*ct;
+  extent[0] = fabs( a*cos(t_x)*ct    + b*sin(t_x)*st );
+  extent[1] = fabs( -1*a*cos(t_y)*st + b*sin(t_y)*ct );
+}
+
+
+
+
+
+void
+gal_box_bound_ellipse(double a, double b, double theta_deg, long *width)
+{
+  double extent[2];
+
+  /* Find the extent of the ellipse. */
+  gal_box_bound_ellipse_extent(a, b, theta_deg, extent);
 
   /* max_x and max_y are calculated from the center of the ellipse. We
      want the final height and width of the box enclosing the
      ellipse. So we have to multiply them by two, then take one from
      them (for the center). */
-  width[0]=2*( (size_t)fabs(max_x)+1 ) + 1;
-  width[1]=2*( (size_t)fabs(max_y)+1 ) + 1;
+  width[0] = 2 * ( (size_t)extent[0] + 1 ) + 1;
+  width[1] = 2 * ( (size_t)extent[1] + 1 ) + 1;
 }
 
 
diff --git a/lib/checkset.c b/lib/checkset.c
index 6a9da0d..1d410b3 100644
--- a/lib/checkset.c
+++ b/lib/checkset.c
@@ -133,6 +133,28 @@ gal_checkset_allocate_copy_set(char *arg, char **copy, int 
*set)
 
 
 
+/* The dataset may be alone in a file (for example a table in a text file)
+   or it may an extension of a FITS file. In error messages in particular,
+   we need to differentiate between the two. This function will check the
+   filename and if it is FITS, it will return a string with the filename
+   and HDU in parenthesis. If it isn't a FITS file, it will only return the
+   filename. Note that the output needs to be freed, although when used in
+   an error message, you can leave it to the system to free the
+   space. There is no problem. */
+char *
+gal_checkset_dataset_name(char *filename, char *hdu)
+{
+  char *out;
+  if( gal_fits_name_is_fits(filename) )
+    asprintf(&out, "%s (hdu %s)", filename, hdu);
+  else
+    gal_checkset_allocate_copy(filename, &out);
+  return out;
+}
+
+
+
+
 
 
 
diff --git a/lib/data.c b/lib/data.c
index 17f09f4..702d2d4 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -694,9 +694,9 @@ data_copy_to_string(gal_data_t *from, gal_data_t *to)
   if(to->type!=GAL_TYPE_STRING)
     error(EXIT_FAILURE, 0, "%s: `to' must have a string type", __func__);
   if(from->block)
-    error(EXIT_FAILURE, 0, "%s: tile inputs not currently supported (`block' "
-          "element must be NULL). Please contact us at %s so we can implement "
-          "this feature", __func__, PACKAGE_BUGREPORT);
+    error(EXIT_FAILURE, 0, "%s: tile inputs not currently supported "
+          "(`block' element must be NULL). Please contact us at %s so we "
+          "can implement this feature", __func__, PACKAGE_BUGREPORT);
 
   /* Do the copying */
   switch(from->type)
@@ -918,9 +918,9 @@ gal_data_copy_to_new_type_free(gal_data_t *in, uint8_t 
newtype)
 
 
 /* Copy a given dataset (`in') into an already allocated dataset `out' (the
-   actual dataset and its `array' element). The meta-data of `in' will be
-   fully copied into `out' also. `out->size' will be used to find the
-   available space in the allocated space.
+   actual dataset and its `array' element). The meta-data of `in' (except
+   for `block') will be fully copied into `out' also. `out->size' will be
+   used to find the available space in the allocated space.
 
    When `in->size != out->size' this function will behave as follows:
 
diff --git a/lib/gnuastro-internal/checkset.h b/lib/gnuastro-internal/checkset.h
index b9e6775..51050bb 100644
--- a/lib/gnuastro-internal/checkset.h
+++ b/lib/gnuastro-internal/checkset.h
@@ -79,6 +79,9 @@ gal_checkset_allocate_copy(const char *arg, char **copy);
 void
 gal_checkset_allocate_copy_set(char *arg, char **copy, int *set);
 
+char *
+gal_checkset_dataset_name(char *filename, char *hdu);
+
 
 
 
@@ -97,7 +100,7 @@ gal_checkset_check_file(char *filename);
 int
 gal_checkset_check_file_return(char *filename);
 
-void
+void    /* keep==0 && dontdelete==0: file will be deleted if exists.*/
 gal_checkset_writable_remove(char *filename, int keep, int dontdelete);
 
 int
diff --git a/lib/gnuastro-internal/options.h b/lib/gnuastro-internal/options.h
index 9dddd10..a89962a 100644
--- a/lib/gnuastro-internal/options.h
+++ b/lib/gnuastro-internal/options.h
@@ -278,6 +278,10 @@ gal_options_parse_sizes_reverse(struct argp_option 
*option, char *arg,
                                 char *filename, size_t lineno, void *params);
 
 void *
+gal_options_parse_csv_float64(struct argp_option *option, char *arg,
+                              char *filename, size_t lineno, void *junk);
+
+void *
 gal_options_read_sigma_clip(struct argp_option *option, char *arg,
                             char *filename, size_t lineno, void *junk);
 
diff --git a/lib/gnuastro/box.h b/lib/gnuastro/box.h
index 31bffd4..78a806c 100644
--- a/lib/gnuastro/box.h
+++ b/lib/gnuastro/box.h
@@ -52,6 +52,10 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 */
 
 void
+gal_box_bound_ellipse_extent(double a, double b, double theta_deg,
+                             double *extent);
+
+void
 gal_box_bound_ellipse(double a, double b, double theta_deg, long *width);
 
 void
diff --git a/lib/gnuastro/fits.h b/lib/gnuastro/fits.h
index 95dcb86..86d7343 100644
--- a/lib/gnuastro/fits.h
+++ b/lib/gnuastro/fits.h
@@ -44,6 +44,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <wcslib/wcshdr.h>
 #include <wcslib/wcsfix.h>
 
+#include <gnuastro/list.h>
 #include <gnuastro/data.h>
 #include <gnuastro/table.h>
 
diff --git a/lib/gnuastro/box.h b/lib/gnuastro/match.h
similarity index 65%
copy from lib/gnuastro/box.h
copy to lib/gnuastro/match.h
index 31bffd4..b155a05 100644
--- a/lib/gnuastro/box.h
+++ b/lib/gnuastro/match.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-Box -- Define bounding and overlapping boxes.
+match -- Functions to match catalogs and WCS.
 This 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) 2017, 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,12 +20,12 @@ 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 __GAL_BOX_H__
-#define __GAL_BOX_H__
+#ifndef __GAL_MATCH_H__
+#define __GAL_MATCH_H__
 
 /* Include other headers if necessary here. Note that other header files
    must be included before the C++ preparations below */
-
+#include <gnuastro/data.h>
 
 
 /* C++ Preparations */
@@ -42,28 +42,25 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* Actual header contants (the above were for the Pre-processor). */
-__BEGIN_C_DECLS  /* From C++ preparations */
+
+
+gal_data_t *
+gal_match_coordinates(gal_data_t *coord1, gal_data_t *coord2,
+                      gal_data_t *aperture, int sorted_by_first,
+                      int inplace, size_t minmapsize);
+
+
+
+
 
 
 
-/*                        IMPORTANT NOTE:
-         All the axises are based on the FITS standard, NOT C.
-*/
 
-void
-gal_box_bound_ellipse(double a, double b, double theta_deg, long *width);
 
-void
-gal_box_border_from_center(double *center, size_t ndim, long *width,
-                           long *fpixel, long *lpixel);
 
-int
-gal_box_overlap(long *naxes, long *fpixel_i, long *lpixel_i,
-                long *fpixel_o, long *lpixel_o, size_t ndim);
 
 
 
 __END_C_DECLS    /* From C++ preparations */
 
-#endif           /* __GAL_BOX_H__ */
+#endif           /* __GAL_TABLE_H__ */
diff --git a/lib/match.c b/lib/match.c
new file mode 100644
index 0000000..250aef3
--- /dev/null
+++ b/lib/match.c
@@ -0,0 +1,669 @@
+/*********************************************************************
+match -- Functions to match catalogs and WCS.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <address@hidden>
+Contributing author(s):
+Copyright (C) 2017, 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
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+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/>.
+**********************************************************************/
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <error.h>
+#include <stdlib.h>
+
+#include <gsl/gsl_sort.h>
+
+#include <gnuastro/box.h>
+#include <gnuastro/list.h>
+#include <gnuastro/permutation.h>
+
+
+
+
+
+
+
+
+
+/**********************************************************************/
+/*****************   Coordinate match custom list   *******************/
+/**********************************************************************/
+struct match_coordinate_sfll
+{
+  float f;
+  size_t v;
+  struct match_coordinate_sfll *next;
+};
+
+
+
+
+
+static void
+match_coordinate_add_to_sfll(struct match_coordinate_sfll **list,
+                             size_t value, float fvalue)
+{
+  struct match_coordinate_sfll *newnode;
+
+  errno=0;
+  newnode=malloc(sizeof *newnode);
+  if(newnode==NULL)
+    error(EXIT_FAILURE, errno, "%s: new node couldn't be allocated",
+          __func__);
+
+  newnode->v=value;
+  newnode->f=fvalue;
+  newnode->next=*list;
+  *list=newnode;
+}
+
+
+
+
+
+static void
+match_coordinate_pop_from_sfll(struct match_coordinate_sfll **list,
+                               size_t *value, float *fvalue)
+{
+  struct match_coordinate_sfll *tmp;
+  tmp=*list;
+  *value=tmp->v;
+  *fvalue=tmp->f;
+  *list=tmp->next;
+  free(tmp);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/********************************************************************/
+/*************            Coordinate matching           *************/
+/*************      Sanity checks and preparations      *************/
+/********************************************************************/
+/* Since these checks are repetative, its easier to have a separate
+   function for both inputs. */
+static void
+match_coordinate_sanity_check_columns(gal_data_t *coord, char *info)
+{
+  gal_data_t *tmp;
+
+  for(tmp=coord; tmp!=NULL; tmp=tmp->next)
+    {
+      if(tmp->type!=GAL_TYPE_FLOAT64)
+        error(EXIT_FAILURE, 0, "%s: the input coordinates must have "
+              "`float64' type. At least one node of the %s list has type "
+              "of `%s'", __func__, info, gal_type_name(tmp->type, 1));
+
+      if(tmp->ndim!=1)
+        error(EXIT_FAILURE, 0, "%s: each input coordinate column must have "
+              "a single dimension (be a single column). Atleast one node of "
+              "the %s list has %zu dimensions", __func__, info, tmp->ndim);
+
+      if(tmp->size!=coord->size)
+        error(EXIT_FAILURE, 0, "%s: the nodes of each list of coordinates "
+              "must have the same number of elements. At least one node of "
+              "the %s list has %zu elements while the first has %zu "
+              "elements", __func__, info, tmp->size, coord->size);
+    }
+}
+
+
+
+
+
+/* To keep the main function clean, we'll do the sanity checks here. */
+static void
+match_coordinaes_sanity_check(gal_data_t *coord1, gal_data_t *coord2,
+                              gal_data_t *aperture)
+{
+  double *aper=aperture->array;
+  size_t ncoord1=gal_list_data_number(coord1);
+
+  /* Make sure both lists have the same number of datasets. NOTE: they
+     don't need to have the same number of elements. */
+  if( ncoord1!=gal_list_data_number(coord2) )
+    error(EXIT_FAILURE, 0, "%s: the two inputs have different numbers of "
+          "datasets (%zu and %zu respectively)", __func__, ncoord1,
+          gal_list_data_number(coord2));
+
+
+  /* This function currently only works for 2 dimensions. */
+  if(ncoord1!=2)
+    error(EXIT_FAILURE, 0, "%s: inputs correspond to %zu dimensions, this "
+          "function currently only works on 2 dimensional datasets",
+          __func__, ncoord1);
+
+  /* Check the column properties. */
+  match_coordinate_sanity_check_columns(coord1, "first");
+  match_coordinate_sanity_check_columns(coord2, "second");
+
+  /* Check the aperture values. */
+  if(aper[0]<=0)
+    error(EXIT_FAILURE, 0, "%s: the first value in the aperture (%g) is the "
+          "semi-major axis and thus not be zero or negative", __func__,
+          aper[0]);
+  if(aper[1]<=0 || aper[1]>1)
+    error(EXIT_FAILURE, 0, "%s: the second value in the aperture (%g) is "
+          "the axis ratio, so it must be larger than zero and less than 1",
+          __func__, aper[1]);
+}
+
+
+
+
+
+/* To keep things clean, the sorting of each input array will be done in
+   this function. */
+static size_t *
+match_coordinates_prepare_sort(gal_data_t *coords, size_t minmapsize)
+{
+  gal_data_t *tmp;
+  size_t *permutation=gal_data_malloc_array(GAL_TYPE_SIZE_T, coords->size,
+                                            __func__, "permutation");
+
+  /* Get the permutation necessary to sort all the columns (based on the
+     first column). */
+  gsl_sort_index(permutation, coords->array, 1, coords->size);
+
+  /* Sort all the coordinates. */
+  for(tmp=coords; tmp!=NULL; tmp=tmp->next)
+    gal_permutation_apply(tmp, permutation);
+
+  /* Clean up. */
+  return permutation;
+}
+
+
+
+
+
+/* Do the preparations for matching of coordinates. */
+static void
+match_coordinates_prepare(gal_data_t *coord1, gal_data_t *coord2,
+                          int sorted_by_first, int inplace,
+                          gal_data_t **A_out, gal_data_t **B_out,
+                          size_t **A_perm, size_t **B_perm,
+                          size_t minmapsize)
+{
+  gal_data_t *c, *tmp, *A=NULL, *B=NULL;
+
+  /* Sort the datasets if they aren't sorted. If the dataset is already
+     sorted, then `inplace' is irrelevant. */
+  if(!sorted_by_first)
+    {
+      /* Allocating a new list is only necessary when  */
+      if(!inplace)
+        {
+          /* Copy the first list. */
+          for(tmp=coord1; tmp!=NULL; tmp=tmp->next)
+            {
+              c=gal_data_copy(tmp);
+              c->next=NULL;
+              gal_list_data_add(&A, c);
+            }
+
+          /* Copy the second list. */
+          for(tmp=coord2; tmp!=NULL; tmp=tmp->next)
+            {
+              c=gal_data_copy(tmp);
+              c->next=NULL;
+              gal_list_data_add(&B, c);
+            }
+
+          /* Reverse both lists: the copying process reversed the order. */
+          gal_list_data_reverse(&A);
+          gal_list_data_reverse(&B);
+
+          /* Set the output pointers. */
+          *A_out=A;
+          *B_out=B;
+        }
+      else
+        {
+          *A_out=coord1;
+          *B_out=coord2;
+        }
+
+      /* Sort each dataset by the first coordinate. */
+      *A_perm = match_coordinates_prepare_sort(*A_out, minmapsize);
+      *B_perm = match_coordinates_prepare_sort(*B_out, minmapsize);
+    }
+  else
+    {
+      *A_out=coord1;
+      *B_out=coord2;
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/********************************************************************/
+/*************            Coordinate matching           *************/
+/********************************************************************/
+static double
+match_coordinates_elliptical_r(double d1, double d2, double *ellipse,
+                               double c, double s)
+{
+  double Xr = d1 * ( c       )     +   d2 * ( s );
+  double Yr = d1 * ( -1.0f*s )     +   d2 * ( c );
+  return sqrt( Xr*Xr + Yr*Yr/ellipse[1]/ellipse[1] );
+}
+
+
+
+
+
+
+/* Go through both catalogs and find which records/rows in the second
+   catalog (catalog b) are within the acceptable distance of each record in
+   the first (a). */
+static void
+match_coordinates_second_in_first(gal_data_t *A, gal_data_t *B,
+                                  gal_data_t *aperture,
+                                  struct match_coordinate_sfll **bina)
+{
+  /* To keep things easy to read, all variables related to catalog 1 start
+     with an `a' and things related to catalog 2 are marked with a `b'. The
+     redundant variables (those that equal a previous value) are only
+     defined to make it easy to read the code.*/
+  double dist[2];
+  size_t ar=A->size, br=B->size;
+  size_t ai, bi, blow, prevblow=0;
+  double r, c, s, *aper=aperture->array;
+  double *a[2]={A->array, A->next->array};
+  double *b[2]={B->array, B->next->array};
+  int iscircle=((double *)(aperture->array))[1]==1 ? 1 : 0;
+
+  /* Preparations for the shape of the aperture. */
+  if(iscircle)
+    dist[0]=dist[1]=aper[0];
+  else
+    {
+      /* Using the box that encloses the aperture, calculate the distance
+         along each axis. */
+      gal_box_bound_ellipse_extent(aper[0], aper[0]*aper[1], aper[2], dist);
+
+      /* Calculate the sin and cos of the given ellipse if necessary for
+         ease of processing later. */
+      c = cos( aper[3] * M_PI/180.0 );
+      s = sin( aper[3] * M_PI/180.0 );
+    }
+
+
+  /* For each row/record of catalog `a', make a list of the nearest records
+     in catalog b within the maximum distance. Note that both catalogs are
+     sorted by their first axis coordinate.*/
+  for(ai=0;ai<ar;++ai)
+    {
+      /* Initialize `bina'. */
+      bina[ai]=NULL;
+
+      /* Find the first (lowest first axis value) row/record in catalog `b'
+        that is within the search radius for this record of catalog
+        `a'. `blow' is the index of the first element to start searching
+        in the catalog `b' for a match to `a[][ai]' (the record in catalog
+        a that is currently being searched). `blow' is only based on the
+        first coordinate, not the second.
+
+         Both catalogs are sorted by their first coordinate, so the `blow'
+        to search for the next record in catalog `a' will be larger or
+        equal to that of the previous catalog `a' record. To account for
+        possibly large distances between the records, we do a search here
+        to change `blow' if necessary before doing further searching.*/
+      for( blow=prevblow; blow<ar && b[0][blow] < a[0][ai]-dist[0]; ++blow)
+       {/* This can be blank, the `for' does all we need :-). */}
+
+
+      /* `blow' is now found for this `ai' and will be used unchanged to
+        the end of the loop. So keep its value to help the search for the
+        next entry in catalog `a'. */
+      prevblow=blow;
+
+
+      /* Go through catalog `b' (starting at `blow') with a first axis
+        value smaller than the maximum acceptable range for `si'. */
+      for( bi=blow; bi<br && b[0][bi] <= a[0][ai] + dist[0]; ++bi )
+       {
+         /* Only consider records with a second axis value in the
+            correct range, note that unlike the first axis, the
+            second axis is no longer sorted. so we have to do both
+            lower and higher limit checks for each item.
+
+            Positions can have an accuracy to a much higher order of
+            magnitude than the search radius. Therefore, it is
+            meaning-less to sort the second axis (after having sorted
+            the first). In other words, very rarely can two first
+            axis coordinates have EXACTLY the same floating point
+            value as each other to easily define an independent
+            sorting in the second axis. */
+         if( b[1][bi] >= a[1][ai]-dist[1] && b[1][bi] <= a[1][ai]+dist[1] )
+           {
+             /* Now, `bi' is within the rectangular range of `ai'. But
+                this is not enough to consider the two objects matched for
+                the following reasons:
+
+                1) Until now we have avoided calculations other than
+                   larger or smaller on double precision floating point
+                   variables for efficiency. So the `bi' is within a
+                   square of side `dist[0]*dist[1]' around `ai' (not
+                   within a fixed radius).
+
+                2) Other objects in the `b' catalog may be closer to `ai'
+                   than this `bi'.
+
+                3) The closest `bi' to `ai' might be closer to another
+                   catalog `a' record.
+
+                To address these problems, we will use a linked list to
+                keep the indexes of the `b's near `ai', along with their
+                distance. We only add the `bi's to this list that are
+                within the acceptable distance.
+
+                 Since we are dealing with much fewer objects at this
+                stage, it is justified to do complex mathematical
+                operations like square root and multiplication. This fixes
+                the first problem.
+
+                The next two problems will be solved with the list after
+                parsing of the whole catalog is complete.*/
+              r = ( iscircle
+                    ? sqrt( (b[0][bi]-a[0][ai])*(b[0][bi]-a[0][ai])
+                            + (b[1][bi]-a[1][ai])*(b[1][bi]-a[1][ai]) )
+                    : match_coordinates_elliptical_r(b[0][bi]-a[0][ai],
+                                                     b[1][bi]-a[1][ai],
+                                                     aper, c, s));
+             if(r<aper[0])
+               match_coordinate_add_to_sfll(&bina[ai], bi, r);
+           }
+       }
+
+
+      /* If there was no objects within the acceptable distance, then the
+        linked list pointer will be NULL, so go on to the next `ai'. */
+      if(bina[ai]==NULL)
+       continue;
+
+      /* For checking the status of affairs uncomment this block
+      {
+       struct match_coordinate_sfll *tmp;
+       printf("\n\nai: %lu:\n", ai);
+       printf("ax: %f (%f -- %f)\n", a[0][ai], a[0][ai]-dist[0],
+               a[0][ai]+dist[0]);
+       printf("ay: %f (%f -- %f)\n", a[1][ai], a[1][ai]-dist[1],
+               a[1][ai]+dist[1]);
+       for(tmp=bina[ai];tmp!=NULL;tmp=tmp->next)
+         printf("%lu: %f\n", tmp->v, tmp->f);
+      }
+      */
+    }
+}
+
+
+
+
+
+/* In `match_coordinates_second_in_first', we made an array of lists, here
+   we want to reverse that list to fix the second two issues that were
+   discussed there. */
+void
+match_coordinates_rearrange(gal_data_t *A, gal_data_t *B,
+                            struct match_coordinate_sfll **bina)
+{
+  size_t bi;
+  float *fp, *fpf, r, *ainb;
+  size_t ai, ar=A->size, br=B->size;
+
+  /* Allocate the space for `ainb' and initialize it to NaN (since zero is
+     meaningful) in this context (both for indexs and also for
+     floats). This is a two column array that will keep the distance and
+     index of the closest element in catalog `a' for each element in
+     catalog b. */
+  errno=0; ainb=calloc(2*br, sizeof *ainb);
+  if(ainb==NULL)
+    error(EXIT_FAILURE, errno, "%s: %zu bytes for `ainb'", __func__,
+          br*sizeof *ainb);
+  fpf=(fp=ainb)+2*br; do *fp++=NAN; while(fp<fpf);
+
+  /* Go over each object in catalog `a' and re-distribute the near objects,
+     to find which ones in catalog `a' are within the search radius of
+     catalog b in a sorted manner. Note that we only need the `ai' with the
+     minimum distance to `bi', the rest are junk.*/
+  for( ai=0; ai<ar; ++ai )
+    while( bina[ai] )  /* As long as its not NULL.            */
+      {
+       /* Pop out a `bi' and its distance to this `ai' from `bina'. */
+       match_coordinate_pop_from_sfll(&bina[ai], &bi, &r);
+
+       /* If nothing has been put here (the isnan condition below is
+          true), or something exists (the isnan is false, and so it
+          will check the second OR test) with a distance that is
+          larger than this distance then just put this value in. */
+       if( isnan(ainb[bi*2]) || r<ainb[bi*2+1] )
+         {
+           ainb[bi*2  ] = ai;
+           ainb[bi*2+1] = r;
+         }
+      }
+
+  /* For checking the status of affairs uncomment this block
+  {
+    printf("\n\nFilled ainb:\n");
+    for(bi=0;bi<br;++bi)
+      if( !isnan(ainb[bi*2]) )
+       printf("bi: %lu: %.0f, %f\n", bi, ainb[bi*2], ainb[bi*2+1]);
+  }
+  */
+
+  /* Re-fill the bina array, but this time only with the `bi' that is
+     closest to it. Note that bina was fully set to NULL after popping all
+     the elements in the loop above.*/
+  for( bi=0; bi<br; ++bi )
+    if( !isnan(ainb[bi*2]) )
+      {
+       /* Just to keep the same terminology as before and easier
+          reading.*/
+       r=ainb[bi*2+1];
+       ai=(size_t)(ainb[bi*2]);
+
+       /* Check if this is the first time we are associating a `bi' to
+          this `ai'. If so, then just allocate a single element
+          list. Otherwise, see if the distance is closer or not. If so,
+          replace the values in the single node. */
+       if( bina[ai] )
+         {
+           /* If the distance of this record is smaller than the existing
+              entry, then replace the values. */
+           if( r < bina[ai]->f )
+             {
+               bina[ai]->f=r;
+               bina[ai]->v=bi;
+             }
+         }
+       else
+          match_coordinate_add_to_sfll(&bina[ai], bi, r);
+      }
+
+  /* For checking the status of affairs uncomment this block
+  {
+    size_t bi, counter=0;
+    double *a[2]={A->array, A->next->array};
+    double *b[2]={B->array, B->next->array};
+    printf("Rearranged bina:\n");
+    for(ai=0;ai<ar;++ai)
+      if(bina[ai])
+        {
+          ++counter;
+          bi=bina[ai]->v;
+          printf("A_%lu (%.8f, %.8f) <--> B_%lu (%.8f, %.8f):\n\t%f\n",
+                 ai, a[0][ai], a[1][ai], bi, b[0][bi], b[1][bi],
+                 bina[ai]->f);
+        }
+    printf("\n-----------\nMatched: %zu\n", counter);
+  }
+  exit(0);
+  */
+
+  /* Clean up */
+  free(ainb);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/********************************************************************/
+/*************            Coordinate matching           *************/
+/********************************************************************/
+/* Match two positions: the inputs (`coord1' and `coord2') should be lists
+   of coordinates. To speed up the search, this function needs the input
+   arrays to be sorted by their first column. If it is already sorted, you
+   can set `sorted_by_first' to non-zero. When sorting is necessary and
+   `inplace' is non-zero, the actual inputs will be sorted. Otherwise, an
+   internal copy of the inputs will be made which will be used (sorted) and
+   later freed. Therefore when `inplace==0', the input's won't be
+   changed.
+
+   The output is a list of `gal_data_t' with the following columns:
+
+       Node 1: First catalog index (counting from zero).
+       Node 2: Second catalog index (counting from zero).
+       Node 3: Distance between the match.                    */
+gal_data_t *
+gal_match_coordinates(gal_data_t *coord1, gal_data_t *coord2,
+                      gal_data_t *aperture, int sorted_by_first,
+                      int inplace, size_t minmapsize)
+{
+  float r;
+  double *rmatch;
+  size_t *aind, *bind;
+  gal_data_t *A, *B, *out;
+  struct match_coordinate_sfll **bina;
+  size_t ai, bi, counter=0, *A_perm=NULL, *B_perm=NULL;
+
+  /* Do a small sanity check and make the preparations. After this point,
+     we'll call the two arrays `a' and `b'.*/
+  match_coordinaes_sanity_check(coord1, coord2, aperture);
+  match_coordinates_prepare(coord1, coord2, sorted_by_first, inplace,
+                            &A, &B, &A_perm, &B_perm, minmapsize);
+
+  /* Allocate the `bina' array (an array of lists). Let's call the first
+     catalog `a' and the second `b'. This array has `a->size' elements
+     (pointers) and for each, it keeps a list of `b' elements that are
+     nearest to it. */
+  errno=0;
+  bina=calloc(A->size, sizeof *bina);
+  if(bina==NULL)
+    error(EXIT_FAILURE, errno, "%s: %zu bytes for `bina'", __func__,
+          A->size*sizeof *bina);
+
+  /* All records in `b' that match each `a' (possibly duplicate). */
+  match_coordinates_second_in_first(A, B, aperture, bina);
+
+  /* Two re-arrangings will fix the issue. */
+  match_coordinates_rearrange(A, B, bina);
+
+  /* Find how many matches there were in total */
+  for(ai=0;ai<A->size;++ai) if(bina[ai]) ++counter;
+
+  /* Allocate the output list. */
+  out=gal_data_alloc(NULL, GAL_TYPE_SIZE_T, 1, &counter, NULL, 0,
+                     minmapsize, "CAT1_ROW", "counter",
+                     "Row index in first catalog (counting from 0).");
+  out->next=gal_data_alloc(NULL, GAL_TYPE_SIZE_T, 1, &counter, NULL, 0,
+                           minmapsize, "CAT2_ROW", "counter",
+                           "Row index in second catalog (counting "
+                           "from 0).");
+  out->next->next=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &counter, NULL,
+                                 0, minmapsize, "MATCH_DIST", NULL,
+                                 "Distance between the match.");
+
+  /* Fill in the output arrays. */
+  counter=0;
+  aind=out->array;
+  bind=out->next->array;
+  rmatch=out->next->next->array;
+  for(ai=0;ai<A->size;++ai)
+    if(bina[ai])
+      {
+        /* Note that the permutation keeps the original indexs. */
+        match_coordinate_pop_from_sfll(&bina[ai], &bi, &r);
+        aind[counter]=A_perm[ai];
+        bind[counter]=B_perm[bi];
+        rmatch[counter++]=r;
+      }
+
+  /* Clean up and return. */
+  free(bina);
+  free(A_perm);
+  free(B_perm);
+  if(A!=coord1)
+    {
+      gal_list_data_free(A);
+      gal_list_data_free(B);
+    }
+  return out;
+}
diff --git a/lib/options.c b/lib/options.c
index 1bcc386..2f5d893 100644
--- a/lib/options.c
+++ b/lib/options.c
@@ -764,6 +764,65 @@ gal_options_parse_sizes_reverse(struct argp_option 
*option, char *arg,
 
 
 
+/* Parse options with values of a list of numbers. */
+void *
+gal_options_parse_csv_float64(struct argp_option *option, char *arg,
+                              char *filename, size_t lineno, void *junk)
+{
+  size_t i, nc;
+  double *darray;
+  gal_data_t *values;
+  char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
+
+  /* We want to print the stored values. */
+  if(lineno==-1)
+    {
+      /* Set the pointer to the values dataset. */
+      values = *(gal_data_t **)(option->value);
+      darray=values->array;
+
+      /* Write each string into the output string */
+      nc=0;
+      for(i=0;i<values->size;++i)
+        {
+          if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
+            error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
+                  "can address the problem. The number of necessary "
+                  "characters in the statically allocated string has become "
+                  "too close to %d", __func__, PACKAGE_BUGREPORT,
+                  GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
+          nc += sprintf(sstr+nc, "%g,", darray[i]);
+        }
+      sstr[nc-1]='\0';
+
+      /* Copy the string into a dynamically allocated space, because it
+         will be freed later.*/
+      gal_checkset_allocate_copy(sstr, &str);
+      return str;
+    }
+
+  /* We want to read the user's string. */
+  else
+    {
+      /* If the option is already set, just return. */
+      if(option->set) return NULL;
+
+      /* Read the values. */
+      values=gal_options_parse_list_of_numbers(arg, filename, lineno);
+
+      /* Put the values into the option. */
+      *(gal_data_t **)(option->value) = values;
+
+      /* The return value is only for printing mode, so we can return
+         NULL after reading is complete. */
+      return NULL;
+    }
+}
+
+
+
+
+
 /* Two numbers must be provided as an argument. This function will read
    them as the sigma-clipping multiple and parameter and store the two in a
    2-element array. `option->value' must point to an already allocated
diff --git a/lib/permutation.c b/lib/permutation.c
index d5f86ef..1401928 100644
--- a/lib/permutation.c
+++ b/lib/permutation.c
@@ -136,6 +136,7 @@ gal_permutation_apply(gal_data_t *input, size_t 
*permutation)
 
 
 
+
 /* Apply the inverse of given permutation on the input dataset, see
    `gal_permutation_apply_inverse'. */
 void
diff --git a/lib/table.c b/lib/table.c
index 332d6d5..71112e5 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -223,144 +223,153 @@ make_list_of_indexs(gal_list_str_t *cols, gal_data_t 
*allcols,
   gal_list_sizet_t *indexll=NULL;
   char *str, *strcheck, *tailptr, *errorstring;
 
-  for(tmp=cols; tmp!=NULL; tmp=tmp->next)
-    {
-      /* Counter for number of columns matched, and length of name. */
-      nummatch=0;
-      len=strlen(tmp->v);
-
-      /* REGULAR EXPRESSION: When the first and last characters are `/'. */
-      if( tmp->v[0]=='/' && tmp->v[len-1]=='/' )
-        {
-          /* Remove the slashes, note that we don't want to change `tmp->v'
-             (because it should be freed later). So first we set the last
-             character to `\0', then define a new string from the first
-             element. */
-          tmp->v[len-1]='\0';
-          str = tmp->v + 1;
-
-          /* Allocate the regex_t structure: */
-          errno=0;
-          regex=malloc(sizeof *regex);
-          if(regex==NULL)
-            error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for regex",
-                  __func__, sizeof *regex);
-
-          /* First we have to "compile" the string into the regular
-             expression, see the "POSIX Regular Expression Compilation"
-             section of the GNU C Library.
-
-             About the case of the string: the FITS standard says: "It is
-             _strongly recommended_ that every field of the table be
-             assigned a unique, case insensitive name with this keyword..."
-             So the column names can be case-sensitive.
-
-             Here, we don't care about the details of a match, the only
-             important thing is a match, so we are using the REG_NOSUB
-             flag.*/
-          regreturn=0;
-          regreturn=regcomp(regex, str, ( ignorecase
-                                          ? RE_SYNTAX_AWK | REG_ICASE
-                                          : RE_SYNTAX_AWK ) );
-          if(regreturn)
-            table_regexerrorexit(regreturn, regex, str);
-
-
-          /* With the regex structure "compile"d you can go through all the
-             column names. Just note that column names are not mandatory in
-             the FITS standard, so some (or all) columns might not have
-             names, if so `p->tname[i]' will be NULL. */
-          for(i=0;i<numcols;++i)
-            {
-              strcheck=table_set_strcheck(&allcols[i], searchin);
-              if(strcheck && regexec(regex, strcheck, 0, 0, 0)==0)
-                {
-                  ++nummatch;
-                  gal_list_sizet_add(&indexll, i);
-                }
-            }
-
-          /* Free the regex_t structure: */
-          regfree(regex);
-
-          /* Put the `/' back into the input string. This is done because
-             after this function, the calling program might want to inform
-             the user of their exact input string. */
-          tmp->v[len-1]='/';
-        }
-
-
-      /* Not regular expression. */
-      else
-        {
-          tlong=strtol(tmp->v, &tailptr, 0);
-
-          /* INTEGER: If the string is an integer, then tailptr should
-             point to the null character. If it points to anything else, it
-             shows that we are not dealing with an integer (usable as a
-             column number). So floating point values are also not
-             acceptable. Since it is possible for the users to give zero
-             for the column number, we need to read the string as a number
-             first, then check it here. */
-          if(*tailptr=='\0')
-            {
-              /* Make sure the number is larger than zero! */
-              if(tlong<=0)
-                error(EXIT_FAILURE, 0, "%s: column numbers must be positive "
-                      "(not zero or negative). You have asked for column "
-                      "number %ld", __func__, tlong);
-
-              /* Check if the given value is not larger than the number of
-                 columns in the input catalog (note that the user is
-                 counting from 1, not 0!) */
-              if(tlong>numcols)
-                error(EXIT_FAILURE, 0, "%s: has %zu columns, but you "
-                      "have asked for column number %ld",
-                      gal_fits_name_save_as_string(filename, hdu),
-                      numcols, tlong);
-
-              /* Everything seems to be fine, put this column number in the
-                 output column numbers linked list. Note that internally,
-                 the column numbers start from 0, not 1.*/
-              gal_list_sizet_add(&indexll, tlong-1);
-              ++nummatch;
-            }
-
-
-
-          /* EXACT MATCH: */
-          else
-            {
-              /* Go through all the desired column information and add the
-                 column number when there is a match. */
-              for(i=0;i<numcols;++i)
-                {
-                  /* Check if this column actually has any
-                     information. Then do a case-sensitive or insensitive
-                     comparison of the strings. */
-                  strcheck=table_set_strcheck(&allcols[i], searchin);
-                  if(strcheck && ( ignorecase
-                                   ? !strcasecmp(tmp->v, strcheck)
-                                   : !strcmp(tmp->v, strcheck) ) )
-                    {
-                      ++nummatch;
-                      gal_list_sizet_add(&indexll, i);
-                    }
-                }
-            }
-        }
-
-
-      /* If there was no match, then report an error. This can only happen
-         for string matches, not column numbers, for numbers, the checks
-         are done (and program is aborted) before this step. */
-      if(nummatch==0)
-        {
-          asprintf(&errorstring, "`%s' didn't match any of the column %ss.",
-                   tmp->v, gal_tableintern_searchin_as_string(searchin));
-          gal_tableintern_error_col_selection(filename, hdu, errorstring);
-        }
-    }
+  /* Go over the given columns.  */
+  if(cols)
+    for(tmp=cols; tmp!=NULL; tmp=tmp->next)
+      {
+        /* Counter for number of columns matched, and length of name. */
+        nummatch=0;
+        len=strlen(tmp->v);
+
+        /* REGULAR EXPRESSION: the first and last characters are `/'. */
+        if( tmp->v[0]=='/' && tmp->v[len-1]=='/' )
+          {
+            /* Remove the slashes, note that we don't want to change
+               `tmp->v' (because it should be freed later). So first we set
+               the last character to `\0', then define a new string from
+               the first element. */
+            tmp->v[len-1]='\0';
+            str = tmp->v + 1;
+
+            /* Allocate the regex_t structure: */
+            errno=0;
+            regex=malloc(sizeof *regex);
+            if(regex==NULL)
+              error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for regex",
+                    __func__, sizeof *regex);
+
+            /* First we have to "compile" the string into the regular
+               expression, see the "POSIX Regular Expression Compilation"
+               section of the GNU C Library.
+
+               About the case of the string: the FITS standard says: "It is
+               _strongly recommended_ that every field of the table be
+               assigned a unique, case insensitive name with this
+               keyword..."  So the column names can be case-sensitive.
+
+               Here, we don't care about the details of a match, the only
+               important thing is a match, so we are using the REG_NOSUB
+               flag.*/
+            regreturn=0;
+            regreturn=regcomp(regex, str, ( ignorecase
+                                            ? RE_SYNTAX_AWK | REG_ICASE
+                                            : RE_SYNTAX_AWK ) );
+            if(regreturn)
+              table_regexerrorexit(regreturn, regex, str);
+
+
+            /* With the regex structure "compile"d you can go through all
+               the column names. Just note that column names are not
+               mandatory in the FITS standard, so some (or all) columns
+               might not have names, if so `p->tname[i]' will be NULL. */
+            for(i=0;i<numcols;++i)
+              {
+                strcheck=table_set_strcheck(&allcols[i], searchin);
+                if(strcheck && regexec(regex, strcheck, 0, 0, 0)==0)
+                  {
+                    ++nummatch;
+                    gal_list_sizet_add(&indexll, i);
+                  }
+              }
+
+            /* Free the regex_t structure: */
+            regfree(regex);
+
+            /* Put the `/' back into the input string. This is done because
+               after this function, the calling program might want to
+               inform the user of their exact input string. */
+            tmp->v[len-1]='/';
+          }
+
+
+        /* Not regular expression. */
+        else
+          {
+            tlong=strtol(tmp->v, &tailptr, 0);
+
+            /* INTEGER: If the string is an integer, then tailptr should
+               point to the null character. If it points to anything else,
+               it shows that we are not dealing with an integer (usable as
+               a column number). So floating point values are also not
+               acceptable. Since it is possible for the users to give zero
+               for the column number, we need to read the string as a
+               number first, then check it here. */
+            if(*tailptr=='\0')
+              {
+                /* Make sure the number is larger than zero! */
+                if(tlong<=0)
+                  error(EXIT_FAILURE, 0, "%s: column numbers must be "
+                        "positive (not zero or negative). You have asked "
+                        "for column number %ld", __func__, tlong);
+
+                /* Check if the given value is not larger than the number
+                   of columns in the input catalog (note that the user is
+                   counting from 1, not 0!) */
+                if(tlong>numcols)
+                  error(EXIT_FAILURE, 0, "%s: has %zu columns, but you "
+                        "have asked for column number %ld",
+                        gal_fits_name_save_as_string(filename, hdu),
+                        numcols, tlong);
+
+                /* Everything seems to be fine, put this column number in
+                   the output column numbers linked list. Note that
+                   internally, the column numbers start from 0, not 1.*/
+                gal_list_sizet_add(&indexll, tlong-1);
+                ++nummatch;
+              }
+
+
+
+            /* EXACT MATCH: */
+            else
+              {
+                /* Go through all the desired column information and add
+                   the column number when there is a match. */
+                for(i=0;i<numcols;++i)
+                  {
+                    /* Check if this column actually has any
+                       information. Then do a case-sensitive or insensitive
+                       comparison of the strings. */
+                    strcheck=table_set_strcheck(&allcols[i], searchin);
+                    if(strcheck && ( ignorecase
+                                     ? !strcasecmp(tmp->v, strcheck)
+                                     : !strcmp(tmp->v, strcheck) ) )
+                      {
+                        ++nummatch;
+                        gal_list_sizet_add(&indexll, i);
+                      }
+                  }
+              }
+          }
+
+
+        /* If there was no match, then report an error. This can only happen
+           for string matches, not column numbers, for numbers, the checks
+           are done (and program is aborted) before this step. */
+        if(nummatch==0)
+          {
+            asprintf(&errorstring, "`%s' didn't match any of the column %ss.",
+                     tmp->v, gal_tableintern_searchin_as_string(searchin));
+            gal_tableintern_error_col_selection(filename, hdu, errorstring);
+          }
+      }
+
+  /* cols==NULL */
+  else
+    for(i=0;i<numcols;++i)
+      gal_list_sizet_add(&indexll, i);
+
+
 
   /* Reverse the list. */
   gal_list_sizet_reverse(&indexll);
@@ -396,9 +405,6 @@ gal_table_read(char *filename, char *hdu, gal_list_str_t 
*cols,
   size_t i, numcols, numrows;
   gal_data_t *allcols, *out=NULL;
 
-  /* If the column string linked list is empty, no need to continue. */
-  if(cols==NULL) return NULL;
-
   /* First get the information of all the columns. */
   allcols=gal_table_info(filename, hdu, &numcols, &numrows, &tableformat);
 
diff --git a/lib/txt.c b/lib/txt.c
index 0f7fd90..5dd87a3 100644
--- a/lib/txt.c
+++ b/lib/txt.c
@@ -1245,6 +1245,9 @@ gal_txt_write(gal_data_t *input, gal_list_str_t *comment, 
char *filename)
   size_t i, j, num=0, fmtlen;
   gal_data_t *data, *next2d=NULL;
 
+  /* Make sure input is valid. */
+  if(input==NULL) error(EXIT_FAILURE, 0, "%s: input is NULL", __func__);
+
 
   /* Currently only 1 and 2 dimension datasets are acceptable. */
   if( input->ndim!=1 && input->ndim!=2 )
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 366b76e..cf4c6b4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -122,6 +122,11 @@ if COND_FITS
   fits/delete.sh: fits/write.sh.log
   fits/copyhdu.sh: fits/write.sh.log mkprof/mosaic2.sh.log
 endif
+if COND_MATCH
+  MAYBE_MATCH_TESTS = match/positions.sh
+
+  match/positions.sh: prepconf.sh.log
+endif
 if COND_MKCATALOG
   MAYBE_MKCATALOG_TESTS = mkcatalog/simple.sh mkcatalog/aperturephot.sh
 
@@ -220,10 +225,10 @@ lib/multithread.sh: mkprof/mosaic1.sh.log
 TESTS = prepconf.sh lib/multithread.sh $(MAYBE_VERSIONCPP_SH)              \
   $(MAYBE_ARITHMETIC_TESTS) $(MAYBE_BUILDPROG_TESTS)                       \
   $(MAYBE_CONVERTT_TESTS) $(MAYBE_CONVOLVE_TESTS) $(MAYBE_COSMICCAL_TESTS) \
-  $(MAYBE_CROP_TESTS) $(MAYBE_FITS_TESTS) $(MAYBE_MKCATALOG_TESTS)         \
-  $(MAYBE_MKNOISE_TESTS) $(MAYBE_MKPROF_TESTS) $(MAYBE_NOISECHISEL_TESTS)  \
-  $(MAYBE_STATISTICS_TESTS) $(MAYBE_SUBTRACTSKY_TESTS)                     \
-  $(MAYBE_TABLE_TESTS) $(MAYBE_WARP_TESTS)
+  $(MAYBE_CROP_TESTS) $(MAYBE_FITS_TESTS) $(MAYBE_MATCH_TESTS)             \
+  $(MAYBE_MKCATALOG_TESTS) $(MAYBE_MKNOISE_TESTS) $(MAYBE_MKPROF_TESTS)    \
+  $(MAYBE_NOISECHISEL_TESTS) $(MAYBE_STATISTICS_TESTS)                     \
+  $(MAYBE_SUBTRACTSKY_TESTS) $(MAYBE_TABLE_TESTS) $(MAYBE_WARP_TESTS)
 
 
 
@@ -231,9 +236,10 @@ TESTS = prepconf.sh lib/multithread.sh 
$(MAYBE_VERSIONCPP_SH)              \
 
 # Files to distribute along with the tests.
 EXTRA_DIST = $(TESTS) during-dev.sh buildprog/simpleio.c crop/cat.txt     \
-  mkprof/mkprofcat1.txt mkprof/ellipticalmasks.txt mkprof/clearcanvas.txt \
-  mkprof/mkprofcat2.txt mkprof/mkprofcat3.txt mkprof/mkprofcat4.txt       \
-  mkprof/radeccat.txt table/table.txt
+  match/positions-1.txt match/positions-2.txt mkprof/mkprofcat1.txt       \
+  mkprof/ellipticalmasks.txt mkprof/clearcanvas.txt mkprof/mkprofcat2.txt \
+  mkprof/mkprofcat3.txt mkprof/mkprofcat4.txt mkprof/radeccat.txt         \
+  table/table.txt
 
 
 
diff --git a/tests/match/positions-1.txt b/tests/match/positions-1.txt
new file mode 100644
index 0000000..5964218
--- /dev/null
+++ b/tests/match/positions-1.txt
@@ -0,0 +1,9 @@
+1   1   1
+2   2   2
+3   3   3
+4   4   4
+5   5   5
+6   6   6
+7   7   7
+8   8   8
+9   9   9
diff --git a/tests/match/positions-2.txt b/tests/match/positions-2.txt
new file mode 100644
index 0000000..5bb5395
--- /dev/null
+++ b/tests/match/positions-2.txt
@@ -0,0 +1,5 @@
+1   8.20    7.90
+2   4.80    5.20
+3   5.01    4.99
+4   6.10    6.05
+5   0.99    1.10
diff --git a/tests/match/positions.sh b/tests/match/positions.sh
new file mode 100755
index 0000000..4e69c1b
--- /dev/null
+++ b/tests/match/positions.sh
@@ -0,0 +1,52 @@
+# Match the two input catalogs
+#
+# See the Tests subsection of the manual for a complete explanation
+# (in the Installing gnuastro section).
+#
+# Original author:
+#     Mohammad Akhlaghi <address@hidden>
+# Contributing author(s):
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without any warranty.
+
+
+
+
+
+# Preliminaries
+# =============
+#
+# Set the variables (The executable is in the build tree). Do the
+# basic checks to see if the executable is made or if the defaults
+# file exists (basicchecks.sh is in the source tree).
+prog=match
+execname=../bin/$prog/ast$prog
+cat1=$topsrc/tests/$prog/positions-1.txt
+cat2=$topsrc/tests/$prog/positions-2.txt
+
+
+
+
+
+# Skip?
+# =====
+#
+# If the dependencies of the test don't exist, then skip it. There are two
+# types of dependencies:
+#
+#   - The executable was not made (for example due to a configure option),
+#
+#   - The input data was not made (for example the test that created the
+#     data file failed).
+if [ ! -f $execname ]; then echo "$execname not created."; exit 77; fi
+
+
+
+
+
+# Actual test script
+# ==================
+$execname $cat1 $cat2 --aperture=0.5 --log --output=match-positions.fits
diff --git a/tests/prepconf.sh b/tests/prepconf.sh
index 0e2a998..df1ddf5 100755
--- a/tests/prepconf.sh
+++ b/tests/prepconf.sh
@@ -69,8 +69,9 @@ rm addedoptions.txt
 #
 # Each utility's configuration file is copied in the `tests' directory for
 # easy readability.
-for prog in arithmetic buildprog convertt convolve cosmiccal crop fits \
-            mkcatalog mknoise mkprof noisechisel statistics table warp
+for prog in arithmetic buildprog convertt convolve cosmiccal crop fits  \
+            match mkcatalog mknoise mkprof noisechisel statistics table \
+            warp
 do
     cp $topsrc/bin/$prog/ast$prog.conf .gnuastro/ast$prog.conf
 done



reply via email to

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