gnuastro-commits
[Top][All Lists]
Advanced

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

[gnuastro-commits] master aa80ac4 112/113: Imported recent updates in ma


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master aa80ac4 112/113: Imported recent updates in master branch, minor conflict fixed
Date: Fri, 16 Apr 2021 10:34:04 -0400 (EDT)

branch: master
commit aa80ac478093ffd88965ca1a1e5c93a0057b0426
Merge: 6e11585 4137ccd
Author: Mohammad Akhlaghi <mohammad@akhlaghi.org>
Commit: Mohammad Akhlaghi <mohammad@akhlaghi.org>

    Imported recent updates in master branch, minor conflict fixed
    
    The minor conflict was in a paragraph in the book that has been easily
    fixed.
---
 .autom4te.cfg                                      |    2 +-
 .dir-locals.el                                     |    2 +-
 .gitignore                                         |   10 +-
 .mailmap                                           |    2 +-
 ChangeLog                                          |  118 +-
 Makefile.am                                        |   94 +-
 NEWS                                               | 1327 +++-
 README                                             |   24 +-
 README-hacking                                     |    8 +-
 THANKS                                             |   29 +-
 bin/TEMPLATE/Makefile.am                           |   23 +-
 bin/TEMPLATE/TEMPLATE.c                            |    2 +-
 bin/TEMPLATE/TEMPLATE.h                            |    2 +-
 bin/TEMPLATE/args.h                                |    2 +-
 bin/TEMPLATE/astTEMPLATE.conf                      |    4 +-
 bin/TEMPLATE/authors-cite.h                        |    8 +-
 bin/TEMPLATE/main.c                                |    2 +-
 bin/TEMPLATE/main.h                                |    2 +-
 bin/TEMPLATE/ui.c                                  |   22 +-
 bin/TEMPLATE/ui.h                                  |    2 +-
 bin/arithmetic/Makefile.am                         |   16 +-
 bin/arithmetic/args.h                              |    8 +-
 bin/arithmetic/arithmetic.c                        |  414 +-
 bin/arithmetic/arithmetic.h                        |    6 +-
 bin/arithmetic/astarithmetic.conf                  |    6 +-
 bin/arithmetic/authors-cite.h                      |    8 +-
 bin/arithmetic/main.c                              |    4 +-
 bin/arithmetic/main.h                              |   12 +-
 bin/arithmetic/operands.c                          |  254 +-
 bin/arithmetic/operands.h                          |    5 +-
 bin/arithmetic/ui.c                                |   77 +-
 bin/arithmetic/ui.h                                |    2 +-
 bin/buildprog/Makefile.am                          |   32 +-
 bin/buildprog/args.h                               |   10 +-
 bin/buildprog/astbuildprog.conf.in                 |    4 +-
 bin/buildprog/authors-cite.h                       |    8 +-
 bin/buildprog/buildprog.c                          |   12 +-
 bin/buildprog/buildprog.h                          |    2 +-
 bin/buildprog/main.c                               |    2 +-
 bin/buildprog/main.h                               |    4 +-
 bin/buildprog/ui.c                                 |   46 +-
 bin/buildprog/ui.h                                 |    2 +-
 bin/convertt/Makefile.am                           |   17 +-
 bin/convertt/args.h                                |    4 +-
 bin/convertt/astconvertt.conf                      |    4 +-
 bin/convertt/authors-cite.h                        |    8 +-
 bin/convertt/color.c                               |   19 +-
 bin/convertt/color.h                               |    2 +-
 bin/convertt/convertt.c                            |   12 +-
 bin/convertt/convertt.h                            |    2 +-
 bin/convertt/main.c                                |    2 +-
 bin/convertt/main.h                                |    3 +-
 bin/convertt/ui.c                                  |  108 +-
 bin/convertt/ui.h                                  |    2 +-
 bin/convolve/Makefile.am                           |   17 +-
 bin/convolve/args.h                                |    6 +-
 bin/convolve/astconvolve.conf                      |    4 +-
 bin/convolve/authors-cite.h                        |    8 +-
 bin/convolve/convolve.c                            |   43 +-
 bin/convolve/convolve.h                            |    2 +-
 bin/convolve/main.c                                |    2 +-
 bin/convolve/main.h                                |    2 +-
 bin/convolve/ui.c                                  |   54 +-
 bin/convolve/ui.h                                  |    2 +-
 bin/cosmiccal/Makefile.am                          |   17 +-
 bin/cosmiccal/args.h                               |  102 +-
 bin/cosmiccal/astcosmiccal.conf                    |    4 +-
 bin/cosmiccal/authors-cite.h                       |    8 +-
 bin/cosmiccal/cosmiccal.c                          |   13 +-
 bin/cosmiccal/cosmiccal.h                          |    2 +-
 bin/cosmiccal/main.c                               |    2 +-
 bin/cosmiccal/main.h                               |    7 +-
 bin/cosmiccal/ui.c                                 |  142 +-
 bin/cosmiccal/ui.h                                 |   13 +-
 bin/crop/Makefile.am                               |   17 +-
 bin/crop/args.h                                    |   41 +-
 bin/crop/astcrop.conf                              |    4 +-
 bin/crop/authors-cite.h                            |    8 +-
 bin/crop/crop.c                                    |   32 +-
 bin/crop/crop.h                                    |    2 +-
 bin/crop/main.c                                    |    2 +-
 bin/crop/main.h                                    |   10 +-
 bin/crop/onecrop.c                                 |  229 +-
 bin/crop/onecrop.h                                 |    7 +-
 bin/crop/ui.c                                      |  302 +-
 bin/crop/ui.h                                      |    6 +-
 bin/crop/wcsmode.c                                 |   30 +-
 bin/crop/wcsmode.h                                 |    2 +-
 bin/fits/Makefile.am                               |   17 +-
 bin/fits/args.h                                    |  132 +-
 bin/fits/astfits.conf                              |    4 +-
 bin/fits/authors-cite.h                            |    8 +-
 bin/fits/extension.c                               |    2 +-
 bin/fits/extension.h                               |    2 +-
 bin/fits/fits.c                                    |  278 +-
 bin/fits/fits.h                                    |    2 +-
 bin/fits/keywords.c                                |  491 +-
 bin/fits/keywords.h                                |    2 +-
 bin/fits/main.c                                    |    2 +-
 bin/fits/main.h                                    |   51 +-
 bin/fits/ui.c                                      |  173 +-
 bin/fits/ui.h                                      |   48 +-
 bin/gnuastro.conf                                  |   21 +-
 bin/match/Makefile.am                              |   17 +-
 bin/match/args.h                                   |    4 +-
 bin/match/astmatch.conf                            |    6 +-
 bin/match/authors-cite.h                           |    8 +-
 bin/match/main.c                                   |    2 +-
 bin/match/main.h                                   |    2 +-
 bin/match/match.c                                  |   40 +-
 bin/match/match.h                                  |    2 +-
 bin/match/ui.c                                     |  124 +-
 bin/match/ui.h                                     |    2 +-
 bin/mkcatalog/Makefile.am                          |   17 +-
 bin/mkcatalog/args.h                               |  504 +-
 bin/mkcatalog/astmkcatalog.conf                    |    5 +-
 bin/mkcatalog/authors-cite.h                       |   46 +-
 bin/mkcatalog/columns.c                            |  804 +-
 bin/mkcatalog/columns.h                            |    2 +-
 bin/mkcatalog/main.c                               |    2 +-
 bin/mkcatalog/main.h                               |   70 +-
 bin/mkcatalog/mkcatalog.c                          |  162 +-
 bin/mkcatalog/mkcatalog.h                          |    2 +-
 bin/mkcatalog/parse.c                              |  739 +-
 bin/mkcatalog/parse.h                              |    4 +-
 bin/mkcatalog/ui.c                                 |  583 +-
 bin/mkcatalog/ui.h                                 |   36 +-
 bin/mkcatalog/upperlimit.c                         |   34 +-
 bin/mkcatalog/upperlimit.h                         |    2 +-
 bin/mknoise/Makefile.am                            |   18 +-
 bin/mknoise/args.h                                 |   17 +-
 bin/mknoise/astmknoise.conf                        |    7 +-
 bin/mknoise/authors-cite.h                         |    8 +-
 bin/mknoise/main.c                                 |    2 +-
 bin/mknoise/main.h                                 |    6 +-
 bin/mknoise/mknoise.c                              |   86 +-
 bin/mknoise/mknoise.h                              |    2 +-
 bin/mknoise/ui.c                                   |   84 +-
 bin/mknoise/ui.h                                   |   15 +-
 bin/mkprof/Makefile.am                             |   17 +-
 bin/mkprof/args.h                                  |   39 +-
 bin/mkprof/astmkprof-3d.conf                       |    6 +-
 bin/mkprof/astmkprof.conf                          |    5 +-
 bin/mkprof/authors-cite.h                          |    8 +-
 bin/mkprof/main.c                                  |    2 +-
 bin/mkprof/main.h                                  |   11 +-
 bin/mkprof/mkprof.c                                |  141 +-
 bin/mkprof/mkprof.h                                |    2 +-
 bin/mkprof/oneprofile.c                            |   34 +-
 bin/mkprof/oneprofile.h                            |    2 +-
 bin/mkprof/profiles.c                              |   40 +-
 bin/mkprof/profiles.h                              |    5 +-
 bin/mkprof/ui.c                                    |  361 +-
 bin/mkprof/ui.h                                    |    4 +-
 bin/noisechisel/Makefile.am                        |   15 +-
 bin/noisechisel/args.h                             |    8 +-
 bin/noisechisel/astnoisechisel.conf                |   10 +-
 bin/noisechisel/authors-cite.h                     |   33 +-
 bin/noisechisel/detection.c                        |  103 +-
 bin/noisechisel/detection.h                        |    2 +-
 bin/noisechisel/kernel-2d.h                        |    8 +-
 bin/noisechisel/main.c                             |    2 +-
 bin/noisechisel/main.h                             |    3 +-
 bin/noisechisel/noisechisel.c                      |   44 +-
 bin/noisechisel/noisechisel.h                      |    2 +-
 bin/noisechisel/sky.c                              |  151 +-
 bin/noisechisel/sky.h                              |    2 +-
 bin/noisechisel/threshold.c                        |  107 +-
 bin/noisechisel/threshold.h                        |    2 +-
 bin/noisechisel/ui.c                               |   86 +-
 bin/noisechisel/ui.h                               |    2 +-
 bin/{convertt => query}/Makefile.am                |   29 +-
 bin/{crop => query}/args.h                         |  285 +-
 bin/{fits/astfits.conf => query/astquery.conf}     |   14 +-
 bin/query/astron.c                                 |   77 +
 bin/{TEMPLATE/TEMPLATE.h => query/astron.h}        |   14 +-
 bin/{TEMPLATE => query}/authors-cite.h             |   12 +-
 bin/query/gaia.c                                   |  122 +
 bin/{TEMPLATE/TEMPLATE.h => query/gaia.h}          |   14 +-
 bin/{TEMPLATE => query}/main.c                     |   14 +-
 bin/query/main.h                                   |   78 +
 bin/query/ned.c                                    |   88 +
 bin/{TEMPLATE/TEMPLATE.h => query/ned.h}           |   14 +-
 bin/query/query.c                                  |  392 +
 bin/{TEMPLATE/TEMPLATE.h => query/query.h}         |   24 +-
 bin/query/tap.c                                    |  440 ++
 bin/{TEMPLATE/TEMPLATE.h => query/tap.h}           |   17 +-
 bin/query/ui.c                                     |  578 ++
 bin/{table => query}/ui.h                          |   50 +-
 bin/query/vizier.c                                 |  179 +
 bin/{TEMPLATE/TEMPLATE.h => query/vizier.h}        |   14 +-
 bin/script/Makefile.am                             |   19 +-
 bin/script/make-ds9-reg.in                         |  326 +
 bin/script/sort-by-night.in                        |  108 +-
 bin/segment/Makefile.am                            |   17 +-
 bin/segment/args.h                                 |    8 +-
 bin/segment/astsegment.conf                        |    4 +-
 bin/segment/authors-cite.h                         |   33 +-
 bin/segment/clumps.c                               |   86 +-
 bin/segment/clumps.h                               |    4 +-
 bin/segment/kernel-2d.h                            |    8 +-
 bin/segment/main.c                                 |    2 +-
 bin/segment/main.h                                 |    2 +-
 bin/segment/segment.c                              |  614 +-
 bin/segment/segment.h                              |    2 +-
 bin/segment/ui.c                                   |  110 +-
 bin/segment/ui.h                                   |    2 +-
 bin/statistics/Makefile.am                         |   15 +-
 bin/statistics/args.h                              |   81 +-
 bin/statistics/aststatistics.conf                  |    9 +-
 bin/statistics/authors-cite.h                      |    8 +-
 bin/statistics/contour.c                           |    2 +-
 bin/statistics/contour.h                           |    2 +-
 bin/statistics/main.c                              |    2 +-
 bin/statistics/main.h                              |    9 +-
 bin/statistics/sky.c                               |   32 +-
 bin/statistics/sky.h                               |    2 +-
 bin/statistics/statistics.c                        |  263 +-
 bin/statistics/statistics.h                        |    2 +-
 bin/statistics/ui.c                                |  362 +-
 bin/statistics/ui.h                                |    7 +-
 bin/table/Makefile.am                              |   17 +-
 bin/table/args.h                                   |  173 +-
 bin/table/arithmetic.c                             |  402 +-
 bin/table/arithmetic.h                             |   14 +-
 bin/table/asttable.conf                            |    7 +-
 bin/table/authors-cite.h                           |    8 +-
 bin/table/main.c                                   |    2 +-
 bin/table/main.h                                   |   27 +-
 bin/table/table.c                                  |  707 +-
 bin/table/table.h                                  |    2 +-
 bin/table/ui.c                                     |  306 +-
 bin/table/ui.h                                     |   18 +-
 bin/warp/Makefile.am                               |   17 +-
 bin/warp/args.h                                    |    2 +-
 bin/warp/astwarp.conf                              |    4 +-
 bin/warp/authors-cite.h                            |    8 +-
 bin/warp/main.c                                    |    2 +-
 bin/warp/main.h                                    |    2 +-
 bin/warp/ui.c                                      |  129 +-
 bin/warp/ui.h                                      |    2 +-
 bin/warp/warp.c                                    |   56 +-
 bin/warp/warp.h                                    |    2 +-
 bootstrap.conf                                     |   74 +-
 bootstrapped/README                                |    4 +-
 configure.ac                                       |  377 +-
 developer-build                                    |   36 +-
 doc/Makefile.am                                    |   64 +-
 doc/README                                         |   16 +-
 doc/announce-acknowledge.txt                       |   16 +-
 doc/coming-soon.html                               |   99 +
 doc/fdl.texi                                       |  505 ++
 doc/fonts.css                                      |    2 +-
 doc/formath.texi                                   |    2 +-
 doc/forwebpage                                     |   28 +-
 doc/genauthors                                     |   20 +-
 doc/gnuastro-figures/README                        |    4 +-
 doc/gnuastro.en.html                               |  218 +-
 doc/gnuastro.fr.html                               |  260 +-
 doc/gnuastro.texi                                  | 7995 ++++++++++++++------
 doc/gnuastro.translist                             |    2 +-
 doc/javascript.html                                |   37 +-
 doc/plotsrc/Makefile                               |   53 +-
 doc/plotsrc/README                                 |   10 +-
 doc/plotsrc/all.tex                                |    2 +-
 doc/plotsrc/conversions.sh                         |    2 +-
 doc/plotsrc/tex/flatplane.tex                      |    2 +-
 doc/plotsrc/tex/iandtime.tex                       |    2 +-
 doc/plotsrc/tex/samplingfreq.tex                   |    2 +-
 doc/plotsrc/tex/sphereandplane.tex                 |    2 +-
 doc/release-checklist.txt                          |  230 +-
 doc/style.css                                      |    8 +-
 genauthors                                         |   10 +-
 lib/Makefile.am                                    |  142 +-
 lib/arithmetic-and.c                               |    6 +-
 lib/arithmetic-bitand.c                            |    6 +-
 lib/arithmetic-bitlsh.c                            |    6 +-
 lib/arithmetic-bitor.c                             |    6 +-
 lib/arithmetic-bitrsh.c                            |    6 +-
 lib/arithmetic-bitxor.c                            |    6 +-
 lib/arithmetic-divide.c                            |    6 +-
 lib/arithmetic-eq.c                                |    6 +-
 lib/arithmetic-ge.c                                |    6 +-
 lib/arithmetic-gt.c                                |    6 +-
 lib/arithmetic-le.c                                |    6 +-
 lib/arithmetic-lt.c                                |    6 +-
 lib/arithmetic-minus.c                             |    6 +-
 lib/arithmetic-modulo.c                            |    6 +-
 lib/arithmetic-multiply.c                          |    6 +-
 lib/arithmetic-ne.c                                |    6 +-
 lib/arithmetic-or.c                                |    6 +-
 lib/arithmetic-plus.c                              |    6 +-
 lib/arithmetic-set.c                               |  193 +
 lib/arithmetic.c                                   |  707 +-
 lib/array.c                                        |    2 +-
 lib/binary.c                                       |  147 +-
 lib/blank.c                                        |  247 +-
 lib/box.c                                          |   42 +-
 lib/checkset.c                                     |  332 +-
 lib/convolve.c                                     |   73 +-
 lib/cosmology.c                                    |   26 +-
 lib/data.c                                         |  230 +-
 lib/dimension.c                                    |   86 +-
 lib/eps.c                                          |    8 +-
 lib/fits.c                                         |  977 ++-
 lib/git.c                                          |    2 +-
 lib/gnuastro-internal/README                       |    7 +-
 lib/gnuastro-internal/arithmetic-and.h             |    2 +-
 lib/gnuastro-internal/arithmetic-binary.h          |    8 +-
 lib/gnuastro-internal/arithmetic-bitand.h          |    2 +-
 lib/gnuastro-internal/arithmetic-bitlsh.h          |    2 +-
 lib/gnuastro-internal/arithmetic-bitor.h           |    2 +-
 lib/gnuastro-internal/arithmetic-bitrsh.h          |    2 +-
 lib/gnuastro-internal/arithmetic-bitxor.h          |    2 +-
 lib/gnuastro-internal/arithmetic-divide.h          |    2 +-
 lib/gnuastro-internal/arithmetic-eq.h              |    2 +-
 lib/gnuastro-internal/arithmetic-ge.h              |    2 +-
 lib/gnuastro-internal/arithmetic-gt.h              |    2 +-
 lib/gnuastro-internal/arithmetic-internal.h        |   14 +-
 lib/gnuastro-internal/arithmetic-le.h              |    2 +-
 lib/gnuastro-internal/arithmetic-lt.h              |    2 +-
 lib/gnuastro-internal/arithmetic-minus.h           |    2 +-
 lib/gnuastro-internal/arithmetic-modulo.h          |    2 +-
 lib/gnuastro-internal/arithmetic-multiply.h        |    2 +-
 lib/gnuastro-internal/arithmetic-ne.h              |    2 +-
 lib/gnuastro-internal/arithmetic-or.h              |    2 +-
 lib/gnuastro-internal/arithmetic-plus.h            |    2 +-
 lib/gnuastro-internal/arithmetic-set.h             |   51 +
 lib/gnuastro-internal/checkset.h                   |   15 +-
 lib/gnuastro-internal/commonopts.h                 |   14 +-
 lib/gnuastro-internal/config.h.in                  |    7 +-
 lib/gnuastro-internal/fixedstringmacros.h          |    6 +-
 lib/gnuastro-internal/options.h                    |   25 +-
 lib/gnuastro-internal/tableintern.h                |   12 +-
 lib/gnuastro-internal/tile-internal.h              |   22 +-
 lib/gnuastro-internal/timing.h                     |    2 +-
 .../pdf.h => gnuastro-internal/wcsdistortion.h}    |   33 +-
 lib/gnuastro.pc.in                                 |    4 +-
 lib/gnuastro/README                                |    6 +-
 lib/gnuastro/arithmetic.h                          |   40 +-
 lib/gnuastro/array.h                               |    2 +-
 lib/gnuastro/binary.h                              |    8 +-
 lib/gnuastro/blank.h                               |   19 +-
 lib/gnuastro/box.h                                 |    2 +-
 lib/gnuastro/convolve.h                            |    2 +-
 lib/gnuastro/cosmology.h                           |    8 +-
 lib/gnuastro/data.h                                |   73 +-
 lib/gnuastro/dimension.h                           |   52 +-
 lib/gnuastro/eps.h                                 |    2 +-
 lib/gnuastro/fits.h                                |   67 +-
 lib/gnuastro/git.h                                 |    2 +-
 lib/gnuastro/interpolate.h                         |   32 +-
 lib/gnuastro/jpeg.h                                |    2 +-
 lib/gnuastro/{jpeg.h => kdtree.h}                  |   35 +-
 lib/gnuastro/label.h                               |    2 +-
 lib/gnuastro/list.h                                |    2 +-
 lib/gnuastro/match.h                               |    2 +-
 lib/gnuastro/pdf.h                                 |    2 +-
 lib/gnuastro/permutation.h                         |    2 +-
 lib/gnuastro/pointer.h                             |   11 +-
 lib/gnuastro/polygon.h                             |   21 +-
 lib/gnuastro/qsort.h                               |    2 +-
 lib/gnuastro/speclines.h                           |   87 +-
 lib/gnuastro/statistics.h                          |   15 +-
 lib/gnuastro/table.h                               |   12 +-
 lib/gnuastro/threads.h                             |   20 +-
 lib/gnuastro/tiff.h                                |    2 +-
 lib/gnuastro/tile.h                                |   68 +-
 lib/gnuastro/txt.h                                 |    9 +-
 lib/gnuastro/type.h                                |   20 +-
 .../arithmetic-internal.h => gnuastro/units.h}     |   50 +-
 lib/gnuastro/wcs.h                                 |   71 +-
 lib/interpolate.c                                  |  129 +-
 lib/jpeg.c                                         |   10 +-
 lib/kdtree.c                                       |  594 ++
 lib/label.c                                        |  114 +-
 lib/list.c                                         |   13 +-
 lib/match.c                                        |  184 +-
 lib/options.c                                      |  675 +-
 lib/pdf.c                                          |    6 +-
 lib/permutation.c                                  |   14 +-
 lib/pointer.c                                      |  131 +-
 lib/polygon.c                                      |  489 +-
 lib/qsort.c                                        |    6 +-
 lib/speclines.c                                    |  102 +-
 lib/statistics.c                                   |  532 +-
 lib/table.c                                        |   89 +-
 lib/tableintern.c                                  |   61 +-
 lib/threads.c                                      |   65 +-
 lib/tiff.c                                         |   42 +-
 lib/tile-internal.c                                |  608 +-
 lib/tile.c                                         |  175 +-
 lib/timing.c                                       |   14 +-
 lib/txt.c                                          |  541 +-
 lib/type.c                                         |   76 +-
 lib/units.c                                        |  333 +
 lib/wcs.c                                          | 1103 ++-
 lib/wcsdistortion.c                                | 1978 +++++
 tests/Makefile.am                                  |   34 +-
 tests/arithmetic/connected-components.sh           |    4 +-
 tests/arithmetic/onlynumbers.sh                    |    4 +-
 tests/arithmetic/or.sh                             |    6 +-
 tests/arithmetic/snimage.sh                        |    4 +-
 tests/arithmetic/where.sh                          |    6 +-
 tests/buildprog/simpleio.c                         |    2 +-
 tests/buildprog/simpleio.sh                        |   17 +-
 tests/convertt/blankch.sh                          |    4 +-
 tests/convertt/fitstojpeg.sh                       |    4 +-
 tests/convertt/fitstojpegcmyk.sh                   |    4 +-
 tests/convertt/fitstopdf.sh                        |    4 +-
 tests/convertt/fitstotxt.sh                        |    4 +-
 tests/convertt/jpegtofits.sh                       |    4 +-
 tests/convertt/jpegtotxt.sh                        |    4 +-
 tests/convolve/frequency.sh                        |    4 +-
 tests/convolve/spatial.sh                          |    4 +-
 tests/cosmiccal/simpletest.sh                      |    4 +-
 tests/crop/cat.txt                                 |    2 +-
 tests/crop/imgcat.sh                               |    4 +-
 tests/crop/imgcenter.sh                            |    4 +-
 tests/crop/imgcenternoblank.sh                     |    4 +-
 tests/crop/imgpolygon.sh                           |    4 +-
 tests/crop/{imgoutpolygon.sh => imgpolygonout.sh}  |    6 +-
 tests/crop/section.sh                              |    4 +-
 tests/crop/wcscat.sh                               |    4 +-
 tests/crop/wcscenter.sh                            |    4 +-
 tests/crop/wcspolygon.sh                           |    4 +-
 tests/during-dev.sh                                |   22 +-
 tests/fits/copyhdu.sh                              |    4 +-
 tests/fits/delete.sh                               |    4 +-
 tests/fits/print.sh                                |    4 +-
 tests/fits/update.sh                               |    4 +-
 tests/fits/write.sh                                |    4 +-
 tests/lib/multithread.c                            |   18 +-
 tests/lib/multithread.sh                           |    4 +-
 tests/lib/versioncxx.cpp                           |    2 +-
 tests/lib/versioncxx.sh                            |    4 +-
 tests/match/merged-cols.sh                         |    4 +-
 tests/match/positions-1.txt                        |    2 +-
 tests/match/positions-2.txt                        |    2 +-
 tests/match/positions.sh                           |    4 +-
 tests/mkcatalog/aperturephot.sh                    |    4 +-
 tests/mkcatalog/detections.sh                      |    4 +-
 tests/mkcatalog/objects-clumps.sh                  |    4 +-
 tests/mknoise/addnoise-3d.sh                       |    3 +-
 tests/mknoise/addnoise.sh                          |    9 +-
 tests/mkprof/clearcanvas.sh                        |    4 +-
 tests/mkprof/clearcanvas.txt                       |    4 +-
 tests/mkprof/ellipticalmasks.sh                    |    4 +-
 tests/mkprof/ellipticalmasks.txt                   |    2 +-
 tests/mkprof/mkprofcat1.txt                        |    2 +-
 tests/mkprof/mkprofcat2.txt                        |    2 +-
 tests/mkprof/mkprofcat3.txt                        |    2 +-
 tests/mkprof/mkprofcat4.txt                        |    2 +-
 tests/mkprof/mosaic1.sh                            |    4 +-
 tests/mkprof/mosaic2.sh                            |    4 +-
 tests/mkprof/mosaic3.sh                            |    4 +-
 tests/mkprof/mosaic4.sh                            |    4 +-
 tests/mkprof/radeccat.sh                           |    4 +-
 tests/mkprof/radeccat.txt                          |    2 +-
 tests/noisechisel/noisechisel.sh                   |    6 +-
 tests/prepconf.sh                                  |   12 +-
 tests/script/list-by-night.sh                      |   10 +-
 tests/segment/segment.sh                           |    4 +-
 tests/statistics/basicstats.sh                     |    4 +-
 tests/statistics/estimate_sky.sh                   |    4 +-
 tests/statistics/from-stdin.sh                     |    4 +-
 tests/statistics/stdin-input.txt                   |    2 +-
 tests/table/fits-ascii-to-txt.sh                   |    4 +-
 tests/table/fits-binary-to-txt.sh                  |    4 +-
 tests/table/table.txt                              |    6 +-
 tests/table/txt-to-fits-ascii.sh                   |    4 +-
 tests/table/txt-to-fits-binary.sh                  |    4 +-
 tests/warp/homographic.sh                          |    4 +-
 tests/warp/warp_scale.sh                           |    4 +-
 474 files changed, 30074 insertions(+), 9204 deletions(-)

diff --git a/.autom4te.cfg b/.autom4te.cfg
index 462af1f..e6d125d 100644
--- a/.autom4te.cfg
+++ b/.autom4te.cfg
@@ -14,7 +14,7 @@
 # directory within the bootstrapped directory to keep things on the
 # top directory clean.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2020 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
diff --git a/.dir-locals.el b/.dir-locals.el
index 25774d2..6164933 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -9,7 +9,7 @@
 ;;
 ;; For more information see (info "(emacs) Directory Variables")
 ;;
-;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
 ;;
 ;; Copying and distribution of this file, with or without modification,
 ;; are permitted in any medium without royalty provided the copyright
diff --git a/.gitignore b/.gitignore
index b0ce3cc..26b30b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,7 @@
 # To help readability, please add new files based on the length of the line
 # you wish to add.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2020 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -85,6 +85,7 @@ stamp-h1
 Makefile
 asttable
 astmatch
+astquery
 ltmain.sh
 .dirstamp
 configure
@@ -99,10 +100,14 @@ astmknoise
 config.sub
 install-sh
 aclocal.m4
+confdefs.h
+conftest.c
+.bootstrap*
 astconvertt
 astconvolve
 test-driver
 config.h.in
+conftest.err
 lib/config.h
 astcosmiccal
 astmkcatalog
@@ -160,3 +165,6 @@ bin/buildprog/astbuildprog.conf
 # directory, we have abandoned the convention above.
 
 /bootstrapped/build-aux
+
+# vscode
+.vscode/
diff --git a/.mailmap b/.mailmap
index 90c7765..a012604 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,6 +1,6 @@
 # Map different emails to people in Git.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2020 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
diff --git a/ChangeLog b/ChangeLog
index c709bd8..6cae471 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -87,10 +87,10 @@
 2015-09-17  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
        * bin/mkprof/mkprof.c (mkprof): Only print a log file if the
-       `--nolog' option is not called.
+       '--nolog' option is not called.
 
        * bin/imgcrop/imgcrop.c (imgcrop): Only print a log file if the
-       `--nolog' option is not called.
+       '--nolog' option is not called.
 
        * bin/mkprof/main.h (mkprofparams): Removed the p->dir0file1
        parameter from the main structure.
@@ -113,8 +113,8 @@
        blank. Before the blank fraction was ignored.
 
        * bin/imgstat/imgstat.c (reportsimplestats): Now uses the value to
-       the `--mirrordist' option instead of the fixed value of 1.5.
-       (reportsimplestats): Report the `NOT ACCURATE' warning in a
+       the '--mirrordist' option instead of the fixed value of 1.5.
+       (reportsimplestats): Report the 'NOT ACCURATE' warning in a
        separate line to make reading by AWK easier, also removed comma
        between outputs for the same reason.
 
@@ -162,7 +162,7 @@
 2015-07-22  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
        * bin/noisechisel/thresh.c (snthresh): Had mistakenly used
-       `p->detquant', for both detection and segmentation! This is now
+       'p->detquant', for both detection and segmentation! This is now
        corrected.
 
        * bin/mkcatalog/mkcatalog.c (secondpass): Sets the river flux to
@@ -180,10 +180,10 @@
 2015-07-18  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
        * bin/noisechisel/segmentation.c (nextavailablelabel): Now
-       accounts for the `--grownclumps' option.
+       accounts for the '--grownclumps' option.
 
        * bin/noisechisel/args.h (options): Corrected the type of output
-       for `--gthresh'. I had forgot to include any for it!
+       for '--gthresh'. I had forgot to include any for it!
 
 2015-07-17  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
@@ -224,9 +224,9 @@
        flux is subtracted here, not before.
 
        * lib/fitsarrayvv.c: The numblank variable did not actually store
-       the `number' of blank pixels, it was only a 0 or 1 value to
+       the 'number' of blank pixels, it was only a 0 or 1 value to
        specify if there are any blank pixels or not. It is now changed to
-       `anyblank' to be more clear and not confuse the readers. All the
+       'anyblank' to be more clear and not confuse the readers. All the
        Gnuastro programs that used this variable, were also corrected.
 
 2015-07-06  Mohammad Akhlaghi  <akhlaghi@gnu.org>
@@ -389,13 +389,13 @@
 
 2015-06-11  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
-       * bin/noisechisel/args.h (options): `minbfrac' and `minnumfalse'
-       options moved under the `Input' category from the detections
+       * bin/noisechisel/args.h (options): 'minbfrac' and 'minnumfalse'
+       options moved under the 'Input' category from the detections
        category. This move was because these two options are used by both
        the detection and segmentation steps and thus keeping them under
-       detection would be confusing. Also `checkthresh' was changed to
-       `checkthreshold'. Also, the option `numerosion' was changed to
-       `erode' to be consistent with the opening and dilate options.
+       detection would be confusing. Also 'checkthresh' was changed to
+       'checkthreshold'. Also, the option 'numerosion' was changed to
+       'erode' to be consistent with the opening and dilate options.
 
 2015-06-10  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
@@ -442,12 +442,12 @@
        generator to ui.c to make things faster and more easier to
        understand.
 
-       * bin/mknoise/args.h (argp_option): Removed the `backgroundinmean'
+       * bin/mknoise/args.h (argp_option): Removed the 'backgroundinmean'
        option, because I couldn't figure out what use it would have. If
        it is needed we can add it again later!
 
-       * bin/imgstat/args.h (argp_option): Changed `binonzero' option to
-       the more general `onebinvalue' option.
+       * bin/imgstat/args.h (argp_option): Changed 'binonzero' option to
+       the more general 'onebinvalue' option.
 
        * bin/convolve/convolve.c (removepaddingcorrectroundoff):
        Corrected bug: would return *d in both cases! It is corrected now.
@@ -507,18 +507,18 @@
        functions for the minimum and maximum values. NaN values will
        automatically fail all comparisons.
 
-       * lib/timing.c (reporttiming): Removed the `in' from reporting
+       * lib/timing.c (reporttiming): Removed the 'in' from reporting
        seconds.
 
        * include/timing.h (VERBMSGLENGTH_V): Changed to 45 from 40.
 
 2015-05-27  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
-       * bin/convolve/args.h (options): Remove the `noedgecorrection'
+       * bin/convolve/args.h (options): Remove the 'noedgecorrection'
        option. Since it was useless. Also changed the old short option
-       for spatial (`s') to `p'. Because `s' is now needed for the mesh
-       size option. Also changed the old short option for khdu (`H') to
-       `U', to make it similar to all the other programs that do
+       for spatial ('s') to 'p'. Because 's' is now needed for the mesh
+       size option. Also changed the old short option for khdu ('H') to
+       'U', to make it similar to all the other programs that do
        convolution.
 
        * include/mesh.h (meshparams): the garrays are now only
@@ -529,13 +529,13 @@
 
 2015-05-25  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
-       * lib/mesh.c (operateonmesh): New name for `fillmesh'. Now the
+       * lib/mesh.c (operateonmesh): New name for 'fillmesh'. Now the
        function that will be spinned off from each thread can be
        specified from the outside.
 
 2015-05-24  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
-       * bin/imgcrop/crop.c (sectionparser): When only a `*' was given
+       * bin/imgcrop/crop.c (sectionparser): When only a '*' was given
        with no positive or negative following number, a segmentation
        fault would happen. This bug is now fixed.
 
@@ -548,8 +548,8 @@
        the case when 2*mi is larger than size.
 
        * bin/subtractsky/args.h: --numnearest and --kernelwidth moved to
-       the `Mesh grid' part of the help output. --checksmoothing and
-       --checkinterpolation also moved to the `Mesh grid' part.
+       the 'Mesh grid' part of the help output. --checksmoothing and
+       --checkinterpolation also moved to the 'Mesh grid' part.
 
        * lib/mesh.c (checkgarray): Modified to show both the full, or
        contiguous over full image, garray and the default one which is
@@ -642,7 +642,7 @@
 
        * bin/mkprof/oneprofile.c (makepixbypix): Will only do monte carlo
        integration when the profile is not a constant value.
-       (setprofparams): Changed the function of point to `Fixed'.
+       (setprofparams): Changed the function of point to 'Fixed'.
 
        * bin/mkprof/mkprof.c (write): Changed to replace and read from an
        image and add profiles on that.
@@ -666,8 +666,8 @@
 2015-04-06  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
        * bin/mkprof/mkprof.c: Corrected condition when a thread would
-       finish `build' profiles but the last set of its profiles wouldn't
-       pass onto `write'.
+       finish 'build' profiles but the last set of its profiles wouldn't
+       pass onto 'write'.
 
        * bin/mknoise/: Changed background flux unit to magnitudes, not
        flux.
@@ -676,13 +676,13 @@
 
 2015-04-03  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
-       * lib/fitsarrayvv.c: Changed all occurences of `nul' or `NUL' to
-       `blank' or `BLANK'. The FITS standard defines a BLANK keyword for
+       * lib/fitsarrayvv.c: Changed all occurences of 'nul' or 'NUL' to
+       'blank' or 'BLANK'. The FITS standard defines a BLANK keyword for
        integer types.
        (copyrightandend): Will now right extra headers.
        (arraytofitsimg): Can now accept header to write to output.
 
-       * include/fitsarrayvv.h: Changed all `NUL' macros to `BLANK'.
+       * include/fitsarrayvv.h: Changed all 'NUL' macros to 'BLANK'.
 
 2015-04-02  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
@@ -729,8 +729,8 @@
        * bin/convolve/ui.c (preparearrays): The flip parts was missing a
        -1.
 
-       * doc/gnuastro.texi: Changed all `gnuastro' occurrences in the
-       text to `Gnuastro'.
+       * doc/gnuastro.texi: Changed all 'gnuastro' occurrences in the
+       text to 'Gnuastro'.
 
 2015-03-14  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
@@ -845,8 +845,8 @@
 
 2015-02-13  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
-       * bin/convertt/args.h (argp_options): `convert` changed to
-       `change`. Since the program name is also convert this could cause
+       * bin/convertt/args.h (argp_options): 'convert' changed to
+       'change'. Since the program name is also convert this could cause
        confusions.
 
        * doc/gnuastro.texi (Invoking astconvertt): Option explanations.
@@ -890,8 +890,8 @@
 
 2015-01-25  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
-       * ./*: In all files, the name `AstrUtils` was changed to
-       `gnuastro` and the `astr` prefix was changed to `ast`.
+       * ./*: In all files, the name 'AstrUtils' was changed to
+       'gnuastro' and the 'astr' prefix was changed to 'ast'.
 
 2015-01-20  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
@@ -936,10 +936,10 @@
        * bin/mkprof/mkprof.c (writelog): Added.
        (write): Completed.
 
-       * bin/mkprof/args.h (arg_option): Added `--nomerged`.
+       * bin/mkprof/args.h (arg_option): Added '--nomerged'.
 
-       * lib/txtarrayvv.c (doformatting): Added option to use `f` in
-       printf or `g`.
+       * lib/txtarrayvv.c (doformatting): Added option to use 'f' in
+       printf or 'g'.
 
 2015-01-16  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
@@ -947,34 +947,34 @@
        (saveindividual): Added.
        (build): Is working until the profile is built.
 
-       * bin/mkprof/ellipse.h: Function taken to `box.h`.
+       * bin/mkprof/ellipse.h: Function taken to 'box.h'.
 
-       * bin/mkprof/ellipse.c: Function taken to `box.c`.
+       * bin/mkprof/ellipse.c: Function taken to 'box.c'.
 
-       * bin/mkprof/args.h (argp_option): added `--numrandom`, changed
-       `mginimg` to `psfinimg`.
+       * bin/mkprof/args.h (argp_option): added '--numrandom', changed
+       'mginimg' to 'psfinimg'.
 
        * bin/imgcrop/main.h (imgcropparams): iwidth is now a 2 element
        array.
 
-       * bin/imgcrop/crop.c: Removed `borderfromcenter` and
-       `correctflpixels` to `lib/box.c`.
+       * bin/imgcrop/crop.c: Removed 'borderfromcenter' and
+       'correctflpixels' to 'lib/box.c'.
 
-       * lib/stats.h: Changed to `statistics.h`, all non-used functions
+       * lib/stats.h: Changed to 'statistics.h', all non-used functions
        removed.
 
        * lib/arraymanip.c: Removed all non-used functions.
 
-       * include/stats.h: Changed to `statistics.h` and all non-used
+       * include/stats.h: Changed to 'statistics.h' and all non-used
        functions removed.
 
        * include/fixedstringmacros.h (ASTRUTILSBIBTEX): Added.
 
-       * include/commonargs.h (argp_option): Added `--cite` option and
-       added the `main.h` and `cite.h` header for each program to use
+       * include/commonargs.h (argp_option): Added '--cite' option and
+       added the 'main.h' and 'cite.h' header for each program to use
        their names.
 
-       * include/checkset.h: function name `nameiswritable` is changed to
+       * include/checkset.h: function name 'nameiswritable' is changed to
        dir0file1 which is much more descriptive.
 
        * include/arraymanip.h: Removed all the non-used functions from
@@ -1042,7 +1042,7 @@
        * bin/imgcrop/args.h (parse_opt): Removed the check below.
 
        * include/commonargs.h (cparse_opt): Added check for not calling
-       `--setdirconf` and `--setusrconf` together.
+       '--setdirconf' and '--setusrconf' together.
 
 2015-01-05  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
@@ -1064,8 +1064,8 @@
        * include/commonparams.h (commonparams): Similar to the change in
        commonargs.h.
 
-       * include/commonargs.h (argp_option): `--dirdefaults` and
-       `--userdefaults` changed to `--setdirconf` and `--setusrconf`.
+       * include/commonargs.h (argp_option): '--dirdefaults' and
+       '--userdefaults' changed to '--setdirconf' and '--setusrconf'.
 
        * doc/astrutils.texi: Included formath.texi to display math
        equations, updates to the text.
@@ -1094,7 +1094,7 @@
 
        * lib/defaults.c (addhomedir): Now returns char *.
        (writelocaldefaultstop): Informs the user of how to make the
-       needed directory with `make -p`.
+       needed directory with 'make -p'.
 
        * include/defaults.h (SAVE_LOCAL_DEFAULTS): changed addhomedir.
        (CHECKSETDEFAULTS): A new macro to read defaults.
@@ -1103,11 +1103,11 @@
        * doc/astrutils.texi: A lot of updates in the text! Listing them
        all would be too much!
 
-       * configure.ac: `--with-numthreads` and `--enable-progname` were
+       * configure.ac: '--with-numthreads' and '--enable-progname' were
        created along with all their necessary checks.
 
        * Makefile.am: Set conditional subdirectories. Added
-       `basicchecks.sh` to the files to be distributed.
+       'basicchecks.sh' to the files to be distributed.
 
 2014-12-28  Mohammad Akhlaghi  <akhlaghi@gnu.org>
 
@@ -1118,7 +1118,7 @@
 ;; coding: utf-8
 ;; End:
 
- Copyright (C) 2015-2019, Free Software Foundation, Inc.
+ Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
  This file is part of GNU Astronomy Utitlies (Gnuastro).
 
diff --git a/Makefile.am b/Makefile.am
index 49bda39..45b97af 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,7 +6,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -29,12 +29,12 @@
 ## ============================================
 ##
 ## These target(s) will be created before anything else when running
-## `make', `make check', or `make install', see the "Built sources" section
+## 'make', 'make check', or 'make install', see the "Built sources" section
 ## of the AUTOMAKE manual and the git-version-gen script comments for more
-## information. Note that AUTHORS depends on `$(top_srcdir)/.version', and
-## `$(top_srcdir)/.version' depends on $(top_srcdir)/configure'. So any
+## information. Note that AUTHORS depends on '$(top_srcdir)/.version', and
+## '$(top_srcdir)/.version' depends on $(top_srcdir)/configure'. So any
 ## time the version is updated, both of these will be re-built. But during
-## usual utility and library development, `$(top_srcdir)/configure' is not
+## usual utility and library development, '$(top_srcdir)/configure' is not
 ## commonly updated, so this will not slow down the process.
 BUILT_SOURCES = $(top_srcdir)/AUTHORS
 
@@ -83,6 +83,9 @@ endif
 if COND_NOISECHISEL
   MAYBE_NOISECHISEL = bin/noisechisel
 endif
+if COND_QUERY
+  MAYBE_QUERY = bin/query
+endif
 if COND_SEGMENT
   MAYBE_SEGMENT = bin/segment
 endif
@@ -107,18 +110,18 @@ endif
 ## Subdirectories to build
 ## =======================
 ##
-## Note that by default `COND_TEMPLATE' is not set in configure, it is
+## Note that by default 'COND_TEMPLATE' is not set in configure, it is
 ## commented, and exists only as a template for you to copy and paste to
-## name your new utility. The same rule is applied here (in the `if'
-## conditions above). When `MAYBE_TEMPLATE' is not defined, then Make will
+## name your new utility. The same rule is applied here (in the 'if'
+## conditions above). When 'MAYBE_TEMPLATE' is not defined, then Make will
 ## see it as a blank string and igonore it, so there is no problem with
-## 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_MATCH)            \
+## 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_MATCH) \
   $(MAYBE_MKCATALOG) $(MAYBE_MKNOISE) $(MAYBE_MKPROF) $(MAYBE_NOISECHISEL) \
-  $(MAYBE_SEGMENT) $(MAYBE_STATISTICS) $(MAYBE_TABLE) $(MAYBE_TEMPLATE)    \
-  $(MAYBE_WARP) bin/script doc tests
+  $(MAYBE_QUERY) $(MAYBE_SEGMENT) $(MAYBE_STATISTICS) $(MAYBE_TABLE) \
+  $(MAYBE_TEMPLATE) $(MAYBE_WARP) bin/script doc tests
 
 
 
@@ -143,9 +146,9 @@ dist_sysconf_DATA = bin/gnuastro.conf
 ## Files that are only distributed
 ## ===============================
 ##
-## Note that `COPYING' (containing the GNU GPL) is included in the
+## Note that 'COPYING' (containing the GNU GPL) is included in the
 ## distribution tarball by default in Automake, but not other license
-## files, so we have to manually add `COPYING.FDL'.
+## files, so we have to manually add 'COPYING.FDL'.
 EXTRA_DIST = COPYING.FDL genauthors .dir-locals.el .version \
              developer-build bootstrapped/README .autom4te.cfg
 
@@ -160,14 +163,27 @@ EXTRA_DIST = COPYING.FDL genauthors .dir-locals.el 
.version \
 ##
 ## When running ./configure, the user can opt-out of these messages using
 ## the GUIDEMESSAGE variable that is set when they run ./configure with the
-## `--distable-guide-message' option.
+## '--distable-guide-message' option.
 ##
-## Note that all-local is a prerequisite of `make check' too, so we will
-## only print its message when `make' was called with no options. Make will
+## Note that all-local is a prerequisite of 'make check' too, so we will
+## only print its message when 'make' was called with no options. Make will
 ## set MAKECMDGOALS to blank if there are no arguments, however, the way
 ## Automake works, its value is set to "all-am".
 all-local:
-       @if [ x$(MAKECMDGOALS) = x"all-am" ] && [ x$(GUIDEMESSAGE) = xyes ]; 
then    \
+
+        # If we are in static linking mode, correct the 'lib_dependencies'
+        # variable of 'libgnuastro.la'. Because by default it will not
+        # include static libraries!
+       @if [ "X$(MAKECMDGOALS)" = "Xall-am" ] && [ "X$(ENABLE_SHARED)" = "Xno" 
]; then \
+         $(AWK) '/^dependency_libs/{print 
"dependency_libs='\''$(CONFIG_LDADD)'\''"} \
+                 !/^dependency_libs/{print}' 
$(top_builddir)/lib/libgnuastro.la \
+                > $(top_builddir)/lib/libgnuastro_tmp.la; \
+         mv $(top_builddir)/lib/libgnuastro_tmp.la \
+            $(top_builddir)/lib/libgnuastro.la; \
+       fi
+
+        # Print a message if requested.
+       @if [ "X$(MAKECMDGOALS)" = "Xall-am" ] && [ x$(GUIDEMESSAGE) = xyes ]; 
then    \
         echo;                                                                  
     \
         echo 
"==================================================================="; \
         echo 
"==================================================================="; \
@@ -190,7 +206,7 @@ all-local:
 ##   and all the variables that are supposed to be based on exec_prefix by
 ##   default will actually change."
 ##
-## So actually, they can also do that after `make check'. Usually
+## So actually, they can also do that after 'make check'. Usually
 ## professional users would want to do such a thing, so they can ignore the
 ## NOTE. This note is mostly for beginners and it is not written to convey
 ## that this is the ONLY solution.
@@ -223,7 +239,7 @@ check-local:
         echo;                                                                  
     \
        fi
 
-## Note that the `\' characters in the GNU head here are not printed on the
+## Note that the '\' characters in the GNU head here are not printed on the
 ## command line. So we have to consider them. The ASCII GNU head is taken
 ## from: https://www.gnu.org/graphics/gnu-ascii.html
 install-exec-local:
@@ -274,18 +290,18 @@ install-exec-local:
 ## ======================
 ##
 ## This file is created from the $(VERSION) variable which was defined by
-## the `git-version-gen' script (located at address below), which is run
-## when the `$(top_srcdir)/configure' script is being built by Autoconf.
+## the 'git-version-gen' script (located at address below), which is run
+## when the '$(top_srcdir)/configure' script is being built by Autoconf.
 ##
 ##    $(top_srcdir)/bootstrapped/build-aux/git-version-gen
 ##
-## Note that contrary to what is proposed by `git-version-gen', here the
-## creation of `$(top_srcdir)/.version' depends on the
-## `$(top_srcdir)/configure' script. Therefore, anytime a the VERSION
-## variable is updated there, `$(top_srcdir)/.version' is also
+## Note that contrary to what is proposed by 'git-version-gen', here the
+## creation of '$(top_srcdir)/.version' depends on the
+## '$(top_srcdir)/configure' script. Therefore, anytime a the VERSION
+## variable is updated there, '$(top_srcdir)/.version' is also
 ## updated. During development, of the main functionality of Gnuastro
-## (utilities and libraries), the `$(top_srcdir/configure' script is rarely
-## updated, so `$(top_srcdir)/.version' will not be rebuilt and thus it
+## (utilities and libraries), the '$(top_srcdir/configure' script is rarely
+## updated, so '$(top_srcdir)/.version' will not be rebuilt and thus it
 ## won't harm the speed of tests during development.
 $(top_srcdir)/.version: $(top_srcdir)/configure
        echo $(VERSION) > $@-t && mv $@-t $@
@@ -300,18 +316,18 @@ $(top_srcdir)/.version: $(top_srcdir)/configure
 ## This file is generated automatically from the version controlled
 ## history. Note the following:
 ##
-##   - `$(top_srcdir)/AUTHORS' is defined as a BUILT_SOURCES variable, so
+##   - '$(top_srcdir)/AUTHORS' is defined as a BUILT_SOURCES variable, so
 ##     it is the first thing that is built (even in multi-threaded runs).
 ##
-##   - `$(top_srcdir)/AUTHORS' is updated only when
-##     `$(top_srcdir)/.version' is updated. `$(top_srcdir)/.version' its
-##     self is only updated when `$(top_srcdir)/configure' is updated. But
-##     generally, the `$(top_srcdir)/configure' script is not updated
+##   - '$(top_srcdir)/AUTHORS' is updated only when
+##     '$(top_srcdir)/.version' is updated. '$(top_srcdir)/.version' its
+##     self is only updated when '$(top_srcdir)/configure' is updated. But
+##     generally, the '$(top_srcdir)/configure' script is not updated
 ##     regularly during development and outside of version control.
 ##
-##   - The `$(top_srcdir)/genauthors' script will not do anything
-##     (make/update the `$(top_srcdir)AUTHORS' file) when there is no git
-##     repository. `$(top_srcdir)/AUTHORS' is only necessary when building
+##   - The '$(top_srcdir)/genauthors' script will not do anything
+##     (make/update the '$(top_srcdir)AUTHORS' file) when there is no git
+##     repository. '$(top_srcdir)/AUTHORS' is only necessary when building
 ##     a tarball distribution.
 $(top_srcdir)/AUTHORS: $(top_srcdir)/.version
        $(top_srcdir)/genauthors $(top_srcdir)
@@ -324,7 +340,7 @@ $(top_srcdir)/AUTHORS: $(top_srcdir)/.version
 ## ================================
 ##
 ## These targets will be created when building a (tarball)
-## distribution. Note that AUTHORS depends on `.version'.
+## distribution. Note that AUTHORS depends on '.version'.
 dist-hook: $(top_srcdir)/AUTHORS
        echo $(VERSION) > $(distdir)/.tarball-version
 
diff --git a/NEWS b/NEWS
index 53cce0e..a5af946 100644
--- a/NEWS
+++ b/NEWS
@@ -1,14 +1,655 @@
 GNU Astronomy Utilities NEWS                          -*- outline -*-
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
 * Noteworthy changes in release X.X (library X.X.X) (XXXX-XX-XX) [stable]
 
 ** New features
 
+  New program:
+   - astscript-make-ds9-reg: Given a table (either as a file or from
+     standard input), create an SAO DS9 region file from the requested
+     positional columns (WCS or image coordinates). For example with the
+     command below you can select certain rows of a given table, and show
+     them over an image:
+        asttable table.fits --range=MAG,18:20 --column=RA,DEC \
+            | astscript-make-ds9-reg --column=1,2 --radius=0.5 \
+                                     --command="ds9 image.fits"
+
+  Arithmetic:
+   - New operators (the trigonometric/hyperbolic functions were previously
+     only avaialble in Table's column arithmetic, but they have been moved
+     into the Gnuastro library and are thus now available on images within
+     Arithmetic also):
+     - sin: Trigonometric sine (input in degrees).
+     - cos: Trigonometric cosine (input in degrees).
+     - tan: Trigonometric tangent (input in degrees).
+     - asin: Inverse of trigonometric sine (output in degrees).
+     - acos: Inverse of trigonometric cosine (output in degrees).
+     - atab: Inverse of trigonometric tangent (output in degrees).
+     - sinh: Hyperbolic sine.
+     - cosh: Hyperbolic cosine.
+     - tanh: Hyperbolic tangent.
+     - asinh: Inverse of hyperbolic sine.
+     - acosh: Inverse of hyperbolic cosine.
+     - atabh: Inverse of hyperbolic tangent.
+
+  Table:
+   - When given a value of '_all', the '--noblank' option (that will remove
+     all rows with a blank value in the given columns) will check all
+     columns of the final output table. This is handy when you want a
+     "clean" (no NaN values in any column) table, but the table has many
+     columns.
+   --rowlimit: new option to specify the positional interval of rows to
+     show. Until now the '--head' or '--tail' options would just allow
+     seeing the first or last few rows. You can use this to view a
+     contiguous set of rows in the middle of the table.
+   --rowrandom: Make a random selection of the rows. This option is useful
+     when you have a large dataset and just want to see a random sub-set of
+     the rows. It takes an integer, selects that many rows from the input
+     randomly.
+   - New column arithmetic operators:
+     - 'set-AAA' operator (which allows storing the popped operand into a
+       named variable for easy usage in complex operations) is also usable
+       in Table's column arithmetic. Until now this operator was only
+       available in the Arithmetic program (for operation on images).
+     - 'date-to-sec' Convert FITS date format ('YYYY-MM-DDThh:mm:ss') into
+       seconds from the Unix epoch (1970-01-01,00:00:00 UTC). This can be
+       very useful in combination with the new '--keyvalue' option of the
+       Fits program to sort your FITS images based on observation time.
+
+  Fits:
+   --keyvalue: Print only the values of the FITS keywords given to this
+     option in separate columns. This option can take multiple values and
+     many FITS files. Thus generating a table of keyword values (with one
+     row per file). Its output can thus either be piped to the Table
+     program for selecting a certain sub-set of your FITS files, or sorting
+     them for example.
+
+  Library:
+   - New arithmetic operator macros (for the 'gal_arithmetic' function):
+     - GAL_ARITHMETIC_OP_SIN: sine (input in deg).
+     - GAL_ARITHMETIC_OP_COS: cosine (input in deg).
+     - GAL_ARITHMETIC_OP_TAN: tangent (input in deg).
+     - GAL_ARITHMETIC_OP_ASIN: Inverse sine (output in deg).
+     - GAL_ARITHMETIC_OP_ACOS: Inverse cosine (output in deg).
+     - GAL_ARITHMETIC_OP_ATAN: Inverse tangent (output in deg)
+     - GAL_ARITHMETIC_OP_ATAN2: Inverse tangent (with two inputs, out deg).
+     - GAL_ARITHMETIC_OP_SINH: Hyperbolic sine.
+     - GAL_ARITHMETIC_OP_COSH: Hyperbolic cosine.
+     - GAL_ARITHMETIC_OP_TANH: Hyperbolic tangent.
+     - GAL_ARITHMETIC_OP_ASINH: Inverse hyperbolic sine.
+     - GAL_ARITHMETIC_OP_ACOSH: Inverse hyperbolic cosine.
+     - GAL_ARITHMETIC_OP_ATANH: Inverse hyperbolic tangent.
+
+** Removed features
+
+** Changed features
+
+  astscript-sort-by-night:
+   - Thanks to the new features in the Fits and Table programs (described
+     above), the efficiency of this script has improved dramatically (from
+     19 seconds to 0.42 seconds for about 650 FITS files used in the
+     test!).
+   - The default end to a "night" is set to 11:00a.m. Until now it was
+     9:00a.m. But in some cases, calibration images may be taken after
+     that. So to be safer in general it was incremented by 2 hours.
+
+  Library:
+   - gal_fits_key_write_wcsstr: also takes WCS structure as argument.
+   - gal_fits_key_read_from_ptr: providing a numerical datatype for the
+     desired keyword's value is no longer mandatory. When not given, the
+     smallest numeric datatype that can keep the value will be found and
+     used.
+
+** Bugs fixed
+  bug #60082: Arithmetic library crash for integer operators like modulo
+
+
+
+
+
+* Noteworthy changes in release 0.14 (library 12.0.0) (2021-01-25) [stable]
+
+** New features
+
+  Book:
+   - Tutorial on "Detecting large extended targets" improved with better
+     NoiseChisel configuration, and more clear description.
+   - New sub-section on "Memory management" in the "Common program
+     behavior" chapter. It fully describes how to optimally deal with large
+     datasets that may exceed your system's RAM.
+   - Examples and better description added to many operators in the
+     "Arithmetic operators" subsection of the Arithmetic program's section.
+
+  New program:
+   - Query ('astquery') is a new program to query to external datasets and
+     retrieve the resulting datasets from the command-line. It is possible
+     to get list of datasets or column names from the databases. The basic
+     spatial query (finding objects in the vicinity of a certain point) can
+     be managed without knowing the query language of the database: with
+     basic options (like '--center' with '--radius' or '--width'), or an
+     input image that has WCS. Currently Query supports VizieR (containing
+     +20500 datasets, making it the largest database in astronomy), NED, as
+     ESA's Gaia database and ASTRON. See the new "Query" section of the
+     book (under the "Data containers" chapter) for more.
+
+  All programs:
+   - Plain text table inputs can have floating point columns that are in
+     sexagesimal format of '_h_m_s' or '_d_m_s' (where '_' is a
+     number). These are the classical format to respectively represent
+     Right Ascension (RA) and Declination (Dec). They will be directly read
+     as a single floating point number (in units of degrees) into
+     memory. Therefore the same column of a plain-text table, can be
+     degrees in some rows and sexagesimal in others. Besides large tables,
+     with this feature, conversion to sexagesimal coordinates to degrees
+     becomes very easy, for example:
+         echo "7h34m35.5498 31d53m14.352s" | asttable
+     Recall that the inverse can also be done with the more general column
+     arithmetic:
+         echo "113.64812416667 31.88732" \
+              | asttable -c'arith $1 degree-to-ra $2 degree-to-dec'
+   - If input is a HEALpix grid (1D table column that represents the 2D
+     spherical representation of datasets), the programs will print a
+     warning, suggesting to use the 'HPXcvt' utility of WCSLIB.
+
+  Arithmetic:
+   - New operators:
+     - 'interpolate-maxofregion': interpolate connected blank regions with
+       the maximum value that is immediately touching it. This can be used
+       to fill the blank centers of saturated stars for example.
+     - 'interpolate-minofregion': similar to 'interpolate-maxofregion', but
+       for the minimum.
+     - 'makenew': new operator to create an empty (zero-valued) new dataset
+       with given dimension and size (given as operands).
+
+  Crop:
+   --primaryimghdu: Write the final cropped image into the primary (or
+     0-th) extension of the output FITS file, so the output only has
+     one extension.
+
+  Fits:
+   - New '--skycoverage' option will report the area of the input image in
+     RA/Dec, both in units of center/width, and box minimum/maximum
+     format. This is paritcularly useful in combination with the new
+     'astquery' option, to easily search the contents of external databases
+     within the image.
+
+  MakeCatalog:
+   --maximum: maximum value of labeled regions pixels (clump/object).
+   --areaarcsec2: area of labeled region (clump/object) in arcsec^2.
+   --surfacebrightness: the surface brightness of the labeled region.
+   --fwhm: observed FWHM in pixels (non-parametric), along the major axis.
+   --halfmaxarea: number of pixels with a value larger than half the maximum.
+   --halfmaxradius: radius of region that is larger than half the maximum.
+   --halfmaxsum: sum of pixels with a value larger than half the maximum.
+   --halfmaxsb: surf. brightness within half of the maximum.
+   --fracmax: fractions to use in '--fracmaxarea1' or '--fracmaxarea2'.
+   --fracmaxsum1: sum of pixels brighter than first given fraction of max.
+   --fracmaxsum2: sum of pixels brighter than second given fraction of max.
+   --fracmaxarea1: number of pixels brighter than first given fraction of max.
+   --fracmaxarea2: number of pixels brighter than second given fraction of max.
+   --fracmaxradius1: radius derived from '--fracmaxarea1'.
+   --fracmaxradius2: radius derived from '--fracmaxarea2'.
+   --halfsumsb: Surface brightness within area reported by '--halfsumarea'.
+   --halfsumarea: area containing half of the summed object or clump values.
+   --halfsumradius: radius derived from '--halfsumarea', underestimates r_e.
+   --areaminv: the number of pixels that are equal to the minimum value.
+   --areamaxv: the number of pixels that are equal to the maximum value.
+   - New columns to return the position of pixel with minimum or maximum
+     value: '--minvx', '--maxvx', '--minvy', '--maxvy', '--minvz',
+     '--maxvz'.
+
+  MakeNoise:
+   --bgisbrightness: new option to say that the value of '--background'
+     (used to simulate Poisson noise) should be interpreted as brightness,
+     not magnitude.
+
+  MakeProfiles:
+   - It is now possible to make any custom radial profile with the 'custom'
+     profile (with code '8'). A table should be given to the new
+     '--customtable' option which will define each radial interval and the
+     value to use for that radial interval. See the description of
+     '--customtable' for more.
+
+  Statistics:
+   - 2D histograms can now be built as a FITS image with a linear WCS that
+     contains axis values and box size. This allows using the power of FITS
+     viewers for plotting/inspecting distributions of two columns in a
+     table relative to each other (for example color-magnitude plots). You
+     can also convert these 2D histogram images to PDF or JPEG with
+     Gnuastro's ConvertType to directly use in your papers/reports (see the
+     newly added "2D histogram as an image" section of the book for more).
+
+  Table:
+   - New '--noblank' option will remove all rows in output table that have
+     at least one blank value in the specified columns. For example if
+     'table.fits' has blank values (NaN in floating point types) in the
+     'magnitude' and 'sn' columns, with '--noblank=magnitude,sn', you can
+     be sure that all rows with blank values in these columns have been
+     removed.
+   - New trigonometric operators for column-arithmetic (inputs in units of
+     degrees): 'sin', 'cos' and 'tan'. Their inverse trigonometric (outputs
+     in units of degrees) have also been added: 'asin', 'acos' and 'atan'.
+     The 'atan2' operator (inverse tangent that preserves the quadrant, see
+     its description in the book) is also now available.
+   - New hyperbolic operators for column-arithmetic: 'sinh', 'cosh' and
+     'tanh'. Their inverse has also been added: 'asinh', 'acosh' and
+     'atanh'.
+
+  Library:
+   - GAL_ARITHMETIC_OP_MAKENEW: new 'makenew' operator.
+   - gal_binary_connected_adjacency_list: finding connected components
+     without a square adjacency matrix (which can consume major RAM).
+   - gal_blank_flag_remove: Remove all flagged elements in a dataset.
+   - gal_blank_remove_rows: remove all rows that have at least one blank.
+   - gal_dimension_dist_elliptical: Elliptical dist. of a point from center.
+   - gal_fits_hdu_is_healpix: Return 1 if HDU is a HEALpix grid.
+   - gal_fits_hdu_datasum: calculate DATASUM of given HDU in given FITS file.
+   - gal_fits_hdu_datasum_ptr: calculate DATASUM of opened FITS file pointer.
+   - gal_fits_key_list_comment_add: add a COMMENT keyword to the keyword list.
+   - gal_fits_key_list_comment_add_end: add a COMMENT keyword to end of list.
+   - gal_pointer_allocate_ram_or_mmap: allocate in RAM or memory-mapped file.
+   - gal_pointer_mmap_free: "free" (actually delete) the memory-mapped file.
+   - gal_wcs_create: create WCSLIB-compatible WCS from raw values.
+   - gal_wcs_coverage: Return the sky coverage of given image HDU.
+   - gal_wcs_dimension_name: return the name of the requested WCS dim.
+
+** Removed features
+
+** Changed features
+
+  All programs:
+   - Memory management: Until now, an internal array was only allocated in
+     the RAM when its size was smaller (in bytes) than the value given to
+     the '--minmapsize' option. But this was annoying/buggy when the system
+     has enough RAM to keep large files. From this version, all Gnuastro
+     programs will first attempt to write the array in RAM, only when it
+     fails (there is no more RAM left), will they use a memory-mapped file
+     (which can dramatically slow down the program). Please see the newly
+     added "Memory management" section of the book for a complete
+     explanation of Gnuastro's new memory management strategy.
+   - When an array needs to be memory-mapped (read into a file on HDD/SSD,
+     not RAM, usually due to RAM limitations), it is written in a
+     'gnuastro_mmap' directory of the running directory, not the hidden
+     '.gnuastro_mmap' directory. Since the files in this directory are
+     usually large, it is better that the user sees them by default (in
+     case the program crashes and they aren't deleted).
+   --interpnumngb: the default value has been increased to 15 (from 9). The
+     reason for this is that we now have a more robust outlier removal
+     algorithm (see description under "NoiseChisel & Statistics").
+
+  Crop:
+   - When cropping a single image in WCS mode, there is no longer any
+     limitation on the WCS. Until now for all WCS mode crops, it was
+     necessary for the WCS to be aligned to the celestial coordinates. But
+     from this version, this is only necessary when cropping from many
+     files (and stitching them together where necessary). For WCS-mode
+     crops of a single image, any WCS that is recognized by WCSLIB is fine.
+
+  Fits:
+   - The '--pixelscale' option also prints the pixel area (for 2D inputs,
+     or 2D slices of 3D inputs) and the voxel volume (for 3D inputs). Until
+     now, it would only print the pixel scale along each dimension.
+   - When printing FITS file HDU information (no options given), a new
+     "Comments" column may be printed for each HDU in the end of the line.
+     It will be printed if special situations are found (for example a 2D
+     HEALPix grid, that is usually stored as a 1D array/column).
+
+  NoiseChisel & Statistics:
+   - New algorithm used to reject outlying tiles. In NoiseChisel this is
+     done when estimating the quantile threshold, the pseudo-detection
+     threshold and the final Sky value. In Statistics, its just the Sky
+     value. Unlike the previous method that used the global distribution of
+     tile values to identify outliers, the new algorithm uses a relative
+     measure. See the book for more. Since outliers are now rejected more
+     robustly, the default value of '--meanmedqdiff' has been increased to
+     0.01 (was 0.005) and '--outliersigma' has been decreased to '5' (was
+     10). Also 'smoothwidth' has been increased from '3' to '5' to have
+     smoother tessellation values.
+
+  Statistics:
+   - The '--histogram2d' now takes a string argument: either 'image' or
+     'table'. For the old behavior please use '--histogram2d=table'. See
+     the new features above for the 'image' mode.
+
+  Table:
+   - Column arithmetic operators 'degree-to-ra' and 'degree-to-dec' will
+     return the sexagesimal format of '_h_m_s' and '_d_m_s'
+     respectively. Until this version, they would both use colons as
+     delimiters ('_:_:_').
+
+  Library:
+   - gal_pointer_mmap_allocate: new name for 'gal_pointer_allocate_mmap'.
+   - gal_threads_dist_in_threads: now accounts for billions of threads,
+     thus includes memory management options.
+   - gal_threads_spin_off: now accounts for memory management.
+   - gal_units_degree_to_ra: new 'usecolon' argument to optionally format
+     output string with colons as delimiters ('_:_:_'). When this option is
+     zero, the string will be in the '_h_m_s' format.
+   - gal_units_degree_to_dec: similar to 'gal_units_degree_to_ra', but when
+     'usecolon' is zero, the string will be in the '_d_m_s' format.
+
+** Bugs fixed
+  bug #59017: Segment's object IDs are not thread-safe (i.e., reproducible).
+  bug #59105: Column arithmetic operator degree-to-ra, returning to dec.
+  bug #59136: Makeprofiles with --replace is not thread-safe.
+  bug #59155: Match cannot find the proper row when coordinates have NaN.
+  bug #59371: MakeCatalog crash with clumps on non-contiguous object labels.
+  bug #59400: CosmicCalculator fails --printparams when redshift isn't given.
+  bug #59459: Unclear WCS when both PC and CD exist in input, but conflict.
+  bug #59625: MakeProfiles uses last --kernel, if it is called more than once.
+  bug #59700: Segment's excessive RAM usage when many clumps over a detection.
+  bug #59765: MakeCatalog crash when to-be-subtracted Sky is a single-element
+              per tile tessellation (e.g., NoiseChisel's '--oneelempertile').
+
+
+
+
+
+* Noteworthy changes in release 0.13 (library 11.0.0) (2020-09-06) [stable]
+
+** New features
+
+  All programs:
+   - When reading plain-text tables, the blank value for numeric columns
+     can be any string (specified in the special comment-line format
+     described in the "Gnuastro text table format" of the manual). Until
+     now, it had to be a number in the same type.
+
   Arithmetic:
-   - The new `add-dimension' operator will stack the popped operands in a
+   - New operators:
+     - interpolate-minngb: Fill blanks with minimum of nearest neighbors.
+     - interpolate-maxngb: Fill blanks with maximum of nearest neighbors.
+          This can be useful to fill the blank values of saturated stars
+          for example.
+   - To force integers to floats, you can also put a '.' or '.0' after
+     them. Until now, it was only possibly by putting an 'f' after
+     them. Hence while '5' will be read as an integer, '5.', '5.0' or '5f'
+     will be read as floating point. This also applies to column arithmetic
+     in Table.
+
+  ConvertType:
+   - New colormap: 'sls-inverse' is the inverse of the SLS color, good for
+     printing because of a white background.
+
+  CosmicCalculator:
+   --velocity: Velocity (in km/s) to use instead of input redshift.
+   --usedvelocity: Print the velocity (in km/s) at input redshift.
+   --listlinesatz: Print the wavelength of all pre-defined spectral lines
+     at given redshift as a simple table with the line names. This is very
+     convenient and can be used in conjunction with '--obsline' for example
+     to print the observed wavelength of all lines when Lyman-alpha is at
+     4000 Angstroms with this simple command:
+        astcosmiccal --obsline=lyalpha,4000 --listlinesatz
+
+  FITS:
+   - New '--pixelscale' option will return the size of pixels in each
+     dimension in the "world coordinates".
+   - New '--wcsdistortion' option allows conversion between the various WCS
+     distortions. For example if you have a FITS image with the TPV-based
+     WCS distortion, and you would like to convert it to a SIP-based
+     distortion, you can simply run 'astfits --wcsdistortion=SIP' on the
+     file. The inverse conversion is also supported (from SIP to TPV).
+
+  Statistics
+   - New feature to create a 2D-histogram using two input columns (useful
+     when you have lots of points that are too dense and may hide important
+     features). This mode can be activated with the new '--histogram2d'
+     option. The binning of the first (X axis) column is specified with the
+     same 1D histogram options. The second column's binning is configured
+     with the following options:
+       --numbins2: Number of bins along the second column.
+       --greaterequal2: Only second column points that are larger than this.
+       --lessthan2; Only second column points that are less than this.
+       --onebinstart2: Make sure one bin starts at the value given here.
+
+  Table:
+   - New '--catcolumns' to specify which columns to concatenate (or append)
+     to the output. You can specify the file name containing the columns to
+     append with the '--catcolumnfile' option and '--catcolumnhdu' (see
+     changed features because until now they had different names).
+   - New '--catcolumnsrawname' will leave the name of concatenated
+     (appended) columns unchanged. By default the names of the appended
+     columns will be appended with a '-N' (where 'N' is a counter for the
+     file that is used to append columns). The default behavior is to avoid
+     multiple columns having the same name.
+   - New '--colmetadata' option to add/update column metadata (name, units
+     or comments) just before writing the output. This is a very useful
+     feature in combination with column arithmetic or column concatenation
+     because it will allow you to update the new column metadata in the
+     same command. See the manual for more.
+
+  Library:
+   - Spectral lines library: SiIII, OIII, CIV, NV and rest of Lyman series.
+   - GAL_CONFIG_HAVE_WCSLIB_DIS_H: if the host's WCSLIB supports distortions.
+   - GAL_CONFIG_HAVE_WCSLIB_MJDREF: if the host's WCSLIB reads MJDREF keyword.
+   - gal_cosmology_velocity_from_z: Calculate velocity from redshift.
+   - gal_cosmology_z_from_velocity: Calculate redshift from velocity.
+   - gal_data_array_ptr_calloc: Allocate array of pointers to gal_data_t
+   - gal_data_array_ptr_free: Free all the datasets within the array and 
itself.
+   - gal_fits_key_list_title_add: Add a title key word to the list.
+   - gal_fits_key_list_title_add_end: Add a title key word to the list's end.
+   - GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL: Radial metric for interpolation.
+   - GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN: Mahattan distance.
+   - GAL_INTERPOLATE_NEIGHBORS_METRIC_INVALID: For error-handling/completeness.
+   - GAL_INTERPOLATE_NEIGHBORS_FUNC_MIN: Use minimum for interpolation.
+   - GAL_INTERPOLATE_NEIGHBORS_FUNC_MAX: Use maximum for interpolation.
+   - GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN: Use median for interpolation.
+   - GAL_INTERPOLATE_NEIGHBORS_FUNC_INVALID: for error-handling/completeness.
+   - gal_kdtree_create: Create a k-d tree for optimal spatial searching.
+   - gal_kdtree_nearest_neighbour: Find the nearest neighbor using a k-d tree.
+   - gal_statistics_histogram2d: Generate 2D histogram from two columns.
+   - GAL_WCS_FLTERROR: Limit to identify a floating point error for WCS.
+   - gal_wcs_write: Write the given WCS into a FITS extension with no data.
+   - gal_wcs_clean_errors: clean major WCS components from errors specified
+       by the FITS keyword 'CRDER', or floating point errors.
+   - gal_wcs_distortion_from_string: Return distortion string/name from ID.
+   - gal_wcs_distortion_to_string: Return distortion ID from string/name.
+   - gal_wcs_distortion_identify: Identify the distortion of given WCS.
+   - gal_wcs_distortion_convert: Convert between various WCS distortions.
+
+** Removed features
+
+** Changed features
+
+  Arithmetic:
+   - The 'pow' operator can also accept integer inputs. This also applies
+     to column arithmetic in Table.
+
+  MakeProfiles:
+   - The status of every created profile (along with the number of
+     remaining profiles) is no longer printed when there are more than 50
+     profiles. This is done because printing itself can slow down the
+     program an in a general/automated script this info is redundant.
+
+  Table:
+   --catcolumnfile ('-L') is new name for '--catcolumn' ('-C').
+   --catcolumnhdu is new name for '--catcolhdu' (short option name hasn't
+     changed).
+
+  Library:
+   - gal_fits_key_list_add: new 'ufree' argument to optionally free units.
+   - gal_fits_key_list_add_end: similar to 'gal_fits_key_list_add'.
+   - gal_interpolate_neighbors: new name for gal_interpolate_close_neighbors.
+   - gal_statistics_outlier_bydistance: new name for the old
+     'gal_statistics_outlier_positive'. It can now use the same algorithm
+     for negative outliers with a new argument.
+   - gal_txt_write: Now accepts a new argument for keyword lists.
+   - gal_type_string_to_number: Numbers ending in '.' or '.0' will be
+     parsed as floating point. Until now, it would only parse numbers as
+     floating point if they had non-zero decimals.
+
+** Bugs fixed
+  bug #58434: MakeCatalog crash when ordering is required and no usable pixels.
+  bug #58455: Timezone is not portable and uses flag instead of seconds.
+  bug #58696: Warp with --centeroncorner --scale making wrong size.
+  bug #58774: Warp' s output on a cube is a 2D image or wrong size.
+  bug #58809: NoiseChisel not removing negative outlier tiles.
+  bug #58833: Segment crashes when detection map has blank pixels.
+  bug #58835: Floating point errors when comparing pixel scale in Crop.
+  bug #58898: Plain text string columns touching next, clear first character.
+  bug #58901: Blank values for non-standard integer types in FITS tables.
+  bug #58974: WCS conversion not reasonable on processed TPV data.
+  bug #59019: FITS Table crash when TFORM comes before TNULL.
+
+
+
+
+
+* Noteworthy changes in release 0.12 (library 10.0.0) (2020-05-20) [stable]
+
+** New features
+
+  Arithmetic:
+   - New 'quantile' operator for coadding datasets.
+   - New 'size' operator to report length of dataset in requested dimension.
+   - When '--wcsfile' is given the value 'none', output will not have any WCS.
+
+  CosmicCalculator:
+   --listlines: list the pre-defined spectral line wavelengths and names
+     (which you can use with the '--obsline' and '--lineatz' options). This
+     is convenient when you forget the specific name of the spectral line
+     used within Gnuastro.
+
+  Crop:
+   --polygon: can now also crop concave polygons (when atleast one inner
+     angle is more than 180 degrees). Concave polygons occur a lot in deep
+     astronomical imaging: in the shape of the deepest regions.
+   --polygonsort: Sort the given set of vertices to the '--polygon'
+     option. For a concave polyton, the sorting will be correct, but for a
+     convex polygon, there is no unique solution/sorting, so it may not be
+     what you expect, see the manual.
+
+  Fits:
+   --datasum: Calculate and print the given HDU's "datasum" to stdout.
+   --datetosec: Can also account for 'Z' in the end of the date-time
+     string. According to 'https://www.w3.org/TR/NOTE-datetime', a 'Z'
+     effectively means no time zone, or UTC time (which is the default in
+     FITS). It still doesn't account for time zone hours of the w3.org
+     standard.
+
+  MakeCatalog:
+   --sigmaclip: define sigma-clipping parameters for the new '--sigclip-*'
+     columns.
+   --forcereadstd: Read the standard deviation image even if not needed by
+     any columns. This is useful when you want the surface brightness
+     limit, but don't need any error-related columns.
+   New output columns:
+     --sigclip-number: Number of sigma-clipped pixels in object/clump.
+     --sigclip-median: Sigma-clipped median of pixels in object/clump.
+     --sigclip-mean: Sigma-clipped mean of pixels in object/clump.
+     --sigclip-std: Sigma-clipped standard deviation of pixels in object/clump.
+
+  Table:
+   --equal: Can now work on columns with string type also.
+   --notequal: Can now work on columns with string type also.
+   --polygon: Polygon to use in '--inpolygon' or '--outpolygon'.
+   --inpolygon: Select rows that are inside the polygon of '--polygon'.
+   --outpolygon: Select rows that are outside the polygon of '--polygon'.
+   --catcolumn: Concatenate tables by column (keeping number of rows fixed).
+   --catcolhdu: Specify the HDU/extension of the FITS files of --catcolumn.
+   - New operators in column arithmetic:
+     - 'ra-to-degree': Convert Right Ascension (HH:MM:SS) to degrees.
+     - 'dec-to-degree': Convert Declination (DD:MM:SS) to degrees.
+     - 'degree-to-ra': Convert degrees to Right Ascension (HH:MM:SS).
+     - 'degree-to-dec': Convert degrees to Declination (HH:MM:SS).
+     - 'distance-flat': Distance between two points, assuming flat space.
+
+  Library:
+   - GAL_SPECLINES_INVALID_MAX: Total number of spectral lines, plus 1.
+   - GAL_ARITHMETIC_OP_QUANTILE: operator for 'gal_arithmetic'.
+   - gal_txt_trim_space: trim white space before and after a string.
+   - gal_polygon_is_convex: identify if a polygon is convex or concave.
+   - gal_polygon_is_inside: if point is inside polygon (convex or concave).
+   - gal_polygon_is_counterclockwise: check if polygon is counter-clockwise.
+   - gal_polygon_to_counterclockwise: convert to counter-clockwise if it isn't.
+   - gal_polygon_vertices_sort: un-ordered vertices to concave/convext 
polygons.
+   - gal_units_extract_decimal: Extract numbers from strings like "A:B:C".
+   - gal_units_ra_to_degree: Convert RA (HH:MM:SS) to degrees.
+   - gal_units_dec_to_degree: Convert Dec (DD:MM:SS) to degrees.
+   - gal_units_degree_to_ra: Convert degrees to RA (DD:MM:SS).
+   - gal_units_degree_to_dec: Convert degrees to Dec (DD:MM:SS).
+
+** Removed features
+
+** Changed features
+
+  All programs and libraries:
+   --minmapsize: Gnuastro's programs no longer attempt to write
+     memory-mapped files under '.gnuastro'. They will only attempt to write
+     them under the '.gnuastro_mmap' directory. Until now, when an internal
+     array needed to be memory-mapped, Gnuastro's programs (through the
+     'pointer.h' library) would first try writing the mmap files in the
+     '.gnuastro' directory. When it failed it would attempt writing in the
+     '.gnuastro_mmap' directory. However, '.gnuastro' is also used to store
+     configuration files (which are hand-written and thus valuable). Mixing
+     the two types of source (configuration files) and automatically
+     generated (memory-mapped) files is very problematic.
+   - FITS ASCII tables: When a column has a floating point type, but its
+     ASCII string can't be parsed as a number, it will be read as a
+     NaN. Until now, the corresponding program/library would abort,
+     printing the problematic string and its location.
+
+  Crop:
+   --polygon: by default it will no longer attempt to sort the polygon
+     vertices, sorting can be requested with the new '--polygonsort' option.
+   --polygonout: is the new name for '--outpolygon'. Having 'polygon' at
+     the start of the option name, makes it easier to find in the help list
+     and also to understand generally.
+
+  MakeCatalog:
+   - Until now, if no standard deviation image was requested, MakeCatalog
+     wouldn't include any surface brightness limit metadata in its
+     output. Now, those two lines are filled, but with a notice on the
+     cause (that there is no standard deviation image), and suggesting
+     solutions.
+
+  NoiseChisel:
+   - Until now, when NoiseChisel didn't detect any pixels, it just printed
+     a message and wouldn't not make any output file. This was very
+     inconvenient in general scripts. From now on, in this scenario, an
+     output file will be created and the detection map will only have a
+     value of zero. As a result, the Sky and Sky standard deviation
+     extensions will be measured over all the tiles.
+
+  Table:
+   - In Column arithmetic, when columns must be specified by their number,
+     that number should be distinguished with a '$' before it (for example
+     '$1' means the first column). Until now, this character was 'c', but
+     the new identifying character is very similar to AWK, allowing easier
+     adoption and is also more clear. It is just important to put the total
+     'arith' string within single quotes, not double quotes.
+   - Operators:
+     - 'distance-on-sphere': New name for old `angular-distance' operator.
+
+  Library:
+   - gal_polygon_is_inside_convex: new name for 'gal_polygon_pin'.
+
+** Bugs fixed
+  bug #57300: MakeCatalog memory crash when input dataset has units.
+  bug #57301: MakeCatalog using river sum instead of mean times by clump area.
+  bug #57921: Arithmetic's interpolation operator not reading metric.
+  bug #57989: Warp not accounting for translation in pixel grid.
+  bug #57995: Fits lib's date to second function affected by host's timezone.
+  bug #58315: Some NaNs with sigma-clip operators in Arithmetic and one input.
+  bug #58371: Table crashes with a commented newline in the columns.
+
+
+
+
+
+* Noteworthy changes in release 0.11 (library 9.0.0) (2019-11-25) [stable]
+
+** New features
+
+  Book:
+   - The "General program usage tutorial" now has a section on how to write
+     scripts effectively to automate your analysis.
+
+  Arithmetic:
+   - The new 'add-dimension' operator will stack the popped operands in a
      higher-dimensional dataset. For example to build a 3D cube from
      individual 2D images/slices.
    --onedonstdout: when the output is one-dimensional, print the values on
@@ -17,12 +658,12 @@ See the end of the file for license conditions.
   BuildProgram:
    - Will use common environment variables like LDFLAGS, CPPFLAGS and CC to
      help in customizing the build of your program.
-   --cc: custom C compiler to use. Until now, `gcc' was hard-coded into the
+   --cc: custom C compiler to use. Until now, 'gcc' was hard-coded into the
      source and there was no way to choose a custom C compiler.
    --noenv: With this option, no environment variables will be read.
 
   ConvertType:
-   - New `viridis' colormap (value for the `--colormap' option). This is
+   - New 'viridis' colormap (value for the '--colormap' option). This is
      the default colormap of Python's Matplotlib, and is available in many
      other plotting tools like LaTeX's PGFPlots.
 
@@ -49,8 +690,17 @@ See the end of the file for license conditions.
      --geow3: Geometric center in third WCS axis.
      --clumpsw3: Flux wheighted center of all clumps in 3rd dim.
      --clumpsgeow3: Geometric center of all clumps in 3rd dim.
-     --areaxy: Projected area in first two dimentions.
+     --areaxy: Projected area in first two dimensions.
      --geoareaxy: Projected geoarea in first two dimensions.
+   --inbetweenints: output will contain one row for all integers between 1
+     and the largest label in the input (irrespective of their existance in
+     the input image). This was the default/only behavior of MakeCatalog
+     until now. However, there are situations where the labeled input image
+     integers may not be contiguous. For example if the input's only
+     labeled pixel values are 11 and 13 from this release MakeCatalog's
+     output will only have two rows. If you want the old behavior (of one
+     row per integer, even if its not in the image), you can use this
+     option.
 
   MakeProfiles:
    - Can produce mock ellipsoids in a datacube (using X-Z-X Euler angles
@@ -59,13 +709,28 @@ See the end of the file for license conditions.
      --p2col: Second Euler angle (X-Z-X order).
      --p3col: Third Euler angle (X-Z-X order).
      --q2col: Axis ratio (major/dim3 in 3D).
-   - The `--kernel' option can build 3D kernels, see the description of
+   - The '--kernel' option can build 3D kernels, see the description of
      this option in the book for examples and details on how to run it.
 
   Match:
    - Matching of catalogs now possible using 3 coordinates (on catalogs
      generated from 3D data cubes), see book for more.
 
+  NoiseChisel:
+   - arXiv:1909.11230 added in papers to cite (with the '--cite' option):
+     this paper describes the major changes made to NoiseChisel in the last
+     10 stable releases since the 2015 paper, most importantly how Segment
+     has been separated and the new growth strategy. It is therefore
+     necessary to cite it along with the initial 2015 paper when using
+     NoiseChisel.
+
+  Segment:
+   - arXiv:1909.11230 added in papers to cite (with the '--cite' option):
+     this paper describes why Segment has been separated from NoiseChisel
+     and some important updates to it compared to the 2015 paper, it is
+     therefore necessary to cite it along with that paper when using
+     Segment.
+
   Statistics:
    --contour: compute a contour plot which can be directly fed into the
      PGFPlots package of LaTeX for plotting the contours. Support for more
@@ -73,20 +738,22 @@ See the end of the file for license conditions.
 
   Table:
    --equal: Output only rows that have a value equal to the given value in
-     the given column. For example `--equal=ID,2,4,5' will select only rows
-     that have a value of 2, 4 and 5 in the `ID' column.
+     the given column. For example '--equal=ID,2,4,5' will select only rows
+     that have a value of 2, 4 and 5 in the 'ID' column.
    --notequal: Output only rows that have a different value compared to the
      values given to this option in the given column.
    - Column Arithmetic operators:
-     - `angular-distance': a new operator to easily find the angular
+     - 'angular-distance': a new operator to easily find the angular
        distance (along a great circle) between points in various table
        columns, or the distances of all the points in the table rows with a
        fixed point. See the book for examples and better explanation.
 
   Library:
    - gal_binary_connected_indexs: store indexs of connected components.
+   - gal_blank_remove_realloc: Remove blanks and shrink allocated space.
    - gal_box_bound_ellipsoid_extent: Extent of 3D ellipsoid.
    - gal_box_bound_ellipsoid: Bounding box for a 3D ellipsoid.
+   - gal_statistics_unique: Return unique (non-blank) elements of the input.
 
 ** Removed features
 
@@ -99,6 +766,10 @@ See the end of the file for license conditions.
   bug #56999: Compilation error on macOS 10.9 not recognizing AT_FDCWD.
   bug #57057: BuildProgram not using environment LDFLAGS or CPPFLAGS.
   bug #57101: Crop segmentation fault when no overlap exists in image-mode.
+  bug #57164: MakeCatalog crashes when a label isn't in the dataset.
+  bug #57180: MakeCatalog reporting infinity S/N when --instd isn't an image.
+  bug #57200: Generated pkgconfig must request wcslib, not wcs.
+  bug #57293: NaN value for brightness-related columns when values have NaN.
 
 
 
@@ -111,8 +782,8 @@ See the end of the file for license conditions.
   Installation:
    - With the the following options at configure time, its possible to
      build Gnuastro without the optional libraries (even if they are
-     present on the host system): `--without-libjpeg', `--without-libtiff',
-     `--without-libgit2'.
+     present on the host system): '--without-libjpeg', '--without-libtiff',
+     '--without-libgit2'.
 
   All programs:
    - When an array is memory-mapped to non-volatile space (like the
@@ -120,13 +791,13 @@ See the end of the file for license conditions.
      its size. Later, when its deleted, a warning/message is also printed,
      informing you that it has been deleted. These warnings can be very
      useful when you actually have enough RAM, but forget to increase the
-     `--minmapsize' value (therefore significantly slowing down the
+     '--minmapsize' value (therefore significantly slowing down the
      program). When you don't have enough RAM, but don't want to be annoyed
-     with the warnings, you can use the new `--quietmmap' option to disable
+     with the warnings, you can use the new '--quietmmap' option to disable
      them.
 
   Arithmetic:
-   - `unique' operator removes all duplicate (and blank) elements from the
+   - 'unique' operator removes all duplicate (and blank) elements from the
      dataset and returns a single-dimension output, containing only the
      unique values in the dataset.
 
@@ -134,7 +805,7 @@ See the end of the file for license conditions.
    - Can also crop 3D datasets (data cubes). A 3D crop has the same syntax
      as the old 2D mode, only when the dataset is 3D, three coordinates
      (values, ranges or catalog-columns) should be given to the relevant
-     option. Just note that `--polygon' crops are still not supported in
+     option. Just note that '--polygon' crops are still not supported in
      3D.
 
   CosmicCalculator:
@@ -143,7 +814,7 @@ See the end of the file for license conditions.
      and observed wavelength and the redshift to use will be calculated
      internally. For many lines, it is possible to give the line name
      instead of its rest-frame wavelength. For example
-     `--obsline=lyalpha,6000' will use the redshift where the Lyman-alpha
+     '--obsline=lyalpha,6000' will use the redshift where the Lyman-alpha
      line has been shifted to 6000 Angstroms.
    --usedredshift: Print the used redshift as a "Specific calculation" (in
      line with other single-valued calculations).
@@ -158,7 +829,7 @@ See the end of the file for license conditions.
      measurement. Until now sigma-clipping results included a lot of
      visually useful information, which also made automatic usage of
      results hard. These options fix this issue. Please see the example in
-     the book under `--sigclip-median' for a nice use case.
+     the book under '--sigclip-median' for a nice use case.
 
   Table:
    - Column arithmetic. It is now possible to apply many operations on the
@@ -166,21 +837,21 @@ See the end of the file for license conditions.
      Arithmetic, but on table columns. The operators and notation is just
      like the Arithmetic program. See the "Column Arithmetic" section of
      the book for a detailed discussion and several examples.
-   - WCS to Image coordinate conversion with `wcstoimg' and `imgtowcs'. For
-     example if the input catalog has at least an `ID' column and two `RA'
-     and `DEC' columns, the set of options below will produce 5 columns
+   - WCS to Image coordinate conversion with 'wcstoimg' and 'imgtowcs'. For
+     example if the input catalog has at least an 'ID' column and two 'RA'
+     and 'DEC' columns, the set of options below will produce 5 columns
      where the last two columns are the image coordinates for each row
-     based on the WCS in `a.fits':
-           `-cID,RA,DEC -c"arith RA DEC wcstoimg" --wcsfile=a.fits'
+     based on the WCS in 'a.fits':
+           '-cID,RA,DEC -c"arith RA DEC wcstoimg" --wcsfile=a.fits'
    --head: Only output the given number of rows from the top of columns.
    --tail: Only output the given number of rows from the bottom of columns.
 
   Library:
-   - New `speclines.h' library functions and macros related to spectral
+   - New 'speclines.h' library functions and macros related to spectral
      lines. It has many macros with line wavelengths, and several functions
      for using them in combination with their names.
    - list.h: Functions to return the last element in linked lists. For
-     example `gal_list_sizet_last' or `gal_list_data_last'.
+     example 'gal_list_sizet_last' or 'gal_list_data_last'.
    - gal_arithmetic_operator_string: Return operator string from code.
    - gal_arithmetic_set_operator: Return operator code from string.
    - gal_blank_initialize_array: Initialize an array with blank values.
@@ -189,46 +860,46 @@ See the end of the file for license conditions.
    - gal_fits_img_info_dim: Only return the size information of a dataset.
    - GAL_TYPE_INT: Corresponding to respective width based on system.
    - GAL_TYPE_UINT: Corresponding to respective width based on system.
-   - GAL_BLANK_INT: Blank value for `int' (can be 16-bit or 32-bit).
-   - GAL_BLANK_UINT: Blank value for unsigned `int' (can be 16-bit or 32-bit).
+   - GAL_BLANK_INT: Blank value for 'int' (can be 16-bit or 32-bit).
+   - GAL_BLANK_UINT: Blank value for unsigned 'int' (can be 16-bit or 32-bit).
 
 ** Removed features
 
 ** Changed features
 
   Installation:
-   - Better `./configure' tests (using Gnulib's `AC_LIB_HAVE_LINKFLAGS') to
-     avoid some crashes during `make' when the host had multiple
+   - Better './configure' tests (using Gnulib's 'AC_LIB_HAVE_LINKFLAGS') to
+     avoid some crashes during 'make' when the host had multiple
      conflicting versions of some dependencies (GSL in particular).
 
   Arithmetic:
    - The output of co-adding operators is no longer the same type as the
-     input in general. The output of the `min' and `max' operators are
-     still the same type as the input. However the `number' and
-     `sigclip-number' operators will output an unsigned 32-bit integer type
-     and the rest (`sum', `mean', `std', `median', `sigclip-median',
-     `sigclip-mean' and `sigclip-std') return 32-bit floating point
+     input in general. The output of the 'min' and 'max' operators are
+     still the same type as the input. However the 'number' and
+     'sigclip-number' operators will output an unsigned 32-bit integer type
+     and the rest ('sum', 'mean', 'std', 'median', 'sigclip-median',
+     'sigclip-mean' and 'sigclip-std') return 32-bit floating point
      datasets
 
   MakeCatalog:
    - When a clumps catalog is requested, MakeCatalog will automatically
      deduce the total number of clumps (at a small cost in
      performance). Until now, it was mandatory for the clumps label dataset
-     to contain the total number of clumps in the `NUMLABS' keyword.
+     to contain the total number of clumps in the 'NUMLABS' keyword.
 
   Library:
    - gal_statistics_outlier_flat_cfp: Improved implementation with new API.
-   - New `quietmmap' argument added to the following functions (as the
-     argument following `minmapsize'). For more, see the description above
-     of the new similarly named option to all programs: `gal_array_read'
-     `gal_array_read_to_type', `gal_array_read_one_ch',
-     `gal_array_read_one_ch_to_type', `gal_data_alloc',
-     `gal_data_initialize', `gal_fits_img_read',
-     `gal_fits_img_read_to_type', `gal_fits_img_read_kernel',
-     `gal_fits_tab_read', `gal_jpeg_read', `gal_label_indexs',
-     `gal_list_data_add_alloc', `gal_match_coordinates',
-     `gal_pointer_allocate_mmap', `gal_table_read', `gal_tiff_read' and
-     `gal_txt_image_read'
+   - New 'quietmmap' argument added to the following functions (as the
+     argument following 'minmapsize'). For more, see the description above
+     of the new similarly named option to all programs: 'gal_array_read'
+     'gal_array_read_to_type', 'gal_array_read_one_ch',
+     'gal_array_read_one_ch_to_type', 'gal_data_alloc',
+     'gal_data_initialize', 'gal_fits_img_read',
+     'gal_fits_img_read_to_type', 'gal_fits_img_read_kernel',
+     'gal_fits_tab_read', 'gal_jpeg_read', 'gal_label_indexs',
+     'gal_list_data_add_alloc', 'gal_match_coordinates',
+     'gal_pointer_allocate_mmap', 'gal_table_read', 'gal_tiff_read' and
+     'gal_txt_image_read'
 
   Book:
    - The two larger tutorials ("General program usage tutorial", and
@@ -271,54 +942,54 @@ See the end of the file for license conditions.
      option's value).
 
   Arithmetic:
-   - The new `tofile-' and `tofilefree-' operators will save the top
+   - The new 'tofile-' and 'tofilefree-' operators will save the top
      operand into a file. They can be very handy in debugging/understanding
      an Arithmetic command (especially as it gets complicated), or to
      produce multiple files/extensions with a single call to Arithmetic.
    - Four new operators have been added to allow coadding multiple datasets
-     into one using sigma-clipping: `sigclip-number', `sigclip-mean',
-     `sigclip-median', and `sigclip-std'. These are very useful when
+     into one using sigma-clipping: 'sigclip-number', 'sigclip-mean',
+     'sigclip-median', and 'sigclip-std'. These are very useful when
      several inputs have strong outliers that affect the median, or the
      mean is required.
    - Multithreaded operation for the following operators that
-     combine/co-add multiple inputs into one output with same size: `min',
-     `max', `number', `sum', `mean', `std', `median', `sigclip-number',
-     `sigclip-median', `sigclip-mean', `sigclip-std'.
+     combine/co-add multiple inputs into one output with same size: 'min',
+     'max', 'number', 'sum', 'mean', 'std', 'median', 'sigclip-number',
+     'sigclip-median', 'sigclip-mean', 'sigclip-std'.
    --wcsfile and --wcshdu: these two options can be used to specify a
      different file for reading the WCS of the output. This is useful when
      the default (the WCS of the first dataset that is read) is not the
      required one.
    --interpmetric: new option that is necessary with the
-     `interpolate-medianngb' operator. For more, see the description of
+     'interpolate-medianngb' operator. For more, see the description of
      this option in NoiseChisel.
 
   Fits:
-   - Add "title" to group FITS keywords with `--write=/,"title name". This
+   - Add "title" to group FITS keywords with '--write=/,"title name". This
      "title" is composed of two keyword records/lines: a blank one (all
-     whitespace), followed by another starting with `/' and ending in any
+     whitespace), followed by another starting with '/' and ending in any
      string given to this option. This visually separates the keywords and
      acts as a title. Classifying the keywords into contextually similar
      groups greatly helps in visual inspection and is encouraged.
-   - Calculate and write `CHECKSUM' and `DATASUM' integrity keywords into
-     the specified header using `--write=checksum' (for both) or
-     `--write=datasum' (only for `DATASUM').
+   - Calculate and write 'CHECKSUM' and 'DATASUM' integrity keywords into
+     the specified header using '--write=checksum' (for both) or
+     '--write=datasum' (only for 'DATASUM').
    --datetosec: Convert the FITS date format (old or new) to number of
      seconds since since the Unix epoch time (1970-01-01,00:00:00). The
-     FITS date format (for example `YYYY-MM-DDThh:mm:ss') is hard to use
+     FITS date format (for example 'YYYY-MM-DDThh:mm:ss') is hard to use
      for automatic processing (requires calendar peculiarities like number
      of days in each month, or leap years and etc). With this option a
      single integer is returned that can be used for example to sort FITS
      files by date keywords without worrying about calendar peculiarities.
-   --verify: confirm if the `DATASUM' and `CHECKSUM' keyword values agree
+   --verify: confirm if the 'DATASUM' and 'CHECKSUM' keyword values agree
      with the specified HDU's content and/or data.
    --copykeys: Copy several keyword records (in a given range) from one
      FITS HDU/extension into another (possibly in another file).
-   --outhdu: The name/number of the output HDU (for `--copykeys').
+   --outhdu: The name/number of the output HDU (for '--copykeys').
 
   Match:
    - All the columns from one of the input catalogs can now be merged with
-     any of the columns of the second using the special `_all' name of
-     `--outcols'. For example the output of `--outcols=a_all,b5' will
+     any of the columns of the second using the special '_all' name of
+     '--outcols'. For example the output of '--outcols=a_all,b5' will
      contain all the columns from the first input and the 5th column of the
      second input. This greatly simplifies the merging of different table
      columns into one.
@@ -349,39 +1020,39 @@ See the end of the file for license conditions.
        image) to define separate pseudo-detections. If its stronger,
        pseudo-detections that are touching on the corner will be identified
        as one.
-     --dopening: The number of openings to do after applying `--dthresh'.
-     --dopeningngb: The connectivity (4 or 8) to use for `--dopening'.
+     --dopening: The number of openings to do after applying '--dthresh'.
+     --dopeningngb: The connectivity (4 or 8) to use for '--dopening'.
 
   Statistics:
    --interpmetric: Similar to NoiseChisel.
 
   Table:
    --range: Limit the output rows to those with a value within the given
-     numeric range with this format: `--range=COL,low,high'. This is very
+     numeric range with this format: '--range=COL,low,high'. This is very
      useful when only certain rows of the input are required not the
      output. The advantage over piping to AWK is that you can save the
      output directly to FITS (preserving the metadata). See the book for
      more.
    --sort: Sort the output rows based on the value of the given column in
      ascending order.
-   --descending: When called with `--sort', will arrange the output rows in
+   --descending: When called with '--sort', will arrange the output rows in
      descending order.
 
   Installed scripts:
    With this release, Gnuastro also installs Bash scripts for common
    higher-level usage of (possibly multiple) programs. These scripts have a
-   `astscript-*' name, to easily show up on the command-line as Gnuastro
+   'astscript-*' name, to easily show up on the command-line as Gnuastro
    executables with the other Gnuastro programs, but are identifiable from
    them. They support options just like the programs (which can be listed
-   with `--help'). Please see the new "Installed scripts" chapter of the
+   with '--help'). Please see the new "Installed scripts" chapter of the
    book for more.
    - astscript-sort-by-night: New Gnuastro executable, using Gnuastro's
      Fits program to identify files with dates in the same night (possibly
      spanning two calendar dates).
 
   Library:
-    GAL_BLANK_LONG: new macro for the `long' type blank value.
-    GAL_BLANK_ULONG: new macro for the `unsigned long' type blank value.
+    GAL_BLANK_LONG: new macro for the 'long' type blank value.
+    GAL_BLANK_ULONG: new macro for the 'unsigned long' type blank value.
     gal_blank_number: Return the number of blank elements in a dataset.
     gal_dimension_dist_radial: Radial distance between two coordinates.
     gal_fits_key_date_to_struct_tm: FITS date format to C broken-down time.
@@ -398,14 +1069,14 @@ See the end of the file for license conditions.
 ** Changed features
 
   Arithmetic:
-   - `num' operator is renamed to `number'.
-   - `numvalue' operator is renamed to `numbervalue'.
-   - `--dontdelete' will append the output to any existing file. Note that
+   - 'num' operator is renamed to 'number'.
+   - 'numvalue' operator is renamed to 'numbervalue'.
+   - '--dontdelete' will append the output to any existing file. Note that
      this change is only in Arithmetic, other programs will still just
      complain and abort.
 
   ConvertType:
-   --forcemin & --forcemax: until now, `--flminbyte' and `--flmaxbyte' were
+   --forcemin & --forcemax: until now, '--flminbyte' and '--flmaxbyte' were
      used to force the range of conversion to color channels (even if the
      range is beyond the limits of the dataset). With the introduction of
      color maps in Gnuastro 0.8, it is also necessary to force a range on
@@ -419,25 +1090,25 @@ See the end of the file for license conditions.
      mean square (the correct definition of the Sky standard deviation).
 
   NoiseChisel:
-   --ignoreblankintiles: Until now `--ignoreblankinsky', would specify if
+   --ignoreblankintiles: Until now '--ignoreblankinsky', would specify if
      blank values should also be written into the tiled Sky and Sky
      standard deviation outputs. But NoiseChisel can optionally produce
-     many more tiled outputs (for example with `--checkqthresh'). So the
-     option was renamed to `--ignoreblankintiles' to highlight that the
+     many more tiled outputs (for example with '--checkqthresh'). So the
+     option was renamed to '--ignoreblankintiles' to highlight that the
      status of blank elements can be set in all tiled outputs.
 
   Statistics:
    --ignoreblankintiles: similar to same option in NoiseChisel.
 
   Table:
-   --colinfoinstdout: now corresponds to the `-O' short option. Until this
-     version, the `-s' short option was used for it. But with the new
-     `--sort' option, `-s' may cause confusion.
+   --colinfoinstdout: now corresponds to the '-O' short option. Until this
+     version, the '-s' short option was used for it. But with the new
+     '--sort' option, '-s' may cause confusion.
 
   Library
    gal_arithmetic: new argument: number of threads to use (when relevant).
-   gal_eps_write: new argument: optional bit-optimization with `dontoptimize'.
-   gal_pdf_write: new argument: optional bit-optimization with `dontoptimize'.
+   gal_eps_write: new argument: optional bit-optimization with 'dontoptimize'.
+   gal_pdf_write: new argument: optional bit-optimization with 'dontoptimize'.
 
 ** Bugs fixed
   bug #55313: Fits program writing --write values in reverse order
@@ -469,7 +1140,7 @@ See the end of the file for license conditions.
    - Standard input (for example from pipes) is now available to feed input
      to all programs that accept plain text input (ConvertType, Convolve,
      Match, MakeProfiles, Statistics, Table).
-   - Updated acknowledgment statement (output of `--cite' option).
+   - Updated acknowledgment statement (output of '--cite' option).
 
   Arithmetic:
     --onedasimage: write output as an image if it has one dimension, not table.
@@ -485,9 +1156,9 @@ See the end of the file for license conditions.
 
   Convolve:
     - Convolves 1D arrays (table columns, for example spectra)
-      also. Therefore two new options have been added to it: `--column'
-      (`-c', similar to other programs that read table columns), and
-      `--kernelcolumn' (to specify the column of the kernel in its own
+      also. Therefore two new options have been added to it: '--column'
+      ('-c', similar to other programs that read table columns), and
+      '--kernelcolumn' (to specify the column of the kernel in its own
       file/extension).
 
   Fits:
@@ -500,7 +1171,7 @@ See the end of the file for license conditions.
       pixel-value distribution test due to the flatness of the extended
       profiles, can be identified and removed as outliers in comparison
       with the other passed tiles. The outlier finding algorithm
-      (`gal_statistics_outlier_positive': a new library function) uses the
+      ('gal_statistics_outlier_positive': a new library function) uses the
       distribution of distances between the sorted elements and is
       configured with these options.
        --outliersclip: Sigma-clipping parameters for the process.
@@ -521,7 +1192,7 @@ See the end of the file for license conditions.
           the Sky over the tiles that have a sufficiently large fraction of
           undetected pixels. This is done to decrease the bias caused by
           faint un-detected wings of bright galaxies or stars, see
-          description of `--minskyfrac' for more. Until now the reference
+          description of '--minskyfrac' for more. Until now the reference
           for this fraction was the whole tile size (irrespective of how
           many blank elements it contains). With this option, it is now
           possible to ask for ignoring blank pixels when calculating the
@@ -530,7 +1201,7 @@ See the end of the file for license conditions.
 
   Statistics:
     - If an input table has only one column, Statistics won't complain and
-      abort when no `--column' (`-c') is given: there is only one column to
+      abort when no '--column' ('-c') is given: there is only one column to
       use anyway, so it will be used. In the absence of which column to
       use, it will still complain and abort if the input has more than one
       column.
@@ -561,29 +1232,29 @@ See the end of the file for license conditions.
 
   Arithmetic:
     - If the output has one dimension, it will be written as a table, not a
-      FITS image/array. This can be changed with the new `--onedasimage'
+      FITS image/array. This can be changed with the new '--onedasimage'
       option.
 
   Convolve:
-    - The short option for `--numchannels' is now `-n'. Until now, it was
-      `-c', but that would conflict with the short option used for
-      `--column' in all the other programs that also read from a table.
+    - The short option for '--numchannels' is now '-n'. Until now, it was
+      '-c', but that would conflict with the short option used for
+      '--column' in all the other programs that also read from a table.
 
   MakeProfiles:
-    --mergedsize: new name for the old `--naxis' option. Since the option
+    --mergedsize: new name for the old '--naxis' option. Since the option
           names and values are now written into the FITS header of the
           output, this option's name would get confused with the mandatory
-          FITS keyword `NAXIS'.
+          FITS keyword 'NAXIS'.
 
   NoiseChisel:
     - Until now, the mode's quantile was used to identify tiles with no
       significant signal. But from this version, the mean's quantile in
       each tile is used instead. The reason is that the mean is more
-      sensitive to outliers (signal). Therefore the old `--modmedqdiff' is
-      now called `--meanmedqdiff' .
+      sensitive to outliers (signal). Therefore the old '--modmedqdiff' is
+      now called '--meanmedqdiff' .
 
   Statistics:
-    --meanmedqdiff: new name for `--modmedqdiff'. Similar to NoiseChisel.
+    --meanmedqdiff: new name for '--modmedqdiff'. Similar to NoiseChisel.
 
   Library:
     - gal_array_read: list of strings (from standard input) acceptable.
@@ -608,7 +1279,7 @@ See the end of the file for license conditions.
   bug #54579: NoiseChisel pseudo-detection failure when dataset is negative.
   bug #54782: Segment's check image not removing sky clumps some tiles.
   bug #54810: Arithmetic crash when previously named operand renamed.
-  bug #55025: MakeCatalog's `--prepforconv' option being ignored.
+  bug #55025: MakeCatalog's '--prepforconv' option being ignored.
   bug #55079: Blank EPS or PDF page when width options not given.
   bug #55157: No sanity check on values given to Crop's --section.
   bug #55295: Crash when more than one collapse operator called.
@@ -624,18 +1295,18 @@ See the end of the file for license conditions.
 
   Installation:
     --enable-debug: debugging flags, no optimization, no shared libraries.
-    --enable-check-with-valgrind: Run `make check' tests within Valgrind.
+    --enable-check-with-valgrind: Run 'make check' tests within Valgrind.
 
   Arithmetic:
-    - `set-A': Set a name (`A' in this case) for the popped dataset. This
+    - 'set-A': Set a name ('A' in this case) for the popped dataset. This
                allows only reading the dataset it into memory once and
                possibly using it many times.
-    - `fill-holes': Flip background (0) pixels surrounded by foreground (1).
-    - `collapse-sum': collapse/remove a dimension by summing over it.
-    - `collapse-min': collapse/remove a dimension by using minimum value.
-    - `collapse-max': collapse/remove a dimension by using maximum value.
-    - `collapse-mean': collapse/remove a dimension by averaging over it.
-    - `collapse-number': Number of elements included in the collapse.
+    - 'fill-holes': Flip background (0) pixels surrounded by foreground (1).
+    - 'collapse-sum': collapse/remove a dimension by summing over it.
+    - 'collapse-min': collapse/remove a dimension by using minimum value.
+    - 'collapse-max': collapse/remove a dimension by using maximum value.
+    - 'collapse-mean': collapse/remove a dimension by averaging over it.
+    - 'collapse-number': Number of elements included in the collapse.
 
   CosmicCalculator:
     - Default cosmology set to Plank 2018 results (Paper VI).
@@ -665,12 +1336,12 @@ See the end of the file for license conditions.
     --checkcenter: the units of value depend on mode (image or WCS).
 
   MakeCatalog:
-    - `--checkuplim': new name for `--checkupperlimit'.
-    - `--brightnessnoriver': new name for `--noriverbrightness'.
+    - '--checkuplim': new name for '--checkupperlimit'.
+    - '--brightnessnoriver': new name for '--noriverbrightness'.
 
   Library:
-    - gal_txt_write: new `colinfoinstdout' argument.
-    - gal_table_write: new `colinfoinstdout' argument.
+    - gal_txt_write: new 'colinfoinstdout' argument.
+    - gal_table_write: new 'colinfoinstdout' argument.
 
 ** Bugs fixed
 
@@ -752,7 +1423,7 @@ See the end of the file for license conditions.
     --rawoutput: only output the detection labels and Sky and its STD.
     --ignoreblankinsky: don't set the pixels that are blank in the input to
       blank in the Sky and Sky standard deviation outputs (when
-      `--oneelempertile' is not called).
+      '--oneelempertile' is not called).
     --label: label the connected detections. Until now this was the default
       behavior. However, from this release, NoiseChisel is only in charge
       of detection. Segmentation is done by a new program (Segment). Since
@@ -767,7 +1438,7 @@ See the end of the file for license conditions.
 
   Libraries:
     gal_array_read: read array from any of known formats (FITS, TIFF, 
JPEG,...).
-    gal_array_read_to_type: similar to `gal_array_read', but to given type.
+    gal_array_read_to_type: similar to 'gal_array_read', but to given type.
     gal_array_read_one_ch: Read a dataset, abort if it has multiple channels.
     gal_array_read_one_ch_to_type: Make sure input is in one channel and type.
     gal_binary_label_holes: label the holes within the foreground.
@@ -777,11 +1448,11 @@ See the end of the file for license conditions.
     gal_eps_to_pt: Converts dataset size to PostScript points.
     gal_eps_write: Writes a dataset into an EPS file.
     gal_interpolate_1d_blank: Fill blank elements through interpolation.
-    gal_interpolate_1d_make_gsl_spline: Allocate and initalize `gsl_spline'.
+    gal_interpolate_1d_make_gsl_spline: Allocate and initalize 'gsl_spline'.
     gal_jpeg_name_is_jpeg: Returns 1 if given filename is JPEG.
     gal_jpeg_suffix_is_jpeg: Returns 1 if given suffix is JPEG.
-    gal_jpeg_read: Reads input JPEG image into `gal_data_t'.
-    gal_jpeg_write: Writes a `gal_data_t' into a JPEG file.
+    gal_jpeg_read: Reads input JPEG image into 'gal_data_t'.
+    gal_jpeg_write: Writes a 'gal_data_t' into a JPEG file.
     gal_label_grow_indexs: grow known indexs into desired areas.
     gal_label_watershed: apply watershed algorithm on desired region.
     gal_label_clump_significance: measure significance of all clumps in region.
@@ -796,7 +1467,7 @@ See the end of the file for license conditions.
     gal_tiff_name_is_tiff: check if name contains a TIFF suffix.
     gal_tiff_suffix_is_tiff: check if suffix is a TIFF suffix.
     gal_tiff_dir_string_read: convert a string to a TIFF directory number.
-    gal_tiff_read: Read the contents of a TIFF "directory" to `gal_data_t'.
+    gal_tiff_read: Read the contents of a TIFF "directory" to 'gal_data_t'.
 
 ** Removed features
 
@@ -812,10 +1483,10 @@ See the end of the file for license conditions.
     --skysubtracted: no longer necessary, included in noise measuremnts.
 
   Library:
-    - The macros `GAL_STATISTICS_SORTED_NOT',
-      `GAL_STATISTICS_SORTED_INVALID', `GAL_STATISTICS_SORTED_INCREASING',
-      `GAL_STATISTICS_SORTED_DECREASING': these macros are removed because
-      we already have the `GAL_DATA_FLAG_SORT*'' bit-flags in `gal_data_t'.
+    - The macros 'GAL_STATISTICS_SORTED_NOT',
+      'GAL_STATISTICS_SORTED_INVALID', 'GAL_STATISTICS_SORTED_INCREASING',
+      'GAL_STATISTICS_SORTED_DECREASING': these macros are removed because
+      we already have the 'GAL_DATA_FLAG_SORT*'' bit-flags in 'gal_data_t'.
 
 
 ** Changed features
@@ -830,9 +1501,9 @@ See the end of the file for license conditions.
     --comment: can be called/written multiple times in one run.
 
   MakeCatalog:
-    - The `WCLUMPS' keyword in the objects labeled image is no longer used
+    - The 'WCLUMPS' keyword in the objects labeled image is no longer used
          to see if a clumps catalog should also be made. To build a clumps
-         catalog, you can now use the `--clumpscat' option.
+         catalog, you can now use the '--clumpscat' option.
     - Estimation of noise-level is now done per-pixel over the whole
          label. Until now the average noise level was used.
     --objectsfile has been removed. The main input argument is now assumed
@@ -843,7 +1514,7 @@ See the end of the file for license conditions.
     do segmentation any more. The new Segment program is in charge of
     segmentation. Many of the changes below are due to this new limited
     scope of NoiseChisel.
-    --kernel: value `none' will disable convolution.
+    --kernel: value 'none' will disable convolution.
     - Renamed options:
       --convolvedhdu ==> --chdu
       --wkhdu        ==> --whdu
@@ -851,7 +1522,7 @@ See the end of the file for license conditions.
       --checkdetsn   ==> --checksn
       --detquant     ==> --snquant
     - By default the output detection map is a binary image (values of 0 or 1).
-    - With no output name, the output has a `_detected.fits' suffix.
+    - With no output name, the output has a '_detected.fits' suffix.
 
   Segment:
     - [Previously in NoiseChisel]: For finding true clumps, the difference
@@ -867,34 +1538,34 @@ See the end of the file for license conditions.
          still acceptable also).
 
   Libraries:
-    gal_binary_holes_fill: new name for `gal_binary_fill_holes'.
-    gal_dimension_is_different: new name for `gal_data_dsize_is_different'.
+    gal_binary_holes_fill: new name for 'gal_binary_fill_holes'.
+    gal_dimension_is_different: new name for 'gal_data_dsize_is_different'.
     gal_fits_img_read: now only reads the data not the WCS, therefore it no
         longer needs the last two arguments. A subsequent call to
-        `gal_wcs_read' can be used to read the WCS information in the file.
-    gal_pointer_increment: new name for `gal_data_ptr_increment'.
-    gal_pointer_num_between: new name for `gal_data_ptr_dist'.
-    gal_pointer_allocate: replaces `gal_data_malloc_array' and
-        `gal_data_calloc_array', through an argument you can ask for the
+        'gal_wcs_read' can be used to read the WCS information in the file.
+    gal_pointer_increment: new name for 'gal_data_ptr_increment'.
+    gal_pointer_num_between: new name for 'gal_data_ptr_dist'.
+    gal_pointer_allocate: replaces 'gal_data_malloc_array' and
+        'gal_data_calloc_array', through an argument you can ask for the
         allocated memory to be cleared or not.
     gal_qsort_TYPE_i: new name for gal_qsort_TYPE_increasing.
     gal_qsort_TYPE_d: new name for gal_qsort_TYPE_decreasing.
     gal_statistics_is_sorted: can now also update the bit-flags regarding
         the sorted nature of the input (to optimize future calls to the
         function).
-    gal_statistics_quantile_function: returns `inf' or `-inf' if the given
+    gal_statistics_quantile_function: returns 'inf' or '-inf' if the given
         value is smaller than the minimum or larger than the maximum of the
         input dataset's range. Until now, it would return blank in such
         cases.
-    gal_statistics_number: the output dataset now has a `size_t' type. Until
-        now it was `uint64_t'.
+    gal_statistics_number: the output dataset now has a 'size_t' type. Until
+        now it was 'uint64_t'.
 
 ** Bugs fixed
 
   bug #50957: --version output not possible on Mac OS X
   bug #52979: Many unused result warnings for asprintf in some compilers.
   bug #53122: Configure time CPPFLAGS and LDFLAGS don't pass to BuildProgram.
-  bug #53142: Crash when printing values with the `--onlyversion' option.
+  bug #53142: Crash when printing values with the '--onlyversion' option.
   bug #53147: NULL value of onlyversion option causing a crash.
   bug #53226: Match output directory ignored when making multiple files.
   bug #53230: Statistics program bad results on integer columns with limits.
@@ -924,68 +1595,68 @@ See the end of the file for license conditions.
   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 low-level `gal_match_catalog' library
+  work is also done with the new low-level 'gal_match_catalog' library
   function which can also be used in more generic contexts.
 
-  All programs: a value of `0' to the `--numthreads' option will use the
+  All programs: a value of '0' to the '--numthreads' option will use the
   number of threads available to the system at run time.
 
-  Arithmetic: The new operators `filter-median' and `filter-mean' can be
+  Arithmetic: The new operators 'filter-median' and 'filter-mean' can be
   used to filter (smooth) the input. The size of the filter can be set as
   the other operands to these operators.
 
-  BuildProgram: The new `--la' option allows the identification of a
-  different Libtool `.la' file for Libtool linking information.
+  BuildProgram: The new '--la' option allows the identification of a
+  different Libtool '.la' file for Libtool linking information.
 
-  BuildProgram: The new `--deletecompiled' option will delete the compiled
+  BuildProgram: The new '--deletecompiled' option will delete the compiled
   program after running it.
 
   CosmicCalculator: all the various cosmological calculations can now be
   requested individually in one line with a specific option added for each
-  calculation (for example `--age' or `--luminositydist' for the age of the
+  calculation (for example '--age' or '--luminositydist' for the age of the
   universe at a given redshift or the luminosity distance). Therefore the
-  old `--onlyvolume' and `--onlyabsmagconv' options are now removed. To
+  old '--onlyvolume' and '--onlyabsmagconv' options are now removed. To
   effectively use these new features, please review the "Invoking
   CosmicCalculator" section of the book.
 
   Fits: when an extension/HDU is identified on the command-line with the
-  `--hdu' option and no operation is requested, the full list of header
-  keywords in that HDU will be printed (as if only `--printallkeys' was
+  '--hdu' option and no operation is requested, the full list of header
+  keywords in that HDU will be printed (as if only '--printallkeys' was
   called).
 
   MakeCatalog: physical nature agnostic WCS column names. Previously the
   first WCS axis was always assumed to be RA and the second DEC. So for
   example even if you had a spectrum (with X and wavelength as the two WCS
-  dimensions), you would have to ask for `--ra' and `--dec'. The new `--w1'
-  and `--w2' options are now generic and don't assume any particular type
+  dimensions), you would have to ask for '--ra' and '--dec'. The new '--w1'
+  and '--w2' options are now generic and don't assume any particular type
   only their order in the FITS header. MakeCatalog now also uses the CTYPE
   and CUNIT keywords to set the names and units of its output columns. The
-  `--ra' and `--dec' options are now just internal aliases for `--w1' or
-  `--w2' which will be determined based on the input's CTYPE keyword. Also
-  the new `--geow1', `--geow2', `--clumpsw1', `--clumpsw2',
-  `--clumpsgeow1', `--clumpsgeow2' options replace the old options
-  `--geora', `--geodec', `--clumpsra', `--clumpsdec', `--clumpsgeora',
-  `--clumpsgeodec'. No alias is currently defined for the latter group.
-
-  MakeCatalog: the new `--uprange' option allows you to specify a range for
+  '--ra' and '--dec' options are now just internal aliases for '--w1' or
+  '--w2' which will be determined based on the input's CTYPE keyword. Also
+  the new '--geow1', '--geow2', '--clumpsw1', '--clumpsw2',
+  '--clumpsgeow1', '--clumpsgeow2' options replace the old options
+  '--geora', '--geodec', '--clumpsra', '--clumpsdec', '--clumpsgeora',
+  '--clumpsgeodec'. No alias is currently defined for the latter group.
+
+  MakeCatalog: the new '--uprange' option allows you to specify a range for
   the random values around each object. This is useful when the noise
   properties of the dataset vary gradually and sampling from the whole
   dataset might produce biased results.
 
-  NoiseChisel: with the new `--convolved' and `--convolvedhdu' options,
+  NoiseChisel: with the new '--convolved' and '--convolvedhdu' options,
   NoiseChisel will not convolve the input any more and use the given
   dataset instead. In many cases, as the inputs get larger, convolution is
   the most time consuming step of NoiseChisel. With this option, you can
   greatly speed up your tests (to find the best parameters by varying them,
   for a given analysis). See the book for more information and examples.
 
-  NoiseChisel: with the new `--widekernel' option it is now possible to use
+  NoiseChisel: with the new '--widekernel' option it is now possible to use
   a wider kernel to identify which tiles contain signal. The rest of the
   steps (identifying the quantile threshold on the selected tiles and etc)
-  are done on the dataset convolved with `--kernel' as they were
+  are done on the dataset convolved with '--kernel' as they were
   before. Since it is time consuming, this is an optional feature.
 
-  NoiseChisel: with the new `--qthreshtilequant' option, it is now possible
+  NoiseChisel: with the new '--qthreshtilequant' option, it is now possible
   to discard high-valued (outlier) tiles before estimating qthresh over the
   whole image. This can be useful in detecting very large diffuse/flat
   regions that would otherwise be detected as background (and effectively
@@ -995,42 +1666,42 @@ See the end of the file for license conditions.
   signal contiguity, not by blind dilation. The growth process is the same
   as the growing of clumps to define objects. Only for true detections, the
   growth occurs in the noise. You can configure this growth with the
-  `--detgrowquant' and `--detgrowmaxholesize'. With this new feature it is
+  '--detgrowquant' and '--detgrowmaxholesize'. With this new feature it is
   now possible to detect signal out to much lower surface brightness limits
   and the detections don't look boxy any more.
 
   Cosmology library: A new set of cosmology functions are now included in
-  the library (declared in `gnuastro/cosmology.h'). These functions are
+  the library (declared in 'gnuastro/cosmology.h'). These functions are
   also used in the CosmicCalculator program.
 
-  `gal_table_read' can now return the number of columns matched with each
+  'gal_table_read' can now return the number of columns matched with each
   input column (for example with regular expressions), a new argument has
   been added to allow this feature.
 
-  `gal_fits_key_img_blank': returns the value that must be used in the
+  'gal_fits_key_img_blank': returns the value that must be used in the
   BLANK keyword for the given type as defined by the FITS standard.
 
-  `gal_txt_write' and `gal_fits_tab_write' now accept an extension name as
+  '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
+  '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
 
-  Installation: The `--enable-bin-op-*' configuration options that were
+  Installation: The '--enable-bin-op-*' configuration options that were
   introduced in Gnuastro 0.3 have been removed. By managing the arithmetic
   functions in a better manner (a separate source file for each operator),
   compilation for all types (when done in parallel) takes about the same
   time as it took with the default (only four) types until now.
 
-  MakeCatalog: `--zeropoint' option doesn't have a short option name any
-  more. Previously it was `-z' which was confusing because `-x' and `-y'
+  MakeCatalog: '--zeropoint' option doesn't have a short option name any
+  more. Previously it was '-z' which was confusing because '-x' and '-y'
   were used to refer to image coordinate positions.
 
-  NoiseChisel: The `--dilate' and `--dilatengb' options have been
+  NoiseChisel: The '--dilate' and '--dilatengb' options have been
   removed. Growing of true detections is no longer done through dilation
-  but through the `--detgrowquant' and `--detgrowmaxholesize' options (see
+  but through the '--detgrowquant' and '--detgrowmaxholesize' options (see
   above).
 
 ** Changed features
@@ -1043,32 +1714,32 @@ See the end of the file for license conditions.
   now, two separate FITS files would be created. Plain text outputs are the
   same as before (two files will be created).
 
-  `gal_binary_fill_holes' now accepts a `connectivity' and `maxsize'
+  'gal_binary_fill_holes' now accepts a 'connectivity' and 'maxsize'
   argument to specify the connectivity of the holes and the maximum size of
   acceptable holes to fill.
 
-  `gal_fits_img_read' and `gal_fits_img_read_to_type' now also read the WCS
+  'gal_fits_img_read' and 'gal_fits_img_read_to_type' now also read the WCS
   structure of the extension/HDU in a FITS file and have two extra
-  arguments: `hstartwcs' and `hendwcs'. With these options it is possible
+  arguments: 'hstartwcs' and 'hendwcs'. With these options it is possible
   to limit the range of header keywords to read the WCS, similar to how
-  they are used in `gal_wcs_read'.
+  they are used in 'gal_wcs_read'.
 
-  `gal_txt_write', `gal_table_write_log', `gal_fits_tab_write' and
-  `gal_txt_write' don't have the `dontdelete' argument any more. The action
+  'gal_txt_write', 'gal_table_write_log', 'gal_fits_tab_write' and
+  'gal_txt_write' don't have the 'dontdelete' argument any more. The action
   they take if the file already exists depends on the file: for FITS, a new
   extension will be added and for text, they will abort with an error.
 
-  `gal_tile_block_write_const_value' and `gal_tile_full_values_write' now
-  accept a new `withblank' option to set all pixels that are blank in the
+  'gal_tile_block_write_const_value' and 'gal_tile_full_values_write' now
+  accept a new 'withblank' option to set all pixels that are blank in the
   tile's block to be blank in the check image.
 
-  `gal_wcs_pixel_area_arcsec2' will return NaN (instead of aborting) when
+  'gal_wcs_pixel_area_arcsec2' will return NaN (instead of aborting) when
   input is unreasonable (not two dimensions or not in units of degrees).
 
-  `gal_wcs_world_to_img' and `gal_wcs_img_to_world': Until now, these two
+  'gal_wcs_world_to_img' and 'gal_wcs_img_to_world': Until now, these two
   WCS conversion functions would explicitly assume RA and Dec and work
   based on input arrays (so for example it was also necessary to give the
-  number of elements and etc). They now accept `gal_data_t' as input for
+  number of elements and etc). They now accept 'gal_data_t' as input for
   the input coordinates, thus their API has been greatly simplified and
   their functionality increased.
 
@@ -1099,113 +1770,113 @@ See the end of the file for license conditions.
 
 ** New features
 
-  All programs: `.fit' is now a recognized FITS file suffix.
+  All programs: '.fit' is now a recognized FITS file suffix.
 
   All programs: ASCII text files (tables) created with CRLF line
   terminators (for example text files created in MS Windows) are now also
   readable as input when necessary.
 
-  Arithmetic: now has a new `--globalhdu' (`-g') option which can be used
+  Arithmetic: now has a new '--globalhdu' ('-g') option which can be used
   once for all the input images.
 
-  MakeNoise: with the new `--sigma' (`-s') option, it is now possible to
+  MakeNoise: with the new '--sigma' ('-s') option, it is now possible to
   directly request the noise sigma or standard deviation. When this option
-  is called, the `--background', `--zeropoint' and other option values will
+  is called, the '--background', '--zeropoint' and other option values will
   be ignored.
 
-  MakeProfiles: the new `--kernel' option can make a kernel image without
+  MakeProfiles: the new '--kernel' option can make a kernel image without
   the need to define a catalog. With this option, a catalog (or
   accompanying background image) must not be given.
 
-  MakeProfiles: the new `--pc', `--cunit' and `--ctype' options can be used
+  MakeProfiles: the new '--pc', '--cunit' and '--ctype' options can be used
   to specify the PC matrix, CUNIT and CTYPE world coordinate system
   keywords of the output FITS file.
 
-  MakeProfiles: the new `distance' profile will save the radial distance of
+  MakeProfiles: the new 'distance' profile will save the radial distance of
   each pixel. This may be used to define your own profiles that are not
   currently supported in MakeProfiles.
 
-  MakeProfiles: with the new `--mcolisbrightness' ("mcol-is-brightness")
-  option, the `--mcol' values of the catalog will be interpretted as total
+  MakeProfiles: with the new '--mcolisbrightness' ("mcol-is-brightness")
+  option, the '--mcol' values of the catalog will be interpretted as total
   brightness (sum of pixel values), not magnitude.
 
-  NoiseChisel: with the new `--dilatengb' option, it is now possible to
+  NoiseChisel: with the new '--dilatengb' option, it is now possible to
   identify the connectivity of the final dilation.
 
   Library: Functions that read data from an ASCII text file
-  (`gal_txt_table_info', `gal_txt_table_read', `gal_txt_image_read') now
+  ('gal_txt_table_info', 'gal_txt_table_read', 'gal_txt_image_read') now
   also operate on files with CRLF line terminators.
 
 ** Removed features
 
 ** Changed features
 
-  Crop: The new `--center' option is now used to define the center of a
-  single crop. Hence the old `--ra', `--dec', `--xc', `--yc' have been
+  Crop: The new '--center' option is now used to define the center of a
+  single crop. Hence the old '--ra', '--dec', '--xc', '--yc' have been
   removed. This new option can take multiple values (one value for each
   dimension). Fractions are also acceptable.
 
-  Crop: The new `--width' option is now used to define the width of a
-  single crop. Hence the old `--iwidth', `--wwidth' were removed. The units
-  to interpret the value to the option are specified by the `--mode'
-  option. With the new `--width' option it is also possible to define a
+  Crop: The new '--width' option is now used to define the width of a
+  single crop. Hence the old '--iwidth', '--wwidth' were removed. The units
+  to interpret the value to the option are specified by the '--mode'
+  option. With the new '--width' option it is also possible to define a
   non-square crop (different widths along each dimension). In WCS mode, its
   units are no longer arcseconds but are the same units of the WCS (degrees
-  for angles). `--width' can also accept fractions. So to set a width of 5
-  arcseconds, you can give it a value of `5/3600' for the angular
+  for angles). '--width' can also accept fractions. So to set a width of 5
+  arcseconds, you can give it a value of '5/3600' for the angular
   dimensions.
 
-  Crop: The new `--coordcol' option is now used to determine the catalog
-  columns that define coordinates. Hence the old `--racol', `--deccol',
-  `--xcol', and `--ycol' have been removed. This new option can be called
+  Crop: The new '--coordcol' option is now used to determine the catalog
+  columns that define coordinates. Hence the old '--racol', '--deccol',
+  '--xcol', and '--ycol' have been removed. This new option can be called
   multiple times and the order of its calling will be used for the column
   containing the center in the respective dimension (in FITS format).
 
-  MakeNoise: the old `--stdadd' (`-s') option has been renamed to
-  `--instrumental' (`-i') to be more clear.
+  MakeNoise: the old '--stdadd' ('-s') option has been renamed to
+  '--instrumental' ('-i') to be more clear.
 
-  MakeProfiles: The new `--naxis' and `--shift' options can take multiple
+  MakeProfiles: The new '--naxis' and '--shift' options can take multiple
   values for each dimension (separated by a comma). This replaces the old
-  `--naxis1', `--naxis2' and `--xshift' and `--yshift' options.
+  '--naxis1', '--naxis2' and '--xshift' and '--yshift' options.
 
-  MakeProfiles: The new `--ccol' option can take the center coordinate
-  columns of the catalog (in multiple calls) and the new `--mode' option is
+  MakeProfiles: The new '--ccol' option can take the center coordinate
+  columns of the catalog (in multiple calls) and the new '--mode' option is
   used to identify what standard to interpret them in (image or
-  WCS). Together, these replace the old `--xcol', `--ycol', `--racol' and
-  `--deccol'.
+  WCS). Together, these replace the old '--xcol', '--ycol', '--racol' and
+  '--deccol'.
 
-  MakeProfiles: The new `--crpix', `--crval' and `--cdelt' options now
+  MakeProfiles: The new '--crpix', '--crval' and '--cdelt' options now
   accept multiple values separated by a comma. So they replace the old
-  `--crpix1', `--crpix2', `--crval1', `--crval2' and `--resolution'
+  '--crpix1', '--crpix2', '--crval1', '--crval2' and '--resolution'
   options.
 
-  `gal_data_free_contents': when the input `gal_data_t' is a tile, its
-  `array' element will not be freed. This enables safe usage of this
-  function (and thus `gal_data_free') on tiles without worrying about the
+  'gal_data_free_contents': when the input 'gal_data_t' is a tile, its
+  'array' element will not be freed. This enables safe usage of this
+  function (and thus 'gal_data_free') on tiles without worrying about the
   memory block associated with the tile.
 
-  `gal_box_bound_ellipse' is the new name for the old
-  `gal_box_ellipse_in_box' (to be more clear and avoid repetition of the
-  term `box'). The input position angle is now also in degrees, not
+  'gal_box_bound_ellipse' is the new name for the old
+  'gal_box_ellipse_in_box' (to be more clear and avoid repetition of the
+  term 'box'). The input position angle is now also in degrees, not
   radians.
 
-  `gal_box_overlap' now works on data of any dimensionality and thus also
+  'gal_box_overlap' now works on data of any dimensionality and thus also
   needs the number of dimensions (elements in each input array).
 
-  `gal_box_border_from_center' now accepts an array of coordinates as one
+  'gal_box_border_from_center' now accepts an array of coordinates as one
   argument and the number of dimensions as another. This allows it to work
   on any dimensionality.
 
-  `gal_fits_img_info' now also returns the name and units of the dataset
+  'gal_fits_img_info' now also returns the name and units of the dataset
   (if they aren't NULL). So it takes two extra arguments.
 
-  `gal_wcs_pixel_scale' now replaces the old `gal_wcs_pixel_scale_deg',
+  'gal_wcs_pixel_scale' now replaces the old 'gal_wcs_pixel_scale_deg',
   since it doesn't only apply to degrees. The pixel scale units are defined
   by the units of the WCS.
 
-  `GAL_TILE_PARSE_OPERATE' (only when `OTHER' is given) can now parse and
+  'GAL_TILE_PARSE_OPERATE' (only when 'OTHER' is given) can now parse and
   operate on different datasets independent of the size of allocated block
-  of memory (the tile sizes of `IN' and `OTHER' have to be identical, but
+  of memory (the tile sizes of 'IN' and 'OTHER' have to be identical, but
   not their allocated blocks of memory). Until now, it was necessary for
   the two blocks to have the same size and this is no longer the case.
 
@@ -1239,7 +1910,7 @@ See the end of the file for license conditions.
 * Noteworthy changes in release 0.3 (library 1.0.0) (2017-06-01) [stable]
 
   This is a full re-write of Gnuastro. Most importantly, Gnuastro now has a
-  new generic data container (`gal_data_t'). This new container can now
+  new generic data container ('gal_data_t'). This new container can now
   deal natively with all standard numeric data types, work in RAM or
   HDD/SSD, keep data in any dimensions and has enabled many other very
   useful features. Some of the most prominent of the new features are
@@ -1249,9 +1920,9 @@ See the end of the file for license conditions.
 
   As discussed below, some program names have changed, if you have a
   previous version of Gnuastro installed from source, it is recommended to
-  uninstall it first (with `make uninstall' using the corresponding
+  uninstall it first (with 'make uninstall' using the corresponding
   tarball), then install this new version. Building Gnuastro can be slow,
-  so please build in parallel with Make's `-j8' option (to build on 8
+  so please build in parallel with Make's '-j8' option (to build on 8
   threads).
 
 ** New programs or library features
@@ -1263,10 +1934,10 @@ See the end of the file for license conditions.
   functions are shown below, also see the "Library demos" section of the
   book for some complete working example:
 
-     -- `gal_table_read' and `gal_table_write' will read and write data to
+     -- 'gal_table_read' and 'gal_table_write' will read and write data to
         plain text, FITS ASCII and FITS Binary formats.
 
-     -- `gal_fits_img_read' and `gal_fits_img_write' can read a FITS image
+     -- 'gal_fits_img_read' and 'gal_fits_img_write' can read a FITS image
         to memory or write a FITS image from memory.
 
   Gnuastro now defines a simple comment line format to keep basic
@@ -1285,24 +1956,24 @@ See the end of the file for license conditions.
   in a FITS file along with basic information. It can copy a whole HDU to
   another file, or delete a HDU from a FITS file. To get the previous
   behavior of listing all the keywords in a FITS HDU, you can run it with
-  the `-p' option.
+  the '-p' option.
 
   All programs now write data into the second HDU of a FITS file to allow a
   clean first HDU. Note that following CFITSIO, HDU counting still starts
   from zero, so FITS images and tables written by Gnuastro in a new file
-  can always be accessed with the `--hdu=1' option (which is now the
+  can always be accessed with the '--hdu=1' option (which is now the
   default).
 
   If any program is run within a Git version controlled directory, a
-  `COMMIT' header keyword will be added to the created FITS files, see the
+  'COMMIT' header keyword will be added to the created FITS files, see the
   "Output headers" section of the book for a discussion on the usefulness
   of this new feature.
 
   BuildProgram: a new program to easily compile, link and run a C program
   you have written with Gnuastro's libraries without having to worry about
   which libraries (Gnuastro dependencies) your program needs. Debugging
-  (`-g'), optimizations (`-O'), warnings (`-W'), include search path
-  (`-I'), link search path (`-L'), and linked libraries (`-l') compiler
+  ('-g'), optimizations ('-O'), warnings ('-W'), include search path
+  ('-I'), link search path ('-L'), and linked libraries ('-l') compiler
   options are also supported. BuildProgram will greatly facilitate the easy
   usage of Gnuastro's libraries.
 
@@ -1322,7 +1993,7 @@ See the end of the file for license conditions.
 
   All Gnuastro programs that read and write tables can now do so in plain
   text table format or in FITS ASCII or FITS Binary tables. Depending on
-  the filename or with the new `--tableformat' common option to all
+  the filename or with the new '--tableformat' common option to all
   programs.
 
   The option management system in all Gnuastro programs has been completely
@@ -1330,18 +2001,18 @@ See the end of the file for license conditions.
   listed below. For developers, you will notice that there is no more usage
   of macros and adding new options has become much more easier.
 
-     -- All programs will now also look for a `gnuastro.conf' configuration
+     -- All programs will now also look for a 'gnuastro.conf' configuration
         file to keep common options for all programs in every directory.
 
-     -- The `--lastconfig' option can be used on the command-line or in any
+     -- The '--lastconfig' option can be used on the command-line or in any
         configuration file to stop parsing any further configuration files.
 
-     -- The `--config' option can now be used to identify any arbitrary
+     -- The '--config' option can now be used to identify any arbitrary
         file to be parsed as a configuration file. Any file that is given
         to this option is parsed immediately.
 
-     -- The `--printparams' option now also prints the short documentation
-        of each option (same description in `--help') after its value.
+     -- The '--printparams' option now also prints the short documentation
+        of each option (same description in '--help') after its value.
 
   It is now possible to choose columns in tables based on column names as
   well as column numbers. It is also possible to search for columns based
@@ -1349,22 +2020,22 @@ See the end of the file for license conditions.
   counting now starts from 1 (one), not 0 (as before). See the new
   "Selecting table columns" section for more on these new features.
 
-  Where relevant, all programs now accept a `--type' option that you can
+  Where relevant, all programs now accept a '--type' option that you can
   use to specify the numerical datatype of the output.
 
-  With the new common option `--minmapsize', you can specify a minimum size
+  With the new common option '--minmapsize', you can specify a minimum size
   of an array (in bytes) to store data in SSD/HDD and not in RAM. This can
   be instrumental when you are dealing with large datasets, or even smaller
   ones, but when your RAM is getting full.
 
   Making a log file is now optional and users have to explicitly ask for it
-  with the `--log' option.
+  with the '--log' option.
 
   Slower building of Gnuastro: Binary operators (e.g., plus or multiply)
   are now done in the native type of the input dataset. Doing so for all
   the different combinations of types, greatly slows down the initial
-  compilation of Gnuastro (after running `make'). So for every type there
-  is now a `--enable-bin-op-*' configure time option. When the dataset's
+  compilation of Gnuastro (after running 'make'). So for every type there
+  is now a '--enable-bin-op-*' configure time option. When the dataset's
   type isn't compiled (only for the binary operators), it will be converted
   to a compiled type and then converted back in the end.
 
@@ -1379,9 +2050,9 @@ See the end of the file for license conditions.
   and the modulo operator).
 
   ConvertType: can also print the input dataset to the command-line
-  (`stdout'). To use this feature set the output filename to `stdout'.
+  ('stdout'). To use this feature set the output filename to 'stdout'.
 
-  Convolve now has the `--minsharpspec' option to specify the minimum
+  Convolve now has the '--minsharpspec' option to specify the minimum
   spectrum value to use in deconvolution (matching PSFs).
 
   Crop: when in WCS mode it can still only work on aligned images. However,
@@ -1400,19 +2071,19 @@ See the end of the file for license conditions.
   pixel projected size, fully derived in the book), it now also reports the
   surface brightness in magnitudes/arcsec^2 also.
 
-  MakeProfiles: Profile codes now start from `1' (until now they started
-  from `0').
+  MakeProfiles: Profile codes now start from '1' (until now they started
+  from '0').
 
   MakeProfiles: now accepts the radial function of profiles as
   human-readable strings instead of a code for each profile (which was very
   cryptic, although codes are also still acceptable). For example in the
-  profile column you can now write `sersic' instead of the code `1'.
+  profile column you can now write 'sersic' instead of the code '1'.
 
-  NoiseChisel: the new `--cleandilated' option will remove dilated objects
+  NoiseChisel: the new '--cleandilated' option will remove dilated objects
   that have a low S/N (it is mainly useful on very clean or mock images).
   For non-clean noise, it will result in a decrease of completeness. With
   this new option, NoiseChisel will also print detection S/N values when
-  run with the `--checkdetsn' option.
+  run with the '--checkdetsn' option.
 
   Statistics: now reads table columns as well as images and does basic
   operations on them. It can also only work on a certain range of the data
@@ -1426,17 +2097,17 @@ See the end of the file for license conditions.
   mode's quantile similar to what NoiseChisel does to find its initial
   threshold.
 
-  Statistics: has several new single valued calculations: `--quantile',
-  `--quantfunc' (quantile function), `--mode', `--modequant', `--modesym',
-  and `--modesymvalue'.
+  Statistics: has several new single valued calculations: '--quantile',
+  '--quantfunc' (quantile function), '--mode', '--modequant', '--modesym',
+  and '--modesymvalue'.
 
-  Warp: align the image with the celestial coordinates using the `--align'
+  Warp: align the image with the celestial coordinates using the '--align'
   option.
 
   Warp: standard modular warpings can now be requested without an input
-  matrix, using the following options: `--shear', `--flip', `--project',
-  `--rotate', `--scale', `--translate'. Any number of these transformations
-  (along with the `--align' option) can be called on the command-line and
+  matrix, using the following options: '--shear', '--flip', '--project',
+  '--rotate', '--scale', '--translate'. Any number of these transformations
+  (along with the '--align' option) can be called on the command-line and
   they will be applied in the same order to create one warping matrix. By
   default the WCS will also be corrected.
 
@@ -1444,49 +2115,49 @@ See the end of the file for license conditions.
 
   Mask image options have been removed from all programs. Instead, all
   programs can work directly on data with blank values. So when some pixels
-  must be masked, the Arithmetic program's `where' operator can be used to
+  must be masked, the Arithmetic program's 'where' operator can be used to
   select special pixels and set them to blank. In particular bit-wise
   operations are now available in Arithmetic to use bit-mask
   images. Managing all these different choices in every program would only
   confuse the user (with too many options).
 
-  Arithmetic: the `x' letter is now used to represent the multiplication
-  operator. Previously it was `*' which needed quotation and was thus very
+  Arithmetic: the 'x' letter is now used to represent the multiplication
+  operator. Previously it was '*' which needed quotation and was thus very
   inconvenient.
 
-  Convolve: the old `--frequency' and `--spatial' options have been removed
-  and are replaced by `--domain' which accepts values of `frequency' and
-  `spatial'.
+  Convolve: the old '--frequency' and '--spatial' options have been removed
+  and are replaced by '--domain' which accepts values of 'frequency' and
+  'spatial'.
 
-  Convolve: the old `--viewfreqsteps' was changed to `--checkfreqsteps' to
+  Convolve: the old '--viewfreqsteps' was changed to '--checkfreqsteps' to
   fit with the general style of such check images in all Gnuastro's
   programs.
 
-  Crop: `--section' syntax is now inclusive in both bounds.
+  Crop: '--section' syntax is now inclusive in both bounds.
 
   Crop: only checks if the center of a crop is filled when the crop was
-  defined by its center (for example with `--ra' and `--dec'). The verbose
+  defined by its center (for example with '--ra' and '--dec'). The verbose
   outputs of Crop are also not cryptic 0s or 1s. The are human readable
   text.
 
-  Crop: doesn't have separate `--imgmode' and `--wcsmode' options any
-  more. There is now a single `--mode' option which accepts values of `img'
-  or `wcs'.
+  Crop: doesn't have separate '--imgmode' and '--wcsmode' options any
+  more. There is now a single '--mode' option which accepts values of 'img'
+  or 'wcs'.
 
-  MakeProfiles: the old `--inputascanvas' is now called `--clearcanvas'.
+  MakeProfiles: the old '--inputascanvas' is now called '--clearcanvas'.
 
   MakeProfiles: until now, it would abort with an error when the input
   columns had blank values. But for masking, it might happen that you set a
   blank magnitude. So this check has now been removed when reading the
   magnitude column.
 
-  NoiseChisel: default value of the `--minskyfrac' option (new name for the
-  old `--minbfrac') is now 0.7 as opposed to 0.5. This will allow much
+  NoiseChisel: default value of the '--minskyfrac' option (new name for the
+  old '--minbfrac') is now 0.7 as opposed to 0.5. This will allow much
   better estimation of noise properties (by default). It may be slightly
   too high for a crowded field, but the users can change it on the
   command-line (or in a configuration file) for such datasets.
 
-  NoiseChisel: when it is run with any of the `--check' options, it will
+  NoiseChisel: when it is run with any of the '--check' options, it will
   abort after all the check images have been created. This is very useful
   for checking your parameters until each step and not be distracted (or
   have to wait) for later steps to finish.
@@ -1495,35 +2166,35 @@ See the end of the file for license conditions.
   calculate sigma-clipped results by default (with no options). It will
   just print some basic information.
 
-  Table: Previously, if a column was requested, the `-i' option would be
+  Table: Previously, if a column was requested, the '-i' option would be
   ignored. But it often happens that the users forget a column name after
   already typing several of their desired columns. So the opposite behavior
   is preferred. Because when more than a couple of columns are needed, you
   will probably forget the column identifiers of the last few and having to
-  retype everything is very frustrating. Something like how `--help' takes
+  retype everything is very frustrating. Something like how '--help' takes
   precedence over all other options.
 
   Table: to select column(s) by regular expression searching, the name now
-  has to be put in `/ /' (similar to AWK). If a value isn't in `/ /', the
+  has to be put in '/ /' (similar to AWK). If a value isn't in '/ /', the
   programs will only select a column with the exact match.
 
   Warp: when a 2 by 2 matrix is given, the FITS pixel positions (which
   define the center of a pixel as an integer) are automatically implemented
   internally, see "Invoking Warp" in the manual for more.
 
-  Warp: the old `--nofitscorrect' option has been changed to
-  `--centeroncorner' to be more clear. The new option is now more general
+  Warp: the old '--nofitscorrect' option has been changed to
+  '--centeroncorner' to be more clear. The new option is now more general
   than before and also works on warping with a matrix, not just on modular
   warpings.
 
-  Warp: the old `--nowcscorrection' option has been given a more clear name
-  of `--keepwcs'. With this option, Warp will not apply the warp the
+  Warp: the old '--nowcscorrection' option has been given a more clear name
+  of '--keepwcs'. With this option, Warp will not apply the warp the
   input's WCS structure.
 
-  Warp: the old `--maxblankfrac' option has been changed to
-  `--coveredfrac'. Until now, Warp would only look for the fraction of
+  Warp: the old '--maxblankfrac' option has been changed to
+  '--coveredfrac'. Until now, Warp would only look for the fraction of
   input blank/NaN pixel area over the output pixel. But this would be
-  useless on the edges of the image. So the new `--coveredfrac' option
+  useless on the edges of the image. So the new '--coveredfrac' option
   takes the acceptable fraction of output pixel area that must be covered
   by input pixels in order to give that output pixel a value. You can use
   this to set edge pixels that are not fully covered in the new grid to
@@ -1531,7 +2202,7 @@ See the end of the file for license conditions.
 
 ** Bugs fixed
 
-  Using `%zu' to print `size_t' variables for clean build on 32-bit
+  Using '%zu' to print 'size_t' variables for clean build on 32-bit
   systems.
 
   Crash in Table for some operating systems due to memory is now fixed (bug
@@ -1584,12 +2255,12 @@ See the end of the file for license conditions.
 
   Shared libraries and headers are now installed. The libraries can be used
   in C and C++ programs. This release includes the following headers:
-  `gnuastro.h', `array.h', `box.h', `fits.h', `linkedlist.h', `mesh.h',
-  `polygon.h', `qsort.h', `spatialconvolve.h', `statistics.h', `threads.h',
-  `wcs.h', `txtarray.h' (task #13765).
+  'gnuastro.h', 'array.h', 'box.h', 'fits.h', 'linkedlist.h', 'mesh.h',
+  'polygon.h', 'qsort.h', 'spatialconvolve.h', 'statistics.h', 'threads.h',
+  'wcs.h', 'txtarray.h' (task #13765).
 
   Gnuastro now comes with a script in its top source directory
-  (`tmpfs-config-make') to configure and build it in the tmpfs (on the
+  ('tmpfs-config-make') to configure and build it in the tmpfs (on the
   RAM), for those systems that have it. See the new "Configure and build in
   RAM" section in the book for more (task #14100).
 
@@ -1598,27 +2269,27 @@ See the end of the file for license conditions.
   MakeProfiles also accepts WCS positions (task #13566).
 
   Flat profiles in MakeProfiles can be given a profile specific value. The
-  new `--mforflatpix' option MakeProfile will use the value in the
+  new '--mforflatpix' option MakeProfile will use the value in the
   magnitude column as a fixed value for each pixel. This can be very useful
   in defining a mask, or creating segmentation maps or labeled images (task
   #14115).
 
   MakeProfiles can now use input image as canvas. Instead of specifying the
-  WSC and image size parameters manually. With the new `--inputascanvas'
+  WSC and image size parameters manually. With the new '--inputascanvas'
   option, MakeProfiles will get this information (along with blank pixels)
   from an already existing image (task #14116).
 
   Type of output in MakeProfiles and Arithmetic can be specified with the
-  `--type' option.
+  '--type' option.
 
-  Magnitude error column in MakeCatalog with the `--magnitudeerr' option.
+  Magnitude error column in MakeCatalog with the '--magnitudeerr' option.
 
   Arithmetic now has new conditional (task #13870) and logical operators
   (task #14153) along with an operator for actions only when conditions are
-  true: `where'. The new `isblank' operator will also enable you to select
+  true: 'where'. The new 'isblank' operator will also enable you to select
   blank, or masked, pixels (task #14146).
 
-  The `--noerodequant' in NoiseChisel enables it to detect small and
+  The '--noerodequant' in NoiseChisel enables it to detect small and
   sharper profiles by disabling erosion on pixels above a certain quantile
   (task #14139).
 
@@ -1631,10 +2302,10 @@ See the end of the file for license conditions.
 
 ** Changes in behavior
 
-  The two MakeProfiles options `--setconsttonan', `--setconsttomin' have
-  been removed (see `--mforflatpix' above for their alternative).
+  The two MakeProfiles options '--setconsttonan', '--setconsttomin' have
+  been removed (see '--mforflatpix' above for their alternative).
 
-  MakeCatalog makes clump catalog only when asked (when the `WCLUMPS'
+  MakeCatalog makes clump catalog only when asked (when the 'WCLUMPS'
   header exists in the objects HDU). This can be very useful in cases like
   aperture photometry, when the object labels are not generated by
   NoiseChisel and so a clump image doesn't exist (task #14122).
@@ -1642,7 +2313,7 @@ See the end of the file for license conditions.
   Default cosmological parameters in CosmicCalculator set to Plank 2015
   results: A&A (2016), 594, A13 (arXiv 1502.01589).
 
-  The `--envseed' option (to read random number generator type and seed
+  The '--envseed' option (to read random number generator type and seed
   from the environment) to MakeProfiles and MakeNoise can also be given in
   the configuration files.
 
@@ -1657,24 +2328,24 @@ See the end of the file for license conditions.
   MakeCatalog's problem in checking the sizes of all input images is now
   fixed.
 
-  NoiseChisel's problem with reading the `--kernel' option is now
+  NoiseChisel's problem with reading the '--kernel' option is now
   corrected (bug #46750).
 
   lib/mesh.c's problem in correctly calculating the mesh sizes was
   corrected (bug #47611).
 
-  `make check' will not look into system utility configuration files. In
+  'make check' will not look into system utility configuration files. In
   the previous release, if Gnuastro was already installed, the
   configuration files already present on the system would also be read. Now
   only configuration files in the tested package are used (bug #47833).
 
   Ghostscript's version is now checked at configure time after its
-  existence. ConvertType uses the `-dPDFFitPage' option to Ghostscript
+  existence. ConvertType uses the '-dPDFFitPage' option to Ghostscript
   which was introduced in version 9.10, so older versions would pass
-  configure but at `make check' time, the PDF test would fail. Now this
+  configure but at 'make check' time, the PDF test would fail. Now this
   test is skipped (bug #47868).
 
-  Most tests would fail when `make check -jN' was run (to do the checks on
+  Most tests would fail when 'make check -jN' was run (to do the checks on
   N threads). A dependency structure has now been defined to fix this
   problem and greatly speed up the testing process (bug #47957).
 
@@ -1693,32 +2364,32 @@ See the end of the file for license conditions.
 
 ** New features
 
-  All the utilities that would produce a log file now have a `--nolog'
+  All the utilities that would produce a log file now have a '--nolog'
   option to avoid printing a log file.
 
-  The tiled image compression convention (.fits.fz, created with `fpack')
+  The tiled image compression convention (.fits.fz, created with 'fpack')
   files can now be used as input in the utilities.
 
   ImageCrop can now also crop a polygon from the input image. The polygon
   vertices can be given in the world or image coordinates. The simple
-  `--polygon' option will keep the insides of the polygon while the
-  `--outpolygon' will keep the outside of the polygon.
+  '--polygon' option will keep the insides of the polygon while the
+  '--outpolygon' will keep the outside of the polygon.
 
   ImageCrop and ImageWarp can now read the WCS information of a FITS header
-  from a specific region with the `--hstartwcs' and `--hendwcs'. In some
+  from a specific region with the '--hstartwcs' and '--hendwcs'. In some
   older FITS images, when the WCS distortions were not as standardized as
   now, there were cases which would confuse WCSLIB.
 
   NoiseChisel can now save the grown clumps image instead of the original
-  clumps image in the output with the `--grownclumps' option.
+  clumps image in the output with the '--grownclumps' option.
 
-  Convolve can now do deconvolution with the `--makekernel' option.
+  Convolve can now do deconvolution with the '--makekernel' option.
 
-  MakeProfiles now has a `--setconsttonan' option which will fill the
+  MakeProfiles now has a '--setconsttonan' option which will fill the
   constant profiles with a NaN (blank) value, not a number, allowing the
   creations of elliptical masked regions for example.
 
-  Header can now import a keyword directly from a string with the `--asis'
+  Header can now import a keyword directly from a string with the '--asis'
   option.
 
   MakeCatalog can now output the geometric (average position independent of
@@ -1728,11 +2399,11 @@ See the end of the file for license conditions.
   example semi-major axis, semi-minor axis, and position angle). This can
   also be done both in standard flux weighted and geometric methods too.
 
-  MakeCatalog now has a `--threshold' function to only use pixels above a
+  MakeCatalog now has a '--threshold' function to only use pixels above a
   given threshold in each object or clump. This is useful to avoid diffuse
   regions in calculations.
 
-  MakeCatalog now has a `--noriverbrightness' option. With this option it
+  MakeCatalog now has a '--noriverbrightness' option. With this option it
   is possible to calculate the clump flux without subtracting the river
   pixels on its circumference.
 
@@ -1740,7 +2411,7 @@ See the end of the file for license conditions.
   determined at run-time for each program. Therefore it is now easily
   possible to built Gnuastro on one system to use on another (commonly done
   in the GNU/Linux package managers). Therefore ./configure no longer has a
-  `--with-numthreads' option.
+  '--with-numthreads' option.
 
   Every commit in Gnuastro's history (after implementing this feature) can
   now be given a unique version number. Since the version number is printed
@@ -1751,14 +2422,14 @@ See the end of the file for license conditions.
   controlled history. Also all the authors that have contributed to
   Gnuastro are included in the second (copyright) page of the PDF book.
 
-  All the bootstrapped directories are now moved with a new `bootstrapped'
+  All the bootstrapped directories are now moved with a new 'bootstrapped'
   directory in the top source directory. This significantly cleans up this
   directory, allowing users to more easily find the hand-written Gnuastro
   source files they like.
 
-  A `bug-gnuastro' Info page was created so users can easily go to that
+  A 'bug-gnuastro' Info page was created so users can easily go to that
   page for information on how to submit bug reports. It is accessible on
-  the command line with the command `info bug-gnuastro'.
+  the command line with the command 'info bug-gnuastro'.
 
 ** Changes in behavior
 
@@ -1776,8 +2447,8 @@ See the end of the file for license conditions.
   NoiseChisel no longer outputs a sky subtracted image. This job can now be
   done with the new Arithmetic utility.
 
-  NoiseChisel's `--segsnhistnbins' option was renamed to
-  `--clumpsnhistnbins'.
+  NoiseChisel's '--segsnhistnbins' option was renamed to
+  '--clumpsnhistnbins'.
 
 ** Improvements
 
@@ -1790,8 +2461,8 @@ See the end of the file for license conditions.
   were commonly not enough points in large mesh sizes and this would add
   scatter. With ImageCrop's polygon capabilities, it is now easily possible
   to cut out the region that has uniform noise properties (depth and
-  correlated noise). Therefore the old `--checkdetectionsn' and
-  `checkclumpsn' options are no longer present.
+  correlated noise). Therefore the old '--checkdetectionsn' and
+  'checkclumpsn' options are no longer present.
 
   When building from the version controlled source, the whole bootstrapping
   process is done with one script. In the previous version, all the
@@ -1799,7 +2470,7 @@ See the end of the file for license conditions.
   old manual).
 
   All the build steps now report what was done and suggest the next
-  step. This feature can be disabled with the `--disable-guide-message' at
+  step. This feature can be disabled with the '--disable-guide-message' at
   configure time.
 
 
@@ -1807,7 +2478,7 @@ See the end of the file for license conditions.
 
 
 * Copyright notice
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
diff --git a/README b/README
index de215bf..03b6b93 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 GNU Astronomy Utilities
 =======================
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
 GNU Astronomy Utilities (Gnuastro) is an official GNU package of programs
@@ -85,6 +85,10 @@ categories/chapters.
     in noise (galaxies in the sky), using thresholds that are below the Sky
     value (see arXiv:1505.01664).
 
+  - Query (astquery): High-level interface to query pre-defined remote, or
+    external, databases and directly download the required sub-tables on the
+    command-line.
+
   - Segment (astsegment): Segment a detection based on the structure of
     signal within it.
 
@@ -102,9 +106,13 @@ categories/chapters.
 The programs listed above are designed to be highly modular and
 generic. For higher-level operations (combining multiple programs, or
 running a program in a special way), Gnuastro also installs Bash scripts
-(all prefixed with `astscript-'). They can be run like a program and behave
+(all prefixed with 'astscript-'). They can be run like a program and behave
 very similarly (with minor differences, as explained in the book).
 
+  - astscript-make-ds9-reg: Given a table (either as a file or from
+    standard input), create an SAO DS9 region file from the requested
+    positional columns (WCS or image coordinates).
+
   - astscript-sort-by-night: Given a list of FITS files, and a HDU and
     keyword name for a date, this script separates the files in the same
     night (possibly over two calendar days).
@@ -150,7 +158,7 @@ The optional dependencies are:
 
 See the "Dependencies" section of the book for their detailed installation
 guides and optional dependencies to enable extra features. Prior to
-installation, you can find it in the `doc/gnuastro.texi' file (source of
+installation, you can find it in the 'doc/gnuastro.texi' file (source of
 the book), or on the web:
 
   https://www.gnu.org/software/gnuastro/manual/html_node/Dependencies.html
@@ -171,7 +179,7 @@ the standard GNU Build system as shown below. After the 
'./configure'
 command, Gnuastro will print messages upon the successful completion of
 each step, giving further information and suggestions for the next steps.
 
-    tar xf gnuastro-latest.tar.lz        # Also works for `tar.gz' files
+    tar xf gnuastro-latest.tar.lz        # Also works for 'tar.gz' files
     cd gnuastro-X.X
     ./configure
     make
@@ -194,7 +202,7 @@ To access the appropriate section of the Gnuastro 
book/documentation from
 your command-line (in the middle of your work, without distracting your
 self by having to move your hand off the keyboard), please run any of the
 following two commands. Note that you can leave the Info environment by
-pressing the key `q'.
+pressing the key 'q'.
 
     info ProgramName               # For example 'info NoiseChisel'
     info astprogname               # For example 'info astnoisechisel'
@@ -223,7 +231,7 @@ Reporting bugs
 
 The most effective way to report bugs is explained in the "Report a bug"
 section of the documentation, after installation, you can read it by
-running (leave the Info environment by pressing the `q' key afterwards):
+running (leave the Info environment by pressing the 'q' key afterwards):
 
     info bug-gnuastro
 
@@ -249,9 +257,9 @@ bug for a shorter and more relevant list to look into.
 
 Copyright information
 ---------------------
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
 version published by the Free Software Foundation; with no Invariant
-Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
\ No newline at end of file
+Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
diff --git a/README-hacking b/README-hacking
index 97648a5..322325f 100644
--- a/README-hacking
+++ b/README-hacking
@@ -1,7 +1,7 @@
 Hacking into GNU Astronomy Utilities
 ====================================
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
 This file is intended for those who are building and installing the version
@@ -50,7 +50,7 @@ already present in your operating system's package management 
system.
     the bootstrap script automatically. However, if you don't already have
     them, we recommed to clone them separately as discussed below. They
     should be in the same directory (let's call it 'DEVDIR', can be any
-    directory). These packages are regularly updated, so run `git pull'
+    directory). These packages are regularly updated, so run 'git pull'
     within the cloned directories to keep them up to date before
     bootstrapping. See the "Bootstrapping dependencies" and "Bootstrapping"
     sections of the book for more information.
@@ -114,9 +114,9 @@ be sure to keep this file where ever you want to run 
'bootstrap'.
 Copyright information
 ---------------------
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
 version published by the Free Software Foundation; with no Invariant
-Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
\ No newline at end of file
+Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
diff --git a/THANKS b/THANKS
index 0de4197..6d36246 100644
--- a/THANKS
+++ b/THANKS
@@ -1,14 +1,14 @@
 GNU Astronomy Utilities Acknowledgments
 =======================================
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
 The following people and institutions who contributed to Gnuastro
 indirectly (not by actually submitting code) are listed here. For the list
 of Gnuastro code/documentation authors (people who have contributed actual
 code/text as commits in the version controlled history of Gnuastro), please
-see the `AUTHORS' file in the same directory.
+see the 'AUTHORS' file in the same directory.
 
 
 People
@@ -20,6 +20,7 @@ support in Gnuastro. The list is ordered alphabetically (by 
family name).
 
     Valentina Abril-melgarejo            valentina.abril@lam.fr
     Marjan Akbari                        mrjakbari@gmail.com
+    Carlos Allende Prieto                callende@iac.es
     Hamed Altafi                         hamed.altafi2@gmail.com
     Roland Bacon                         roland.bacon@univ-lyon1.fr
     Roberto Baena Gallé                  rbaena@iac.es
@@ -27,9 +28,11 @@ support in Gnuastro. The list is ordered alphabetically (by 
family name).
     Karl Berry                           karl@gnu.org
     Leindert Boogaard                    boogaard@strw.leidenuniv.nl
     Nicolas Bouché                       nicolas.bouche@irap.omp.eu
+    Stefan Brüns                         stefan.bruens@rwth-aachen.de
     Fernando Buitrago                    fbuitrago@oal.ul.pt
     Adrian Bunk                          bunk@debian.org
     Rosa Calvi                           rcalvi@iac.es
+    Mark Calabretta                      mcalabre@atnf.csiro.au
     Nushkia Chamba                       chamba@iac.es
     Benjamin Clement                     benjamin.clement@univ-lyon1.fr
     Nima Dehdilani                       nimadehdilani@gmail.com
@@ -37,7 +40,9 @@ support in Gnuastro. The list is ordered alphabetically (by 
family name).
     Alexey Dokuchaev                     danfe@freebsd.org
     Pierre-Alain Duc                     pierre-alain.duc@astro.unistra.fr
     Elham Eftekhari                      elhamea@iac.es
+    Paul Eggert                          eggert@cs.ucla.edu
     Gaspar Galaz                         ggalaz@astro.puc.cl
+    Andrés García-Serra Romero           alu0101451923@ull.edu.es
     Thérèse Godefroy                     godef.th@free.fr
     Madusha Gunawardhana                 gunawardhana@strw.leidenuniv.nl
     Bruno Haible                         bruno@clisp.org
@@ -52,22 +57,36 @@ support in Gnuastro. The list is ordered alphabetically (by 
family name).
     Mohammad-Reza Khellat                moha.khe@gmail.com
     Johan Knapen                         jhk@iac.es
     Geoffry Krouchi                      geoffrey.krouchi@etu.univ-lyon1.fr
+    Martin Kuemmel                       mkuemmel@usm.lmu.de
     Floriane Leclercq                    floriane.leclercq@univ-lyon1.fr
     Alan Lefor                           alefor@astr.tohoku.ac.jp
+    Javier Licandro                      jlicandr@iac.es
     Sebastián Luna Valero                sluna@iaa.es
+    Alberto Madrigal                     brt.madrigal@gmail.com
     Guillaume Mahler                     guillaume.mahler@univ-lyon1.fr
+    Alireza Molaeinezhad                 amolaei@gmail.com
+    Javier Moldon                        jmoldon@iaa.es
     Juan Molina Tobar                    juan.a.molina.t@gmail.com
     Francesco Montanari                  francesco.montanari@openmailbox.org
+    Raphael Morales                      rmorales@iaa.es
+    Carlos Morales Socorro               cmorsoc@gmail.com
+    Sylvain Mottet                       mottet@iap.fr
     Dmitrii Oparin                       doparin2@gmail.com
     Bertrand Pain                        bertrand.pain@inserm.fr
     William Pence                        william.pence@nasa.gov
     Mamta Pommier                        mamta.pommier@univ-lyon1.fr
+    Marcel Popescu                       mpopescu@iac.es
     Bob Proulx                           bob@proulx.com
+    Joseph Putko                         josephputko@gmail.com
+    Samane Raji                          samaneraji@gmail.com
+    Francois Ochsenbein                  francois.ochsenbein@gmail.com
     Teymoor Saifollahi                   teymur.saif@gmail.com
+    Joanna Sakowska                      js01093@surrey.ac.uk
     Elham Saremi                         saremi@ipm.ir
     Yahya Sefidbakht                     y.sefidbakht@gmail.com
     Alejandro Serrano Borlaff            asborlaff@ucm.es
     Zahra Sharbaf                        samaeh.sharbaf2@yahoo.com
+    David Shupe                          shupe@ipac.caltech.edu
     Jenny Sorce                          jenny.sorce@univ-lyon1.fr
     Lee Spitler                          lee.spitler@mq.edu.au
     Richard Stallman                     rms@gnu.org
@@ -82,6 +101,7 @@ support in Gnuastro. The list is ordered alphabetically (by 
family name).
     Aaron Watkins                        aaron.watkins@oulu.fi
     Michael H.F. Wilkinson               m.h.f.wilkinson@gmail.com
     Christopher Willmer                  cnaw@as.arizona.edu
+    Xiuqin Wu                            xiuqin@ipac.caltech.edu
     Sara Yousefi Taemeh                  s.yousefi.t@gmail.com
     Johannes Zabl                        johannes.zabl@irap.omp.eu
 
@@ -104,14 +124,15 @@ Host institutions of Gnuastro's developers.
     University of Salento, Lecce, Italy.
     Centre de Recherche Astrophysique de Lyon (CRAL), Lyon, France.
     Instituto de Astrofisica de Canarias (IAC), Tenerife, Spain.
+    Google Summer of Code(GSoC).
 
 
 Copyright
 ---------
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
 version published by the Free Software Foundation; with no Invariant
-Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
\ No newline at end of file
+Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
diff --git a/bin/TEMPLATE/Makefile.am b/bin/TEMPLATE/Makefile.am
index ee006da..dec0ad8 100644
--- a/bin/TEMPLATE/Makefile.am
+++ b/bin/TEMPLATE/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2016-2019, Free Software Foundation, Inc.
+## Copyright (C) 2016-2021, 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
@@ -20,24 +20,23 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astTEMPLATE
 
-## We are linking with `libgnu' first eventhough `libgnu' is already linked
-## to `libgnuastro'. The reason is that some linkers (Mac OS for example),
+## We are linking with 'libgnu' first eventhough 'libgnu' is already linked
+## to 'libgnuastro'. The reason is that some linkers (Mac OS for example),
 ## don't keep external variables (needed in Argp) after the first link. So
-## the `libgnu' (that is indirectly linked through `libgnuastro') can't see
-## those variables. We thus need to explicitly link with `libgnu' first.
-astTEMPLATE_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                    $(MAYBE_NORPATH)
+## the 'libgnu' (that is indirectly linked through 'libgnuastro') can't see
+## those variables. We thus need to explicitly link with 'libgnu' first.
+astTEMPLATE_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                    -lgnuastro $(CONFIG_LDADD)
 
 astTEMPLATE_SOURCES = main.c ui.c TEMPLATE.c
 
diff --git a/bin/TEMPLATE/TEMPLATE.c b/bin/TEMPLATE/TEMPLATE.c
index 69108aa..751efd9 100644
--- a/bin/TEMPLATE/TEMPLATE.c
+++ b/bin/TEMPLATE/TEMPLATE.c
@@ -5,7 +5,7 @@ TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/TEMPLATE/TEMPLATE.h b/bin/TEMPLATE/TEMPLATE.h
index e75f578..c716269 100644
--- a/bin/TEMPLATE/TEMPLATE.h
+++ b/bin/TEMPLATE/TEMPLATE.h
@@ -5,7 +5,7 @@ TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/TEMPLATE/args.h b/bin/TEMPLATE/args.h
index 0568f0a..ba47692 100644
--- a/bin/TEMPLATE/args.h
+++ b/bin/TEMPLATE/args.h
@@ -5,7 +5,7 @@ TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/TEMPLATE/astTEMPLATE.conf b/bin/TEMPLATE/astTEMPLATE.conf
index d10efb4..34111fe 100644
--- a/bin/TEMPLATE/astTEMPLATE.conf
+++ b/bin/TEMPLATE/astTEMPLATE.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astTEMPLATE                    # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/TEMPLATE/authors-cite.h b/bin/TEMPLATE/authors-cite.h
index 9090793..8243324 100644
--- a/bin/TEMPLATE/authors-cite.h
+++ b/bin/TEMPLATE/authors-cite.h
@@ -5,7 +5,7 @@ TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/TEMPLATE/main.c b/bin/TEMPLATE/main.c
index 22757cb..30c7104 100644
--- a/bin/TEMPLATE/main.c
+++ b/bin/TEMPLATE/main.c
@@ -5,7 +5,7 @@ TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/TEMPLATE/main.h b/bin/TEMPLATE/main.h
index c3f8a08..6c3836f 100644
--- a/bin/TEMPLATE/main.h
+++ b/bin/TEMPLATE/main.h
@@ -5,7 +5,7 @@ TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/TEMPLATE/ui.c b/bin/TEMPLATE/ui.c
index 717bf17..6d96430 100644
--- a/bin/TEMPLATE/ui.c
+++ b/bin/TEMPLATE/ui.c
@@ -5,7 +5,7 @@ TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -143,18 +143,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct TEMPLATEparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -202,7 +202,7 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct TEMPLATEparams *p)
 {
@@ -226,8 +226,8 @@ ui_check_options_and_arguments(struct TEMPLATEparams *p)
       /* If it is FITS, a HDU is also mandatory. */
       if( gal_fits_name_is_fits(p->inputname) && p->cp.hdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified. When the input is a FITS "
-              "file, a HDU must also be specified, you can use the `--hdu' "
-              "(`-h') option and give it the HDU number (starting from "
+              "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
@@ -289,9 +289,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
TEMPLATEparams *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
+  /* 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
diff --git a/bin/TEMPLATE/ui.h b/bin/TEMPLATE/ui.h
index 342508e..6b0f8f7 100644
--- a/bin/TEMPLATE/ui.h
+++ b/bin/TEMPLATE/ui.h
@@ -5,7 +5,7 @@ TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/arithmetic/Makefile.am b/bin/arithmetic/Makefile.am
index 22ccfd2..c73400c 100644
--- a/bin/arithmetic/Makefile.am
+++ b/bin/arithmetic/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,21 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
-
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astarithmetic
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
 astarithmetic_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
-                      -lgnuastro $(MAYBE_NORPATH)
+                      -lgnuastro $(CONFIG_LDADD)
 
 astarithmetic_SOURCES = main.c ui.c arithmetic.c operands.c
 
diff --git a/bin/arithmetic/args.h b/bin/arithmetic/args.h
index 788a818..8e765ee 100644
--- a/bin/arithmetic/args.h
+++ b/bin/arithmetic/args.h
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -25,6 +25,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
+
+
 /* Definition of program-specific options. */
 struct argp_option program_options[] =
   {
@@ -33,7 +35,7 @@ struct argp_option program_options[] =
       UI_KEY_GLOBALHDU,
       "STR",
       0,
-      "Use this HDU for all inputs, ignore `--hdu'.",
+      "Use this HDU for all inputs, ignore '--hdu'.",
       GAL_OPTIONS_GROUP_INPUT,
       &p->globalhdu,
       GAL_TYPE_STRING,
@@ -44,7 +46,7 @@ struct argp_option program_options[] =
     {
       "wcsfile",
       UI_KEY_WCSFILE,
-      "STR",
+      "FITS",
       0,
       "File to use for output's WCS.",
       GAL_OPTIONS_GROUP_INPUT,
diff --git a/bin/arithmetic/arithmetic.c b/bin/arithmetic/arithmetic.c
index 82bbba3..3975a7a 100644
--- a/bin/arithmetic/arithmetic.c
+++ b/bin/arithmetic/arithmetic.c
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -77,6 +77,9 @@ pop_number_of_operands(struct arithmeticparams *p, int op, 
char *token_string,
   /* See if this operator needs any parameters. If so, pop them. */
   switch(op)
     {
+    case GAL_ARITHMETIC_OP_QUANTILE:
+      numparams=1;
+      break;
     case GAL_ARITHMETIC_OP_SIGCLIP_STD:
     case GAL_ARITHMETIC_OP_SIGCLIP_MEAN:
     case GAL_ARITHMETIC_OP_SIGCLIP_MEDIAN:
@@ -93,15 +96,15 @@ pop_number_of_operands(struct arithmeticparams *p, int op, 
char *token_string,
          to the list. */
       tmp=operands_pop(p, token_string);
       if(tmp->size>1)
-        error(EXIT_FAILURE, 0, "the %s popped operand of the \"%s\" "
+        error(EXIT_FAILURE, 0, "the %s popped operand of the '%s' "
               "operator must be a single number", cstring, token_string);
       tmp=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
       gal_list_data_add(params, tmp);
 
-      /* A small sanity check (none of the parameters for sigma-clipping
-         can be negative.. */
+      /* A small sanity check (none of the parameters for sigma-clipping,
+         or quantile estimation can be negative. */
       if( ((float *)(tmp->array))[0]<=0.0 )
-        error(EXIT_FAILURE, 0, "the %s popped operand of the \"%s\" "
+        error(EXIT_FAILURE, 0, "the %s popped operand of the '%s' "
               "operator cannot be negative", cstring, token_string);
 
       /* Increment the counter string. */
@@ -111,7 +114,7 @@ pop_number_of_operands(struct arithmeticparams *p, int op, 
char *token_string,
   /* Check if its a number. */
   numpop=operands_pop(p, token_string);
   if(numpop->size>1)
-    error(EXIT_FAILURE, 0, "the %s popped operand of the \"%s\" "
+    error(EXIT_FAILURE, 0, "the %s popped operand of the '%s' "
           "operator (number of input datasets) must be a number, not an "
           "array", cstring, token_string);
 
@@ -133,7 +136,7 @@ pop_number_of_operands(struct arithmeticparams *p, int op, 
char *token_string,
     /* Floating point numbers are not acceptable in this context. */
     case GAL_TYPE_FLOAT32:
     case GAL_TYPE_FLOAT64:
-      error(EXIT_FAILURE, 0, "the %s popped operand of the \"%s\" "
+      error(EXIT_FAILURE, 0, "the %s popped operand of the '%s' "
             "operator (number of input datasets) must be an integer type",
             cstring, token_string);
 
@@ -144,7 +147,7 @@ pop_number_of_operands(struct arithmeticparams *p, int op, 
char *token_string,
 
   /* If control reaches here, then the number must have been a negative
      value, so print an error. */
-  error(EXIT_FAILURE, 0, "the %s popped operand of the \"%s\" operator "
+  error(EXIT_FAILURE, 0, "the %s popped operand of the '%s' operator "
         "cannot be zero or a negative number", cstring,
         token_string);
   return 0;
@@ -219,7 +222,7 @@ arithmetic_filter(void *in_prm)
   /* Go over all the pixels that were assigned to this thread. */
   for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
-      /* For easy reading, put the index in `ind'. */
+      /* For easy reading, put the index in 'ind'. */
       ind=tprm->indexs[i];
 
       /* Get the coordinate of the pixel. */
@@ -289,7 +292,7 @@ arithmetic_filter(void *in_prm)
             case ARITHMETIC_OP_FILTER_SIGCLIP_MEDIAN: sind = 1; break;
             default:
               error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at "
-                    "%s to fix the problem. The `afp->operator' value "
+                    "%s to fix the problem. The 'afp->operator' value "
                     "%d is not recognized as sigma-clipped median or "
                     "mean", __func__, PACKAGE_BUGREPORT, afp->operator);
             }
@@ -307,7 +310,7 @@ arithmetic_filter(void *in_prm)
 
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s "
-                "to fix the problem. `afp->operator' code %d is not "
+                "to fix the problem. 'afp->operator' code %d is not "
                 "recognized", PACKAGE_BUGREPORT, __func__,
                 afp->operator);
         }
@@ -494,7 +497,7 @@ wrapper_for_filter(struct arithmeticparams *p, char *token, 
int operator)
 
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to fix "
-                "the problem. The `operator' code %d is not recognized",
+                "the problem. The 'operator' code %d is not recognized",
                 PACKAGE_BUGREPORT, __func__, operator);
         }
 
@@ -509,7 +512,8 @@ wrapper_for_filter(struct arithmeticparams *p, char *token, 
int operator)
 
       /* Spin off threads for each pixel. */
       gal_threads_spin_off(arithmetic_filter, &afp, afp.input->size,
-                           p->cp.numthreads);
+                           p->cp.numthreads, p->cp.minmapsize,
+                           p->cp.quietmmap);
     }
 
 
@@ -550,13 +554,13 @@ arithmetic_binary_sanity_checks(gal_data_t *in, 
gal_data_t *conn,
 {
   int conn_int;
 
-  /* Do proper sanity checks on `conn'. */
+  /* Do proper sanity checks on 'conn'. */
   if(conn->size!=1)
-    error(EXIT_FAILURE, 0, "the first popped operand to `%s' must be a "
+    error(EXIT_FAILURE, 0, "the first popped operand to '%s' must be a "
           "single number. However, it has %zu elements", operator,
           conn->size);
   if(conn->type==GAL_TYPE_FLOAT32 || conn->type==GAL_TYPE_FLOAT64)
-    error(EXIT_FAILURE, 0, "the first popped operand to `%s' is the "
+    error(EXIT_FAILURE, 0, "the first popped operand to '%s' is the "
           "connectivity (a value between 1 and the number of dimensions) "
           "therefore, it must NOT be a floating point", operator);
 
@@ -565,18 +569,18 @@ arithmetic_binary_sanity_checks(gal_data_t *in, 
gal_data_t *conn,
   conn=gal_data_copy_to_new_type_free(conn, GAL_TYPE_INT32);
   conn_int = *((int32_t *)(conn->array));
   if(conn_int>in->ndim)
-    error(EXIT_FAILURE, 0, "the first popped operand of `%s' (%d) is "
+    error(EXIT_FAILURE, 0, "the first popped operand of '%s' (%d) is "
           "larger than the number of dimensions in the second-popped "
           "operand (%zu)", operator, conn_int, in->ndim);
 
   /* Make sure the array has an unsigned 8-bit type. */
   if(in->type!=GAL_TYPE_UINT8)
-    error(EXIT_FAILURE, 0, "the second popped operand of `%s' doesn't "
+    error(EXIT_FAILURE, 0, "the second popped operand of '%s' doesn't "
           "have an 8-bit unsigned integer type. It must be a binary "
           "dataset (only being equal to zero is checked). You can use "
-          "the `uint8' operator for type conversion", operator);
+          "the 'uint8' operator for type conversion", operator);
 
-  /* Clean up and return the integer value of `conn'. */
+  /* Clean up and return the integer value of 'conn'. */
   gal_data_free(conn);
   return conn_int;
 }
@@ -635,7 +639,7 @@ arithmetic_connected_components(struct arithmeticparams *p, 
char *token)
   /* Push the result onto the stack. */
   operands_add(p, NULL, out);
 
-  /* Clean up (`conn' was freed in the sanity check). */
+  /* Clean up ('conn' was freed in the sanity check). */
   gal_data_free(in);
 }
 
@@ -684,10 +688,10 @@ arithmetic_invert(struct arithmeticparams *p, char *token)
     case GAL_TYPE_UINT32: do *u32 = UINT32_MAX-*u32; while(++u32<u32f); break;
     case GAL_TYPE_UINT64: do *u64 = UINT64_MAX-*u64; while(++u64<u64f); break;
     default:
-      error(EXIT_FAILURE, 0, "`invert' operand has %s type. `invert' can "
+      error(EXIT_FAILURE, 0, "'invert' operand has %s type. 'invert' can "
             "only take unsigned integer types.\n\nYou can use any of the "
-            "`uint8', `uint16', `uint32', or `uint64' operators to chage "
-            "the type before calling `invert'",
+            "'uint8', 'uint16', 'uint32', or 'uint64' operators to chage "
+            "the type before calling 'invert'",
             gal_type_name(in->type, 1));
     }
 
@@ -699,11 +703,145 @@ arithmetic_invert(struct arithmeticparams *p, char 
*token)
 
 
 
+#define INTERPOLATE_REGION(TYPE,OP,FUNC) {                              \
+    TYPE mm, b, *a=in->array, *m=minmax->array;                         \
+    FUNC(in->type, &mm);                                                \
+    gal_blank_write(&b, in->type);                                      \
+    for(i=0;i<minmax->size;++i) m[i]=mm;                                \
+    for(i=0;i<in->size;++i)                                             \
+      {                                                                 \
+        if( l[i]>0 )                                                    \
+          GAL_DIMENSION_NEIGHBOR_OP(i, in->ndim, in->dsize, in->ndim,   \
+                                    dinc,                               \
+            {                                                           \
+              if(in->type==GAL_TYPE_FLOAT32 || in->type==GAL_TYPE_FLOAT64) \
+                { if( a[nind] OP m[l[i]] ) m[l[i]]=a[nind]; }           \
+              else                                                      \
+                { if( a[nind]!=b && a[nind] OP m[l[i]] ) m[l[i]]=a[nind]; } \
+            });                                                         \
+      }                                                                 \
+    for(i=0;i<in->size;++i) if( l[i]>0 ) { a[i]=m[l[i]]; }              \
+}
+#define INTERPOLATE_REGION_OP(TYPE) {                                   \
+    switch(operator)                                                    \
+      {                                                                 \
+      case ARITHMETIC_OP_INTERPOLATE_MINOFREGION:                       \
+        INTERPOLATE_REGION(TYPE,<,gal_type_max); break;                 \
+      case ARITHMETIC_OP_INTERPOLATE_MAXOFREGION:                       \
+        INTERPOLATE_REGION(TYPE,>,gal_type_min); break;                 \
+      default:                                                          \
+        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to " \
+              "fix the problem. The operator code %d isn't recognized", \
+              __func__, PACKAGE_BUGREPORT, operator);                   \
+      }                                                                 \
+  }
+static void
+arithmetic_interpolate_region(struct arithmeticparams *p,
+                              int operator, char *token)
+{
+  /* Pop the dataset to interpolate. */
+  int32_t *l;
+  gal_data_t *minmax;
+  gal_data_t *lab=NULL, *flag;
+  size_t i, *con, *dinc, numlabs;
+
+  /* First pop the number of nearby neighbors.*/
+  gal_data_t *connectivity = operands_pop(p, token);
+
+  /* Then pop the actual dataset to interpolate. */
+  gal_data_t *in = operands_pop(p, token);
+
+  /* Do proper sanity checks on 'con'. */
+  if(connectivity->size!=1)
+    error(EXIT_FAILURE, 0, "the first popped operand to the "
+          "'interpolate-XXXofregion' operators must be a single number. "
+          "However, it has %zu elements", connectivity->size);
+  if( connectivity->type==GAL_TYPE_FLOAT32
+      || connectivity->type==GAL_TYPE_FLOAT64)
+    error(EXIT_FAILURE, 0, "the first popped operand to "
+          "'interpolate-XXXofregion' operators is the connectivity to "
+          "define connected blank regions (a counter, an integer, with "
+          "a maximum of the number of dimensions of the input). It must "
+          "NOT be a floating point.\n\n"
+          "If its already an integer, but in a floating point container, "
+          "you can use the 'int32' operator to convert it to a 32-bit "
+          "integer for example");
+
+  /* Convert connectivity to an integer type and make sure its not larger
+     than dimensions of the input. */
+  connectivity=gal_data_copy_to_new_type_free(connectivity,
+                                              GAL_TYPE_SIZE_T);
+  con=connectivity->array;
+  if(con[0]>in->ndim)
+    error(EXIT_FAILURE, 0, "the first popped operand to "
+          "'interpolate-XXXofregion' operators must not be larger than "
+          "the number of dimensions of the input. The connectivity is "
+          "used to define connected blank regions. For example in a "
+          "2D dataset, a connectivity of 1 corresponds to 4-connected "
+          "neighbors and connectivity 2 corresponds to 8-connected "
+          "neighbors");
+
+  /* First make sure the input has blank values. If it doesn't, don't waste
+     time, just return it. */
+  if( gal_blank_present(in, 1)==0 )
+    { operands_add(p, NULL, in); return; }
+
+  /* Build a binary image with the blank regions masked and label them,
+     then free the flagged array. */
+  flag=gal_blank_flag(in);
+  numlabs=gal_binary_connected_components(flag, &lab, con[0]);
+  gal_data_free(flag);
+
+  /* Allocate array to keep maximum values for each region. Just note that
+     because we count the regions from 1, but the indexs from 0, we'll
+     allocate one extra point. */
+  ++numlabs;
+  minmax=gal_data_alloc(NULL, in->type, 1, &numlabs, NULL, 0, in->minmapsize,
+                        in->quietmmap, NULL, NULL, NULL);
+
+  /* Parse the dataset elements for NaNs. */
+  l=lab->array;
+  dinc=gal_dimension_increment(in->ndim, in->dsize);
+  switch(in->type)
+    {
+    case GAL_TYPE_UINT8:   INTERPOLATE_REGION_OP(uint8_t);  break;
+    case GAL_TYPE_INT8:    INTERPOLATE_REGION_OP(int8_t);   break;
+    case GAL_TYPE_UINT16:  INTERPOLATE_REGION_OP(uint16_t); break;
+    case GAL_TYPE_INT16:   INTERPOLATE_REGION_OP(int16_t);  break;
+    case GAL_TYPE_UINT32:  INTERPOLATE_REGION_OP(uint32_t); break;
+    case GAL_TYPE_INT32:   INTERPOLATE_REGION_OP(int32_t);  break;
+    case GAL_TYPE_FLOAT32: INTERPOLATE_REGION_OP(float);    break;
+    case GAL_TYPE_FLOAT64: INTERPOLATE_REGION_OP(double);   break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+            "fix the problem. The code %d is not a recognized data "
+            "type", __func__, PACKAGE_BUGREPORT, in->type);
+    }
+
+  /* For tests.
+  gal_fits_img_write(lab, "test-out.fits", NULL, NULL);
+  gal_fits_img_write(in, "test-out.fits", NULL, NULL);
+  printf("\n...%s...\n", __func__); exit(0);
+  */
+
+  /* Clean up. */
+  gal_data_free(lab);
+  gal_data_free(minmax);
+  gal_data_free(connectivity);
+
+  /* Push the interpolated dataset onto the stack. */
+  operands_add(p, NULL, in);
+}
+
+
+
+
+
 static void
-arithmetic_interpolate(struct arithmeticparams *p, char *token)
+arithmetic_interpolate(struct arithmeticparams *p, int operator, char *token)
 {
-  int num_int;
   gal_data_t *interpolated;
+  int num_int, interpop=GAL_ARITHMETIC_OP_INVALID;
 
   /* First pop the number of nearby neighbors.*/
   gal_data_t *num = operands_pop(p, token);
@@ -711,27 +849,46 @@ arithmetic_interpolate(struct arithmeticparams *p, char 
*token)
   /* Then pop the actual dataset to interpolate. */
   gal_data_t *in = operands_pop(p, token);
 
-  /* Do proper sanity checks on `num'. */
+  /* Do proper sanity checks on 'num'. */
   if(num->size!=1)
     error(EXIT_FAILURE, 0, "the first popped operand to "
-          "`interpolate-medianngb' must be a single number. However, "
-          "it has %zu elements", num->size);
+          "'interpolate-XXXXXngb' operators must be a single number. "
+          "However, it has %zu elements", num->size);
   if(num->type==GAL_TYPE_FLOAT32 || num->type==GAL_TYPE_FLOAT64)
     error(EXIT_FAILURE, 0, "the first popped operand to "
-          "`interpolate-medianngb' is the number of nearby neighbors (a "
-          "counter, an integer). It must NOT be a floating point.\n\n"
+          "'interpolate-XXXXXngb' operators is the number of nearby "
+          "neighbors (a counter, an integer). It must NOT be a floating "
+          "point.\n\n"
           "If its already an integer, but in a floating point container, "
-          "you can use the `int32' operator to convert it to a 32-bit "
+          "you can use the 'int32' operator to convert it to a 32-bit "
           "integer for example");
 
   /* Convert the given number to a 32-bit integer and read it in. */
   num=gal_data_copy_to_new_type_free(num, GAL_TYPE_INT32);
   num_int = *((int32_t *)(num->array));
 
+  /* Set the interpolation operator. */
+  switch(operator)
+    {
+    case ARITHMETIC_OP_INTERPOLATE_MINNGB:
+      interpop=GAL_INTERPOLATE_NEIGHBORS_FUNC_MIN;
+      break;
+    case ARITHMETIC_OP_INTERPOLATE_MAXNGB:
+      interpop=GAL_INTERPOLATE_NEIGHBORS_FUNC_MAX;
+      break;
+    case ARITHMETIC_OP_INTERPOLATE_MEDIANNGB:
+      interpop=GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN;
+      break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+            "the problem. The operator code %d isn't recognized",
+            __func__, PACKAGE_BUGREPORT, operator);
+    }
+
   /* Call the interpolation function. */
-  interpolated=gal_interpolate_close_neighbors(in, NULL, p->cp.interpmetric,
-                                               num_int, p->cp.numthreads,
-                                               1, 0);
+  interpolated=gal_interpolate_neighbors(in, NULL, p->cp.interpmetric,
+                                         num_int, p->cp.numthreads,
+                                         1, 0, interpop);
 
   /* Clean up and push the interpolated array onto the stack. */
   gal_data_free(in);
@@ -758,28 +915,28 @@ arithmetic_collapse(struct arithmeticparams *p, char 
*token, int operator)
 
   /* Small sanity check. */
   if( dimension->ndim!=1 || dimension->size!=1)
-    error(EXIT_FAILURE, 0, "first popped operand of `collapse-*' operators "
+    error(EXIT_FAILURE, 0, "first popped operand of 'collapse-*' operators "
           "(dimension to collapse) must be a single number (single-element, "
           "one-dimensional dataset). But it has %zu dimension(s) and %zu "
           "element(s).", dimension->ndim, dimension->size);
   if(dimension->type==GAL_TYPE_FLOAT32 || dimension->type==GAL_TYPE_FLOAT64)
-    error(EXIT_FAILURE, 0, "first popped operand of `collapse-*' operators "
+    error(EXIT_FAILURE, 0, "first popped operand of 'collapse-*' operators "
           "(dimension to collapse) must have an integer type, but it has "
-          "a floating point type (`%s')", gal_type_name(dimension->type,1));
+          "a floating point type ('%s')", gal_type_name(dimension->type,1));
   dimension=gal_data_copy_to_new_type_free(dimension, GAL_TYPE_LONG);
   dim=((long *)(dimension->array))[0];
   if(dim<0 || dim==0)
-    error(EXIT_FAILURE, 0, "first popped operand of `collapse-*' operators "
+    error(EXIT_FAILURE, 0, "first popped operand of 'collapse-*' operators "
           "(dimension to collapse) must be positive (larger than zero), it "
           "is %ld", dim);
   if(dim > input->ndim)
-    error(EXIT_FAILURE, 0, "input dataset to `%s' has %zu dimension(s), "
-          "but you have asked to collapse along dimension %zu", token,
+    error(EXIT_FAILURE, 0, "input dataset to '%s' has %zu dimension(s), "
+          "but you have asked to collapse along dimension %ld", token,
           input->ndim, dim);
 
 
   /* If a WCS structure has been read, we'll need to pass it to
-     `gal_dimension_collapse', so it modifies it respectively. */
+     'gal_dimension_collapse', so it modifies it respectively. */
   if(p->wcs_collapsed==0)
     {
       p->wcs_collapsed=1;
@@ -818,9 +975,9 @@ arithmetic_collapse(struct arithmeticparams *p, char 
*token, int operator)
 
 
   /* If a WCS structure existed, a modified WCS is now present in
-     `collapsed->wcs'. So we'll let the freeing of `input' free the old
-     `p->refdata.wcs' structure and we'll put the new one there, then we'll
-     set `collapsed->wcs' to `NULL', so the new one isn't freed. */
+     'collapsed->wcs'. So we'll let the freeing of 'input' free the old
+     'p->refdata.wcs' structure and we'll put the new one there, then we'll
+     set 'collapsed->wcs' to 'NULL', so the new one isn't freed. */
   if(collapsed->wcs)
     {
       p->refdata.wcs = collapsed->wcs;
@@ -829,9 +986,9 @@ arithmetic_collapse(struct arithmeticparams *p, char 
*token, int operator)
 
 
   /* We'll also need to correct the size of the reference dataset if it
-     hasn't been corrected yet. We'll use `memcpy' to write the new `dsize'
+     hasn't been corrected yet. We'll use 'memcpy' to write the new 'dsize'
      values into the old ones. The dimensions have decreased, so we won't
-     be writing outside of allocated space that `p->refdata.dsize' points
+     be writing outside of allocated space that 'p->refdata.dsize' points
      to. */
   if( p->refdata.ndim != collapsed->ndim )
     {
@@ -863,7 +1020,7 @@ arithmetic_tofile(struct arithmeticparams *p, char *token, 
int freeflag)
   /* Save it to a file. */
   popped->wcs=p->refdata.wcs;
   if(popped->ndim==1 && p->onedasimage==0)
-    gal_table_write(popped, NULL, p->cp.tableformat, filename,
+    gal_table_write(popped, NULL, NULL, p->cp.tableformat, filename,
                     "ARITHMETIC", 0);
   else
     gal_fits_img_write(popped, filename, NULL, PROGRAM_NAME);
@@ -882,53 +1039,11 @@ arithmetic_tofile(struct arithmeticparams *p, char 
*token, int freeflag)
 
 
 
-/* Pull out unique elements */
-#define UNIQUE_BYTYPE(TYPE) {                                           \
-    size_t i, j;                                                        \
-    TYPE *a=input->array, b;                                            \
-                                                                        \
-    /* Write the blank value for this type into `b'. */                 \
-    gal_blank_write(&b, input->type);                                   \
-                                                                        \
-    /* Go over the elements, and set the duplicates to blank. */        \
-    /* Note that for integers and floats, the behavior of blank/NaN */  \
-    /* differs: for floats (NaN), we can identify a blank using the  */ \
-    /* fact that by definition, NaN!=NaN. */                            \
-    if(b==b)                                                            \
-      for(i=0;i<input->size;++i)                                        \
-        { if(a[i]!=b)    for(j=i+1;j<input->size;++j) if(a[i]==a[j]) a[j]=b;} \
-    else                                                                \
-      for(i=0;i<input->size;++i)                                        \
-        { if(a[i]==a[i]) for(j=i+1;j<input->size;++j) if(a[i]==a[j]) a[j]=b;} \
-  }
-
 void
 arithmetic_unique(struct arithmeticparams *p, char *token, int operator)
 {
-  gal_data_t *input = operands_pop(p, token);
-
-  /* Remove the duplicates based on size. */
-  switch(input->type)
-    {
-    case GAL_TYPE_UINT8:   UNIQUE_BYTYPE( uint8_t  ); break;
-    case GAL_TYPE_INT8:    UNIQUE_BYTYPE( int8_t   ); break;
-    case GAL_TYPE_UINT16:  UNIQUE_BYTYPE( uint16_t ); break;
-    case GAL_TYPE_INT16:   UNIQUE_BYTYPE( int16_t  ); break;
-    case GAL_TYPE_UINT32:  UNIQUE_BYTYPE( uint32_t ); break;
-    case GAL_TYPE_INT32:   UNIQUE_BYTYPE( int32_t  ); break;
-    case GAL_TYPE_UINT64:  UNIQUE_BYTYPE( uint64_t ); break;
-    case GAL_TYPE_INT64:   UNIQUE_BYTYPE( int64_t  ); break;
-    case GAL_TYPE_FLOAT32: UNIQUE_BYTYPE( float    ); break;
-    case GAL_TYPE_FLOAT64: UNIQUE_BYTYPE( double   ); break;
-    default:
-      error(EXIT_FAILURE, 0, "the `unique' operator doesn't support type "
-            "code `%u'", input->type);
-    }
-
-  /* Remove all blank elements. */
-  gal_blank_remove(input);
-
-  /* Add the collapsed dataset to the top of the operands. */
+  /* Pass the popped operand to the statistics library. */
+  gal_data_t *input = gal_statistics_unique(operands_pop(p, token), 1);
   operands_add(p, NULL, input);
 }
 
@@ -945,10 +1060,10 @@ arithmetic_add_dimension(struct arithmeticparams *p, 
char *token, int operator)
 
   /* Make sure the first operand is a number. */
   if(tmp->size!=1)
-    error(EXIT_FAILURE, 0, "first popped operand to `%s' must be a "
+    error(EXIT_FAILURE, 0, "first popped operand to '%s' must be a "
           "number (specifying how many datasets to use)", token);
 
-  /* Put the value into `num'. */
+  /* Put the value into 'num'. */
   tmp=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_SIZE_T);
   num=*(size_t *)(tmp->array);
   gal_data_free(tmp);
@@ -964,14 +1079,14 @@ arithmetic_add_dimension(struct arithmeticparams *p, 
char *token, int operator)
         {
           /* Basic sanity checks. */
           if(tmp->type!=out->type)
-            error(EXIT_FAILURE, 0, "the operands to `%s' have to have the "
+            error(EXIT_FAILURE, 0, "the operands to '%s' have to have the "
                   "same data type (the inputs contain atleast two types: "
-                  "`%s' and `%s')", token, gal_type_name(tmp->type, 1),
+                  "'%s' and '%s')", token, gal_type_name(tmp->type, 1),
                   gal_type_name(out->type, 1));
           if( tmp->ndim!=out->ndim-1
               || tmp->dsize[0]!=out->dsize[1]
               || tmp->dsize[1]!=out->dsize[2] )
-            error(EXIT_FAILURE, 0, "the operands to `%s' have to have the "
+            error(EXIT_FAILURE, 0, "the operands to '%s' have to have the "
                   "same size", token);
         }
       else  /* First popped operand. */
@@ -979,7 +1094,7 @@ arithmetic_add_dimension(struct arithmeticparams *p, char 
*token, int operator)
           /* First popped operand, do necessary basic checks here. */
           if(tmp->ndim!=2)
             error(EXIT_FAILURE, 0, "currently only 2-dimensional datasets "
-                  "are acceptable for `%s', please get in touch with us at "
+                  "are acceptable for '%s', please get in touch with us at "
                   "%s so we add functionality for different dimensions",
                   token, PACKAGE_BUGREPORT);
 
@@ -1058,8 +1173,16 @@ arithmetic_set_operator(char *string, size_t 
*num_operands)
         { op=ARITHMETIC_OP_FILL_HOLES;            *num_operands=0; }
       else if (!strcmp(string, "invert"))
         { op=ARITHMETIC_OP_INVERT;                *num_operands=0; }
+      else if (!strcmp(string, "interpolate-minngb"))
+        { op=ARITHMETIC_OP_INTERPOLATE_MINNGB;    *num_operands=0; }
+      else if (!strcmp(string, "interpolate-maxngb"))
+        { op=ARITHMETIC_OP_INTERPOLATE_MAXNGB;    *num_operands=0; }
       else if (!strcmp(string, "interpolate-medianngb"))
         { op=ARITHMETIC_OP_INTERPOLATE_MEDIANNGB; *num_operands=0; }
+      else if (!strcmp(string, "interpolate-minofregion"))
+        { op=ARITHMETIC_OP_INTERPOLATE_MINOFREGION; *num_operands=0; }
+      else if (!strcmp(string, "interpolate-maxofregion"))
+        { op=ARITHMETIC_OP_INTERPOLATE_MAXOFREGION; *num_operands=0; }
       else if (!strcmp(string, "collapse-sum"))
         { op=ARITHMETIC_OP_COLLAPSE_SUM;          *num_operands=0; }
       else if (!strcmp(string, "collapse-min"))
@@ -1075,7 +1198,7 @@ arithmetic_set_operator(char *string, size_t 
*num_operands)
       else if (!strcmp(string, "add-dimension"))
         { op=ARITHMETIC_OP_ADD_DIMENSION;         *num_operands=0; }
       else
-        error(EXIT_FAILURE, 0, "the argument \"%s\" could not be "
+        error(EXIT_FAILURE, 0, "the argument '%s' could not be "
               "interpretted as a file name, named dataset, number, "
               "or operator", string);
     }
@@ -1098,7 +1221,7 @@ arithmetic_operator_run(struct arithmeticparams *p, int 
operator,
   int flags = ( GAL_ARITHMETIC_INPLACE | GAL_ARITHMETIC_FREE
                 | GAL_ARITHMETIC_NUMOK );
 
-  /* When `num_operands!=0', the operator is in the library. */
+  /* When 'num_operands!=0', the operator is in the library. */
   if(num_operands)
     {
       /* Pop the necessary number of operators. Note that the
@@ -1128,7 +1251,7 @@ arithmetic_operator_run(struct arithmeticparams *p, int 
operator,
              operand. So except for sigma-clipping (that has other
              parameters), the first popped operand must be an
              integer number, we will use that to construct a linked
-             list of any number of operands within the single `d1'
+             list of any number of operands within the single 'd1'
              pointer. */
           numop=pop_number_of_operands(p, operator, operator_string, &d2);
           for(i=0;i<numop;++i)
@@ -1137,12 +1260,12 @@ arithmetic_operator_run(struct arithmeticparams *p, int 
operator,
 
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-                "the problem. `%zu' is not recognized as an operand "
-                "counter (with `%s')", __func__, PACKAGE_BUGREPORT,
+                "the problem. '%zu' is not recognized as an operand "
+                "counter (with '%s')", __func__, PACKAGE_BUGREPORT,
                 num_operands, operator_string);
         }
 
-      /* Run the arithmetic operation. Note that `gal_arithmetic'
+      /* Run the arithmetic operation. Note that 'gal_arithmetic'
          is a variable argument function (like printf). So the
          number of arguments it uses depend on the operator. So
          when the operator doesn't need three operands, the extra
@@ -1181,8 +1304,15 @@ arithmetic_operator_run(struct arithmeticparams *p, int 
operator,
           arithmetic_invert(p, operator_string);
           break;
 
+        case ARITHMETIC_OP_INTERPOLATE_MINNGB:
+        case ARITHMETIC_OP_INTERPOLATE_MAXNGB:
         case ARITHMETIC_OP_INTERPOLATE_MEDIANNGB:
-          arithmetic_interpolate(p, operator_string);
+          arithmetic_interpolate(p, operator, operator_string);
+          break;
+
+        case ARITHMETIC_OP_INTERPOLATE_MINOFREGION:
+        case ARITHMETIC_OP_INTERPOLATE_MAXOFREGION:
+          arithmetic_interpolate_region(p, operator, operator_string);
           break;
 
         case ARITHMETIC_OP_COLLAPSE_SUM:
@@ -1204,7 +1334,7 @@ arithmetic_operator_run(struct arithmeticparams *p, int 
operator,
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! please contact us at "
                 "%s to fix the problem. The code %d is not "
-                "recognized for `op'", __func__, PACKAGE_BUGREPORT,
+                "recognized for 'op'", __func__, PACKAGE_BUGREPORT,
                 operator);
         }
     }
@@ -1214,6 +1344,28 @@ arithmetic_operator_run(struct arithmeticparams *p, int 
operator,
 
 
 
+/* Used by the 'set-' operator. */
+static int
+arithmetic_set_name_used_later(void *in, char *name)
+{
+  struct gal_arithmetic_set_params *p=(struct gal_arithmetic_set_params *)in;
+  gal_list_str_t *token, *tokens = (gal_list_str_t *)(p->tokens);
+
+  size_t counter=0;
+
+  /* If the name exists after the current token, then return 1. */
+  for(token=tokens;token!=NULL;token=token->next)
+    if( counter++ > p->tokencounter && !strcmp(token->v, name) )
+      return 1;
+
+  /* If we get to this point, it means that the name doesn't exist. */
+  return 0;
+}
+
+
+
+
+
 /* This function implements the reverse polish algorithm as explained
    in the Wikipedia page.
 
@@ -1228,16 +1380,19 @@ reversepolish(struct arithmeticparams *p)
   char *hdu, *filename, *printnum;
   int operator=GAL_ARITHMETIC_OP_INVALID;
 
-
   /* Prepare the processing: */
-  p->operands=NULL;
   p->popcounter=0;
-
+  p->operands=NULL;
+  p->setprm.params=p;
+  p->setprm.tokencounter=0;
+  p->setprm.tokens=p->tokens;
+  p->setprm.pop=operands_pop_wrapper_set;
+  p->setprm.used_later=arithmetic_set_name_used_later;
 
   /* Go over each input token and do the work. */
   for(token=p->tokens;token!=NULL;token=token->next)
     {
-      /* The `tofile-' operator's string can end in a `.fits', similar to a
+      /* The 'tofile-' operator's string can end in a '.fits', similar to a
          FITS file input file. So, it needs to be checked before checking
          for a filename. If we have a name or number, then add it to the
          operands linked list. Otherwise, pull out two members and do the
@@ -1249,14 +1404,21 @@ reversepolish(struct arithmeticparams *p)
       else if( !strncmp(OPERATOR_PREFIX_TOFILEFREE, token->v,
                    OPERATOR_PREFIX_LENGTH_TOFILE) )
         arithmetic_tofile(p, token->v, 1);
-      else if( !strncmp(token->v, OPERATOR_PREFIX_SET,
-                        OPERATOR_PREFIX_LENGTH_SET) )
-        operands_set_name(p, token->v);
-      else if( gal_array_name_recognized(token->v)
-          || operands_is_name(p, token->v) )
+      else if( !strncmp(token->v, GAL_ARITHMETIC_SET_PREFIX,
+                        GAL_ARITHMETIC_SET_PREFIX_LENGTH) )
+        gal_arithmetic_set_name(&p->setprm, token->v);
+      else if(    gal_array_name_recognized(token->v)
+               || gal_arithmetic_set_is_name(p->setprm.named, token->v) )
         operands_add(p, token->v, NULL);
       else if( (data=gal_data_copy_string_to_number(token->v)) )
-        operands_add(p, NULL, data);
+        {
+          /* The 'minmapsize' and 'quietmmap' parameters should be passed
+             onto the library within numbers also (since they are the
+             only things that go in the library sometimes). */
+          data->quietmmap=p->cp.quietmmap;
+          data->minmapsize=p->cp.minmapsize;
+          operands_add(p, NULL, data);
+        }
       /* Last option is an operator: the program will abort if the token
          isn't an operator. */
       else
@@ -1266,7 +1428,7 @@ reversepolish(struct arithmeticparams *p)
         }
 
       /* Increment the token counter. */
-      ++p->tokencounter;
+      ++p->setprm.tokencounter;
     }
 
 
@@ -1282,10 +1444,10 @@ reversepolish(struct arithmeticparams *p)
     error(EXIT_FAILURE, 0, "too many operands");
 
 
-  /* If the final operand has a filename, but its `data' element is NULL,
+  /* If the final operand has a filename, but its 'data' element is NULL,
      then the file hasn't actually be read yet. In this case, we need to
      read the contents of the file and put the resulting dataset into the
-     operands `data' element. This can happen for example if no operators
+     operands 'data' element. This can happen for example if no operators
      are called and there is only one filename as an argument (which can
      happen in scripts). */
   if(p->operands->data==NULL && p->operands->filename)
@@ -1305,8 +1467,8 @@ reversepolish(struct arithmeticparams *p)
         }
       else
         error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to fix "
-              "the problem. While `operands->data' is NULL, the filename "
-              "(`%s') is not recognized as a FITS file", __func__,
+              "the problem. While 'operands->data' is NULL, the filename "
+              "('%s') is not recognized as a FITS file", __func__,
               PACKAGE_BUGREPORT, filename);
     }
 
@@ -1326,10 +1488,10 @@ reversepolish(struct arithmeticparams *p)
   else
     {
       /* Put a copy of the WCS structure from the reference image, it
-         will be freed while freeing `data'. */
+         will be freed while freeing 'data'. */
       data->wcs=p->refdata.wcs;
       if(data->ndim==1 && p->onedasimage==0)
-        gal_table_write(data, NULL, p->cp.tableformat,
+        gal_table_write(data, NULL, NULL, p->cp.tableformat,
                         p->onedonstdout ? NULL : p->cp.output,
                         "ARITHMETIC", 0);
       else
@@ -1339,11 +1501,11 @@ reversepolish(struct arithmeticparams *p)
     }
 
 
-  /* Clean up, note that above, we copied the pointer to `refdata->wcs'
-     into `data', so it is freed when freeing `data'. */
+  /* Clean up, note that above, we copied the pointer to 'refdata->wcs'
+     into 'data', so it is freed when freeing 'data'. */
   gal_data_free(data);
   free(p->refdata.dsize);
-  gal_list_data_free(p->named);
+  gal_list_data_free(p->setprm.named);
 
 
   /* Clean up. Note that the tokens were taken from the command-line
diff --git a/bin/arithmetic/arithmetic.h b/bin/arithmetic/arithmetic.h
index 4e90d99..fdf65f3 100644
--- a/bin/arithmetic/arithmetic.h
+++ b/bin/arithmetic/arithmetic.h
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -40,7 +40,11 @@ enum arithmetic_prog_operators
   ARITHMETIC_OP_CONNECTED_COMPONENTS,
   ARITHMETIC_OP_FILL_HOLES,
   ARITHMETIC_OP_INVERT,
+  ARITHMETIC_OP_INTERPOLATE_MINNGB,
+  ARITHMETIC_OP_INTERPOLATE_MAXNGB,
   ARITHMETIC_OP_INTERPOLATE_MEDIANNGB,
+  ARITHMETIC_OP_INTERPOLATE_MINOFREGION,
+  ARITHMETIC_OP_INTERPOLATE_MAXOFREGION,
   ARITHMETIC_OP_COLLAPSE_SUM,
   ARITHMETIC_OP_COLLAPSE_MIN,
   ARITHMETIC_OP_COLLAPSE_MAX,
diff --git a/bin/arithmetic/astarithmetic.conf 
b/bin/arithmetic/astarithmetic.conf
index 5baa1ab..9ce8f10 100644
--- a/bin/arithmetic/astarithmetic.conf
+++ b/bin/arithmetic/astarithmetic.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astarithmetic                  # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
@@ -20,4 +20,4 @@
 # warranty.
 
 # Inputs
- wcshdu        1
\ No newline at end of file
+ wcshdu        1
diff --git a/bin/arithmetic/authors-cite.h b/bin/arithmetic/authors-cite.h
index deb0bec..13588d7 100644
--- a/bin/arithmetic/authors-cite.h
+++ b/bin/arithmetic/authors-cite.h
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/arithmetic/main.c b/bin/arithmetic/main.c
index ff9da8b..f1e72f5 100644
--- a/bin/arithmetic/main.c
+++ b/bin/arithmetic/main.c
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -36,7 +36,7 @@ int
 main (int argc, char *argv[])
 {
   struct timeval t1;
-  struct arithmeticparams p={{{0},0},0};
+  struct arithmeticparams p={{{0},0},{0},0};
 
   /* Set the starting time. */
   time(&p.rawtime);
diff --git a/bin/arithmetic/main.h b/bin/arithmetic/main.h
index af1b9a0..f53c46a 100644
--- a/bin/arithmetic/main.h
+++ b/bin/arithmetic/main.h
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -27,6 +27,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/list.h>
 
 #include <gnuastro-internal/options.h>
+#include <gnuastro-internal/arithmetic-set.h>
 
 
 /* Progarm name macros: */
@@ -40,10 +41,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 /* Constants: */
 #define NEG_DASH_REPLACE 11 /* Vertical tab (ASCII=11) for negative dash */
-#define OPERATOR_PREFIX_SET               "set-"
 #define OPERATOR_PREFIX_TOFILE            "tofile-"
 #define OPERATOR_PREFIX_TOFILEFREE        "tofilefree-"
-#define OPERATOR_PREFIX_LENGTH_SET        strlen(OPERATOR_PREFIX_SET)
 #define OPERATOR_PREFIX_LENGTH_TOFILE     strlen(OPERATOR_PREFIX_TOFILE)
 #define OPERATOR_PREFIX_LENGTH_TOFILEFREE strlen(OPERATOR_PREFIX_TOFILEFREE)
 
@@ -51,8 +50,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* In every node of the operand linked list, only one of the `filename' or
-   `data' should be non-NULL. Otherwise it will be a bug and will cause
+/* In every node of the operand linked list, only one of the 'filename' or
+   'data' should be non-NULL. Otherwise it will be a bug and will cause
    problems. All the operands operate on this premise. */
 struct operand
 {
@@ -71,6 +70,7 @@ struct arithmeticparams
 {
   /* Other structures: */
   struct gal_options_common_params cp;  /* Common parameters.           */
+  struct gal_arithmetic_set_params setprm; /* Parameters for 'set-'.    */
 
   /* Input: */
   gal_list_str_t     *hdus;  /* List of all given HDU strings.          */
@@ -82,8 +82,6 @@ struct arithmeticparams
   char          *globalhdu;  /* Single HDU for all inputs.              */
   uint8_t      onedasimage;  /* Write 1D outputs as an image not table. */
   uint8_t     onedonstdout;  /* Write 1D outputs on stdout, not table.  */
-  gal_data_t        *named;  /* List containing variables.              */
-  size_t      tokencounter;  /* Counter for finding place in tokens.    */
 
   /* Operating mode: */
   int        wcs_collapsed;  /* If the internal WCS is already collapsed.*/
diff --git a/bin/arithmetic/operands.c b/bin/arithmetic/operands.c
index 9b24ece..1c06500 100644
--- a/bin/arithmetic/operands.c
+++ b/bin/arithmetic/operands.c
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -34,6 +34,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/tiff.h>
 #include <gnuastro/array.h>
 #include <gnuastro-internal/checkset.h>
+#include <gnuastro-internal/arithmetic-set.h>
 
 #include "main.h"
 
@@ -76,197 +77,12 @@ operands_num(struct arithmeticparams *p)
 
 
 /**********************************************************************/
-/************                Named operands             ***************/
-/**********************************************************************/
-static int
-operands_name_is_used_later(struct arithmeticparams *p, char *name)
-{
-  size_t counter=0;
-  gal_list_str_t *token;
-
-  /* If the name indeed exists afterwards, then just return 1. */
-  for(token=p->tokens;token!=NULL;token=token->next)
-    if( counter++ > p->tokencounter && !strcmp(token->v, name) )
-      return 1;
-
-  /* If we get to this point, it means that the name doesn't exist. */
-  return 0;
-}
-
-
-
-
-
-/* Remove a name from the list of names and return the dataset it points
-   to. */
-static gal_data_t *
-operands_remove_name(struct arithmeticparams *p, char *name)
-{
-  gal_data_t *tmp, *removed=NULL, *prev=NULL;
-
-  /* Go over all the given names. */
-  for(tmp=p->named;tmp!=NULL;tmp=tmp->next)
-    {
-      if( !strcmp(tmp->name, name) )
-        {
-          removed=tmp;
-          if(prev) prev->next = tmp->next;
-          else     p->named   = tmp->next;
-        }
-
-      /* Set this node as the `prev' pointer. */
-      prev=tmp;
-    }
-
-  /* A small sanity check. */
-  if(removed==NULL)
-    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
-          "problem. `removed' must not be NULL at this point", __func__,
-          PACKAGE_BUGREPORT);
-
-  /* Nothing in the list points to it now. So we can safely modify and
-     return it. */
-  free(removed->name);
-  removed->next=NULL;
-  removed->name=NULL;
-  return removed;
-}
-
-
-
-
-
-/* Pop a dataset and keep it in the `named' list for later use. */
-void
-operands_set_name(struct arithmeticparams *p, char *token)
-{
-  gal_data_t *tmp, *tofree;
-  char *varname=&token[ OPERATOR_PREFIX_LENGTH_SET ];
-
-  /* If a dataset with this name already exists, it will be removed/deleted
-     so we can use the name for the newly designated dataset. */
-  for(tmp=p->named; tmp!=NULL; tmp=tmp->next)
-    if( !strcmp(varname, tmp->name) )
-      {
-        tofree=operands_remove_name(p, varname);
-        gal_data_free(tofree);
-
-        /* IMPORTANT: we MUST break here! `tmp' does't point to the right
-           place any more. We can define a `prev' node and modify it on
-           every attempt, but since there is only one dataset with a given
-           name, that is redundant and will just make the program slow. */
-        break;
-      }
-
-  /* Pop the top operand, then add it to the list of named datasets, but
-     only if it is used in later tokens. If it isn't, free the popped
-     dataset. The latter case (to define a name, but not use it), is
-     obviously a redundant operation, but that is upto the user, we
-     shouldn't worry about it here. We should just have everything in
-     place, so no crashes occur or no extra memory is consumed. */
-  if( operands_name_is_used_later(p, varname) )
-    {
-      /* Add the top popped operand to the list of names. */
-      gal_list_data_add(&p->named, operands_pop(p, "set"));
-
-      /* Write the requested name into this dataset. But note that `name'
-         MUST be already empty. So to be safe, we'll do a sanity check. */
-      if(p->named->name)
-        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-              "the problem. The `name' element should be NULL at this "
-              "point, but it isn't", __func__, PACKAGE_BUGREPORT);
-      gal_checkset_allocate_copy(varname, &p->named->name);
-    }
-  else
-    {
-      /* Pop the top operand, then free it. */
-      tmp=operands_pop(p, "set");
-      gal_data_free(tmp);
-    }
-}
-
-
-
-
-
-/* See if a given token is the name of a variable. */
-int
-operands_is_name(struct arithmeticparams *p, char *token)
-{
-  gal_data_t *tmp;
-
-  /* Make sure the variable name hasn't been set before. */
-  for(tmp=p->named; tmp!=NULL; tmp=tmp->next)
-    if( !strcmp(token, tmp->name) )
-      return 1;
-
-  /* If control reaches here, then there was no match*/
-  return 0;
-}
-
-
-
-
-
-/* Return a copy of the named dataset. */
-static gal_data_t *
-operands_copy_named(struct arithmeticparams *p, char *name)
-{
-  gal_data_t *out=NULL, *tmp;
-
-  /* Find the proper named element to use. */
-  for(tmp=p->named;tmp!=NULL;tmp=tmp->next)
-    if( !strcmp(tmp->name, name) )
-      {
-        /* If the named operand is used later, then copy it into the
-           output. */
-        if( operands_name_is_used_later(p, name) )
-          {
-            out=gal_data_copy(tmp);
-            free(out->name);
-            out->name=NULL;
-            out->next=NULL;
-          }
-        /* The named operand is not used any more. Remove it from the list
-           of named datasets and continue. */
-        else out=operands_remove_name(p, name);
-      }
-
-  /* A small sanity check. */
-  if(out==NULL)
-    error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to fix the "
-          "problem. The requested name `%s' couldn't be found in the list",
-          __func__, PACKAGE_BUGREPORT, name);
-
-  /* Return. */
-  return out;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-/**********************************************************************/
 /************      Adding to and popping from stack     ***************/
 /**********************************************************************/
 void
 operands_add(struct arithmeticparams *p, char *filename, gal_data_t *data)
 {
+  int readwcs;
   size_t ndim, *dsize;
   struct operand *newnode;
 
@@ -279,15 +95,15 @@ operands_add(struct arithmeticparams *p, char *filename, 
gal_data_t *data)
       errno=0;
       newnode=malloc(sizeof *newnode);
       if(newnode==NULL)
-        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `newnode'",
+        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'newnode'",
               __func__, sizeof *newnode);
 
-      /* If the `filename' is the name of a dataset, then use a copy of it.
+      /* If the 'filename' is the name of a dataset, then use a copy of it.
          otherwise, do the basic analysis. */
-      if( filename && operands_is_name(p, filename) )
+      if( filename && gal_arithmetic_set_is_name(p->setprm.named, filename) )
         {
           newnode->filename=NULL;
-          newnode->data=operands_copy_named(p, filename);
+          newnode->data=gal_arithmetic_set_copy_named(&p->setprm, filename);
         }
       else
         {
@@ -308,15 +124,33 @@ operands_add(struct arithmeticparams *p, char *filename, 
gal_data_t *data)
 
               /* If no WCS is set yet, use the WCS of this image and remove
                  possibly extra dimensions if necessary. */
-              if(p->refdata.wcs==NULL)
+              readwcs = (p->wcsfile && !strcmp(p->wcsfile,"none")) ? 0 : 1;
+              if(readwcs && p->refdata.wcs==NULL)
                 {
-                  dsize=gal_fits_img_info_dim(filename, newnode->hdu, &ndim);
+                  /* If the HDU is an image, read its size. */
+                  dsize = ( gal_fits_hdu_format(filename,
+                                                newnode->hdu)==IMAGE_HDU
+                            ? gal_fits_img_info_dim(filename,
+                                                    newnode->hdu, &ndim)
+                            : NULL);
+
+                  /* Read the WCS. */
                   p->refdata.wcs=gal_wcs_read(filename, newnode->hdu, 0, 0,
                                               &p->refdata.nwcs);
-                  ndim=gal_dimension_remove_extra(ndim, dsize, p->refdata.wcs);
+
+                  /* Remove extra (length of 1) dimensions (if we had an
+                     image HDU). */
+                  if(dsize)
+                    {
+                      ndim=gal_dimension_remove_extra(ndim, dsize,
+                                                      p->refdata.wcs);
+                      free(dsize);
+                    }
+
+                  /* Let the user know that the WCS is read. */
                   if(p->refdata.wcs && !p->cp.quiet)
-                    printf(" - WCS: %s (hdu %s).\n", filename, newnode->hdu);
-                  free(dsize);
+                    printf(" - WCS: %s (hdu %s).\n", filename,
+                           newnode->hdu);
                 }
             }
           else newnode->hdu=NULL;
@@ -343,7 +177,7 @@ operands_pop(struct arithmeticparams *p, char *operator)
   /* If the operand linked list has finished, then give an error and
      exit. */
   if(operands==NULL)
-    error(EXIT_FAILURE, 0, "not enough operands for the \"%s\" operator",
+    error(EXIT_FAILURE, 0, "not enough operands for the '%s' operator",
           operator);
 
   /* Set the dataset. If filename is present then read the file
@@ -359,11 +193,14 @@ operands_pop(struct arithmeticparams *p, char *operator)
                                  p->cp.quietmmap);
       data->ndim=gal_dimension_remove_extra(data->ndim, data->dsize, NULL);
 
-      /* Arithmetic changes the contents of a dataset, so the existing name
-         (in the FITS `EXTNAME' keyword) should not be passed on beyond
-         this point. Also, in Arithmetic, the `name' element is used to
-         identify variables. */
-      if(data->name) { free(data->name); data->name=NULL; }
+      /* Arithmetic changes the contents of a dataset, so the old name and
+         metadata (in the FITS 'EXTNAME' keyword for example) must not be
+         used beyond this point. Furthermore, in Arithmetic, the 'name'
+         element is used to identify variables (with the 'set-'
+         operator). */
+      if(data->name)    { free(data->name);    data->name=NULL;    }
+      if(data->unit)    { free(data->unit);    data->unit=NULL;    }
+      if(data->comment) { free(data->comment); data->comment=NULL; }
 
       /* When the reference data structure's dimensionality is non-zero, it
          means that this is not the first image read. So, write its basic
@@ -406,3 +243,16 @@ operands_pop(struct arithmeticparams *p, char *operator)
   free(operands);
   return data;
 }
+
+
+
+
+/* Wrapper to use the 'operands_pop' function with the 'set-' operator. */
+gal_data_t *
+operands_pop_wrapper_set(void *in)
+{
+  struct gal_arithmetic_set_params *tprm
+    = (struct gal_arithmetic_set_params *)in;
+  struct arithmeticparams *p=(struct arithmeticparams *)tprm->params;
+  return operands_pop(p, "set");
+}
diff --git a/bin/arithmetic/operands.h b/bin/arithmetic/operands.h
index fcf6453..2c9d62d 100644
--- a/bin/arithmetic/operands.h
+++ b/bin/arithmetic/operands.h
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -32,6 +32,9 @@ operands_add(struct arithmeticparams *p, char *filename, 
gal_data_t *data);
 gal_data_t *
 operands_pop(struct arithmeticparams *p, char *operator);
 
+gal_data_t *
+operands_pop_wrapper_set(void *in);
+
 void
 operands_set_name(struct arithmeticparams *p, char *token);
 
diff --git a/bin/arithmetic/ui.c b/bin/arithmetic/ui.c
index 95f23aa..93dcbba 100644
--- a/bin/arithmetic/ui.c
+++ b/bin/arithmetic/ui.c
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -69,20 +69,20 @@ doc[] = GAL_STRINGS_TOP_HELP_INFO PROGRAM_NAME" will do 
arithmetic "
   "operations on one or multiple images and numbers. Simply put, the name "
   "of the image along with the arithmetic operators and possible numbers "
   "are given as arguments. The extensions of each input are specified with "
-  "(possibly multiple) calls to the `--hdu' option."
+  "(possibly multiple) calls to the '--hdu' option."
   "\n\nCurrently "PROGRAM_NAME" only supports postfix or reverse polish "
-  "notation. For example to get the result of `5+6', you should write "
-  "`5 6 +', or to get the average of two images, you should write `a.fits "
-  "b.fits + 2 /' (or more simply use the `average' operator with "
-  "`a.fits b.fits average'). Please see the manual for more information. "
+  "notation. For example to get the result of '5+6', you should write "
+  "'5 6 +', or to get the average of two images, you should write 'a.fits "
+  "b.fits + 2 /' (or more simply use the 'average' operator with "
+  "'a.fits b.fits average'). Please see the manual for more information. "
   "\n\n"PROGRAM_NAME" recognizes a large collection of standard operators, "
   "including basic arithmetic (e.g., +, -, x, /), mathematical (e.g., abs, "
   "pow, sqrt, log), statistical (minvalue, min, max, average), comparison "
   "(e.g., lt, le, gt), logical (e.g., and, or, not), the full set of bitwise "
   "operators, and numeric type conversion operators to all known types. "
   "Please run the command below for a complete list describing all "
-  "operators (press the `SPACE' keyboard key, or arrow keys, to go down "
-  "and `q' to return to the command-line):\n\n"
+  "operators (press the 'SPACE' keyboard key, or arrow keys, to go down "
+  "and 'q' to return to the command-line):\n\n"
   "     $ info gnuastro \"Arithmetic operators\"\n"
   GAL_STRINGS_MORE_HELP_INFO
   /* After the list of options: */
@@ -157,8 +157,8 @@ ui_initialize_options(struct arithmeticparams *p,
       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;
+          if(cp->coptions[i].key!=GAL_OPTIONS_KEY_INTERPMETRIC)
+            cp->coptions[i].flags=OPTION_HIDDEN;
           break;
         }
     }
@@ -174,18 +174,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct arithmeticparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -232,15 +232,15 @@ parse_opt(int key, char *arg, struct argp_state *state)
 static void
 ui_read_check_only_options(struct arithmeticparams *p)
 {
-  if(p->wcsfile)
+  if(p->wcsfile && strcmp(p->wcsfile,"none"))
     {
       if(gal_fits_name_is_fits(p->wcsfile)==0)
-        error(EXIT_FAILURE, 0, "%s: file given to `--wcsfile' must be in "
+        error(EXIT_FAILURE, 0, "%s: file given to '--wcsfile' must be in "
               "FITS format with a recognizable FITS format suffix.",
               p->wcsfile);
       if(p->wcshdu==NULL)
         error(EXIT_FAILURE, 0, "%s: no HDU/extension specified (file given "
-              "to `--wcsfile')! Please use `--wcshdu' to specify a "
+              "to '--wcsfile')! Please use '--wcshdu' to specify a "
               "HDU/extension to read from", p->wcsfile);
     }
 }
@@ -250,7 +250,7 @@ ui_read_check_only_options(struct arithmeticparams *p)
 
 
 /* Sanity check on options AND arguments. If only option values are to be
-   checked, use `ui_read_check_only_options'. */
+   checked, use 'ui_read_check_only_options'. */
 static void
 ui_check_options_and_arguments(struct arithmeticparams *p)
 {
@@ -272,11 +272,11 @@ ui_check_options_and_arguments(struct arithmeticparams *p)
   /* The input tokens are put in a lastin-firstout (simple) linked list, so
      change them to the correct order so the order we pop a token is the
      same order that the user input a value. Note that for the options this
-     was done in `gal_options_read_config_set'. */
+     was done in 'gal_options_read_config_set'. */
   gal_list_str_reverse(&p->tokens);
 
-  /* To allow adding extensions to existing files, let the `keep' flag be
-     the same as the `dontdelete'. */
+  /* To allow adding extensions to existing files, let the 'keep' flag be
+     the same as the 'dontdelete'. */
   cp->keep=cp->dontdelete;
 
   /* Set the output file name (if any is needed). Note that since the lists
@@ -286,7 +286,7 @@ ui_check_options_and_arguments(struct arithmeticparams *p)
      list. */
   for(token=p->tokens; token!=NULL; token=token->next)
     {
-      /* Strings given to the `tofile' operator are also considered as
+      /* Strings given to the 'tofile' operator are also considered as
          outputs and we should delete them before starting the parse. */
       if( strncmp(OPERATOR_PREFIX_TOFILE, token->v,
                   OPERATOR_PREFIX_LENGTH_TOFILE) )
@@ -298,8 +298,8 @@ ui_check_options_and_arguments(struct arithmeticparams *p)
           if( gal_array_name_recognized(token->v) )
             {
               /* Increment the counter for FITS files (if they are
-                 input). Recall that the `tofile' operator can also have
-                 `.fits' suffixes (they are the names of the output
+                 input). Recall that the 'tofile' operator can also have
+                 '.fits' suffixes (they are the names of the output
                  files). */
               if( gal_array_name_recognized_multiext(token->v)  )
                 ++nummultiext;
@@ -318,13 +318,13 @@ ui_check_options_and_arguments(struct arithmeticparams *p)
             }
 
           /* This token is a number. Check if a negative dash was present that
-             has been temporarily replaced with `NEG_DASH_REPLACE' before
+             has been temporarily replaced with 'NEG_DASH_REPLACE' before
              option parsing. */
           else if(token->v[0]==NEG_DASH_REPLACE && isdigit(token->v[1]) )
             token->v[0]='-';
         }
 
-      /* We are on the `tofile' operator. */
+      /* We are on the 'tofile' operator. */
       else
         {
           filename=&token->v[ OPERATOR_PREFIX_LENGTH_TOFILE ];
@@ -332,6 +332,15 @@ ui_check_options_and_arguments(struct arithmeticparams *p)
         }
     }
 
+  /* In case no output name has been given (can happen with operators like
+     'makenew' when the user doesn't set an output name explicity), use a
+     default name. */
+  if(p->cp.output==NULL)
+    {
+      gal_checkset_allocate_copy("arithmetic.fits", &p->cp.output);
+      gal_checkset_writable_remove(p->cp.output, cp->keep, cp->dontdelete);
+    }
+
   /* Count the number of HDU values (if globalhdu isn't given) and check if
      its not less than the number of input FITS images. */
   if(p->globalhdu)
@@ -342,10 +351,10 @@ ui_check_options_and_arguments(struct arithmeticparams *p)
       if(numhdus<nummultiext)
         error(EXIT_FAILURE, 0, "not enough HDUs. There are %zu input "
               "files in formats that may contain multiple extensions (for "
-              "example FITS or TIFF). Therefore, the `--hdu' (`-h') option "
+              "example FITS or TIFF). Therefore, the '--hdu' ('-h') option "
               "must be called atleaset %zu times (once for each "
               "multi-extension file). If the HDU value is the same for all "
-              "the files, you may use `--globalhdu' (`-g') to specify a "
+              "the files, you may use '--globalhdu' ('-g') to specify a "
               "single HDU to be used for any number of input files",
               nummultiext, nummultiext);
     }
@@ -362,7 +371,7 @@ ui_preparations(struct arithmeticparams *p)
 
   /* In case a file is specified to read the WCS from (and ignore input
      datasets), read the WCS prior to starting parsing of the arguments. */
-  if(p->wcsfile)
+  if(p->wcsfile && strcmp(p->wcsfile,"none"))
     {
       /* Read the number of dimensions and the size of each. */
       dsize=gal_fits_img_info_dim(p->wcsfile, p->wcshdu, &ndim);
@@ -377,10 +386,10 @@ ui_preparations(struct arithmeticparams *p)
         }
       else
         fprintf(stderr, "WARNING: %s (hdu %s) didn't contain a "
-                "(readable by WCSLIB) WCS.", p->wcsfile, p->wcshdu);
+                "(readable by WCSLIB) WCS.\n", p->wcsfile, p->wcshdu);
 
       /* Correct the WCS dimensions if necessary. Note that we don't need
-         the `ndim' or `dsize' any more. */
+         the 'ndim' or 'dsize' any more. */
       ndim=gal_dimension_remove_extra(ndim, dsize, p->refdata.wcs);
 
       /* Clean up. */
@@ -417,9 +426,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
arithmeticparams *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
+  /* 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
diff --git a/bin/arithmetic/ui.h b/bin/arithmetic/ui.h
index 6bbe04c..8a74d81 100644
--- a/bin/arithmetic/ui.h
+++ b/bin/arithmetic/ui.h
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/buildprog/Makefile.am b/bin/buildprog/Makefile.am
index fd3f917..cd29e8d 100644
--- a/bin/buildprog/Makefile.am
+++ b/bin/buildprog/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2017-2019, Free Software Foundation, Inc.
+## Copyright (C) 2017-2021, 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
@@ -24,22 +24,22 @@
 ## Buildprog will also need some system-specific information that is
 ## gathered at compile time (for example the library installation directory
 ## (LIBDIR) and the executive file suffix (EXEEXT).
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib \
-              -DLIBDIR=\"$(libdir)\" -DINCLUDEDIR=\"$(includedir)\"      \
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib \
+              -DLIBDIR=\"$(libdir)\" \
+              -DINCLUDEDIR=\"$(includedir)\" \
               -DEXEEXT=\"$(EXEEXT)\"
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astbuildprog
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astbuildprog_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                     $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astbuildprog_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                     -lgnuastro $(CONFIG_LDADD)
 
 # Basic program sources.
 astbuildprog_SOURCES = main.c ui.c buildprog.c
@@ -56,11 +56,11 @@ EXTRA_DIST = main.h authors-cite.h args.h ui.h buildprog.h 
astbuildprog.conf.in
 # directories in these variables will be written into the final
 # BuildProgram configuration file.
 #
-# The user might give non-`-I' arguments to CPPFLAGS and non-`-L' arguments
+# The user might give non-'-I' arguments to CPPFLAGS and non-'-L' arguments
 # to LDFLAGS (see bug #54346). But here, we only want these two options,
-# and nothing else. To make the check, we'll see if `$i' (the input string)
-# is the same as `$v' or not. If the string starts with `-I' or `-L', it
-# will change between the two (the `-I' or `-L' have been removed), so it
+# and nothing else. To make the check, we'll see if '$i' (the input string)
+# is the same as '$v' or not. If the string starts with '-I' or '-L', it
+# will change between the two (the '-I' or '-L' have been removed), so it
 # shouldn't be the same.
 astbuildprog.conf: $(top_srcdir)/bin/buildprog/astbuildprog.conf.in
        cat $< > $@
@@ -93,10 +93,10 @@ astbuildprog.conf: 
$(top_srcdir)/bin/buildprog/astbuildprog.conf.in
 
 
 ## The configuration file in BuildProgram must be built on the users's
-## system (see above). The built configuration file is needed during `make
+## system (see above). The built configuration file is needed during 'make
 ## check'. But the (built) configuration file should NOT be distributed in a
 ## tarball. Since all built files are distributed, the only way to avoid
-## the configuration file being distributed it so use a `nodist_' prefix.
+## the configuration file being distributed it so use a 'nodist_' prefix.
 ##
 ## NOTE: the man page is created in doc/Makefile.am
 nodist_sysconf_DATA = astbuildprog.conf
diff --git a/bin/buildprog/args.h b/bin/buildprog/args.h
index 87872e9..4e1ba77 100644
--- a/bin/buildprog/args.h
+++ b/bin/buildprog/args.h
@@ -5,7 +5,7 @@ BuildProgram is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -50,7 +50,7 @@ struct argp_option program_options[] =
       UI_KEY_INCLUDE,
       "STR",
       0,
-      "Directories to search for `#include's.",
+      "Directories to search for '#include's.",
       GAL_OPTIONS_GROUP_INPUT,
       &p->include,
       GAL_TYPE_STRLL,
@@ -78,7 +78,7 @@ struct argp_option program_options[] =
       UI_KEY_LINKLIB,
       "STR",
       0,
-      "Link libraries, e.g., for libgsl: `-lgsl'.",
+      "Link libraries, e.g., for libgsl: '-lgsl'.",
       GAL_OPTIONS_GROUP_INPUT,
       &p->linklib,
       GAL_TYPE_STRLL,
@@ -92,7 +92,7 @@ struct argp_option program_options[] =
       UI_KEY_LA,
       "STR",
       0,
-      "Libtool `.la' to use instead of default.",
+      "Libtool '.la' to use instead of default.",
       GAL_OPTIONS_GROUP_INPUT,
       &p->la,
       GAL_TYPE_STRING,
@@ -106,7 +106,7 @@ struct argp_option program_options[] =
       UI_KEY_TAG,
       "STR",
       0,
-      "Libtool `--tag': programming language.",
+      "Libtool '--tag': programming language.",
       GAL_OPTIONS_GROUP_INPUT,
       &p->tag,
       GAL_TYPE_STRING,
diff --git a/bin/buildprog/astbuildprog.conf.in 
b/bin/buildprog/astbuildprog.conf.in
index 6d3f3cd..9788628 100644
--- a/bin/buildprog/astbuildprog.conf.in
+++ b/bin/buildprog/astbuildprog.conf.in
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astbuildprog                   # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/buildprog/authors-cite.h b/bin/buildprog/authors-cite.h
index 50b4df4..80cb0a6 100644
--- a/bin/buildprog/authors-cite.h
+++ b/bin/buildprog/authors-cite.h
@@ -5,7 +5,7 @@ BuildProgram is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/buildprog/buildprog.c b/bin/buildprog/buildprog.c
index 8a67871..1542121 100644
--- a/bin/buildprog/buildprog.c
+++ b/bin/buildprog/buildprog.c
@@ -5,7 +5,7 @@ BuildProgram is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -45,7 +45,7 @@ buildprog_as_one_string(char *opt, gal_list_str_t *list)
   /* Only if we have a list. */
   if(list)
     {
-      /* For every node in the list, we want the `opt' and a space along with
+      /* For every node in the list, we want the 'opt' and a space along with
          the actual string. */
       for(tmp=list; tmp!=NULL; tmp=tmp->next)
         len += 1 + (opt ? strlen(opt) : 0) + strlen(tmp->v);
@@ -71,7 +71,7 @@ buildprog_as_one_string(char *opt, gal_list_str_t *list)
 int
 buildprog(struct buildprogparams *p)
 {
-  /* Note that the first node of `sourceargs' is the acutal source and the
+  /* Note that the first node of 'sourceargs' is the acutal source and the
      rest are arguments to be run later. */
   int retval;
   char *fullla;
@@ -104,7 +104,7 @@ buildprog(struct buildprogparams *p)
     if( asprintf(&optimize, "-O%s", p->optimize)<0 )
       error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
 
-  /* Libtool `.la' file: */
+  /* Libtool '.la' file: */
   if(p->la) fullla=p->la;
   else
     if( asprintf(&fullla, "%s/libgnuastro.la", LIBDIR)<0 )
@@ -122,10 +122,10 @@ buildprog(struct buildprogparams *p)
                warning     ? warning   : "",
                p->debug    ? "-g"      : "",
                optimize    ? optimize  : "",
-               cppflags    ? cppflags  : "",
                include     ? include   : "",
-               ldflags     ? ldflags   : "",
+               cppflags    ? cppflags  : "",
                linkdir     ? linkdir   : "",
+               ldflags     ? ldflags   : "",
                p->sourceargs->v,
                linklib     ?linklib    : "",
                INCLUDEDIR,
diff --git a/bin/buildprog/buildprog.h b/bin/buildprog/buildprog.h
index d6ba191..2e7918e 100644
--- a/bin/buildprog/buildprog.h
+++ b/bin/buildprog/buildprog.h
@@ -5,7 +5,7 @@ BuildProgram is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/buildprog/main.c b/bin/buildprog/main.c
index b7b868f..354f044 100644
--- a/bin/buildprog/main.c
+++ b/bin/buildprog/main.c
@@ -5,7 +5,7 @@ BuildProgram is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/buildprog/main.h b/bin/buildprog/main.h
index 0a43dc0..a2dcbe2 100644
--- a/bin/buildprog/main.h
+++ b/bin/buildprog/main.h
@@ -5,7 +5,7 @@ BuildProgram is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -48,7 +48,7 @@ struct buildprogparams
   gal_list_str_t     *include;    /* Libraries to link against.         */
   gal_list_str_t     *linkdir;    /* Libraries to link against.         */
   gal_list_str_t     *linklib;    /* Libraries to link against.         */
-  char                    *la;    /* Libtool `.la' instead of default.  */
+  char                    *la;    /* Libtool '.la' instead of default.  */
   char                    *cc;    /* C compiler to use.                 */
   uint8_t               noenv;
 
diff --git a/bin/buildprog/ui.c b/bin/buildprog/ui.c
index 83e2ef4..56d635e 100644
--- a/bin/buildprog/ui.c
+++ b/bin/buildprog/ui.c
@@ -5,7 +5,7 @@ BuildProgram is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -65,7 +65,7 @@ doc[] = GAL_STRINGS_TOP_HELP_INFO PROGRAM_NAME" will compile 
and run a "
   "depends on. Hence you do not have to worry about explicitly linking "
   "with CFITSIO for example if you want to work on a FITS file, or with "
   "GSL if you want to use GNU Scientific Library's functions. The standard "
-  "compiler options of `-I', `-L', and `-l' are also available for further "
+  "compiler options of '-I', '-L', and '-l' are also available for further "
   "customization of the build.\n"
   GAL_STRINGS_MORE_HELP_INFO
   /* After the list of options: */
@@ -130,9 +130,9 @@ ui_initialize_options(struct buildprogparams *p,
           cp->coptions[i].mandatory=GAL_OPTIONS_NOT_MANDATORY;
           break;
 
-        /* `--ignorecase's default short format is `I', but here we want to
-           follow the compiler format, hence we need `I' for
-           `include'. Therefore, here, we'll change the key for `include'
+        /* '--ignorecase's default short format is 'I', but here we want to
+           follow the compiler format, hence we need 'I' for
+           'include'. Therefore, here, we'll change the key for 'include'
            to some large number just to avoid confusion.*/
         case GAL_OPTIONS_KEY_IGNORECASE:
           cp->coptions[i].key=20000;
@@ -163,18 +163,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct buildprogparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -219,21 +219,21 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct buildprogparams *p)
 {
   size_t len;
 
-  /* If an `.la' file is given, make sure it has the correct suffix. */
+  /* If an '.la' file is given, make sure it has the correct suffix. */
   if(p->la)
     {
       len=strlen(p->la);
       if(len>=4)
         if(strcmp(&p->la[len-3], ".la"))
-          error(EXIT_FAILURE, 0, "`%s' is not a Libtool control file name "
-                "(with a `.la' suffix). The file name given to the `--la' "
-                "(`-a') option must be a Libtool control file", p->la);
+          error(EXIT_FAILURE, 0, "'%s' is not a Libtool control file name "
+                "(with a '.la' suffix). The file name given to the '--la' "
+                "('-a') option must be a Libtool control file", p->la);
     }
 }
 
@@ -279,23 +279,23 @@ ui_preparations(struct buildprogparams *p)
      options.c). */
   gal_list_str_reverse(&p->sourceargs);
 
-  /* Set the final output name. `EXEEXT' comes from the configuration
-     script (given by BuildProgram's `Makefile.am'). */
+  /* Set the final output name. 'EXEEXT' comes from the configuration
+     script (given by BuildProgram's 'Makefile.am'). */
   if(p->cp.output==NULL)
     p->cp.output=gal_checkset_automatic_output(&p->cp, p->sourceargs->v,
                                                EXEEXT);
 
-  /* Set the C compiler. Later we can add a check to make sure that `cc' is
+  /* Set the C compiler. Later we can add a check to make sure that 'cc' is
      actually in the PATH. */
   if(p->cc==NULL)
     {                                        /* No C compiler chosen. */
       if(p->noenv==0)
         {
-          p->cc=getenv("CC");                /* First check for `CC'. */
+          p->cc=getenv("CC");                /* First check for 'CC'. */
           if(p->cc==NULL)
-            p->cc=getenv("GCC");             /* Then check for `GCC'. */
+            p->cc=getenv("GCC");             /* Then check for 'GCC'. */
         }
-      if(p->cc==NULL) p->cc="gcc";           /* Default: `gcc'.       */
+      if(p->cc==NULL) p->cc="gcc";           /* Default: 'gcc'.       */
     }
 }
 
@@ -327,9 +327,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
buildprogparams *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
+  /* 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
diff --git a/bin/buildprog/ui.h b/bin/buildprog/ui.h
index 53d9f64..64c8519 100644
--- a/bin/buildprog/ui.h
+++ b/bin/buildprog/ui.h
@@ -5,7 +5,7 @@ BuildProgram is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/convertt/Makefile.am b/bin/convertt/Makefile.am
index 1935ff1..33f2b3c 100644
--- a/bin/convertt/Makefile.am
+++ b/bin/convertt/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astconvertt
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astconvertt_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                    $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astconvertt_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                    -lgnuastro $(CONFIG_LDADD)
 
 astconvertt_SOURCES = main.c ui.c convertt.c color.c
 
diff --git a/bin/convertt/args.h b/bin/convertt/args.h
index 4618a33..5f66e04 100644
--- a/bin/convertt/args.h
+++ b/bin/convertt/args.h
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -191,7 +191,7 @@ struct argp_option program_options[] =
       UI_KEY_CHANGE,
       "STR",
       0,
-      "Change pixel values `from_1:to_1,from_2:to_2`.",
+      "Change pixel values 'from_1:to_1,from_2:to_2'.",
       UI_GROUP_FLUX,
       &p->changestr,
       GAL_TYPE_STRING,
diff --git a/bin/convertt/astconvertt.conf b/bin/convertt/astconvertt.conf
index 07603d3..be36603 100644
--- a/bin/convertt/astconvertt.conf
+++ b/bin/convertt/astconvertt.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astconvertt                    # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/convertt/authors-cite.h b/bin/convertt/authors-cite.h
index 7470718..df6fa87 100644
--- a/bin/convertt/authors-cite.h
+++ b/bin/convertt/authors-cite.h
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/convertt/color.c b/bin/convertt/color.c
index bd34cb7..f9731c7 100644
--- a/bin/convertt/color.c
+++ b/bin/convertt/color.c
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -109,7 +109,7 @@ color_from_mono_hsv(struct converttparams *p)
                    channel->dsize, channel->wcs, 0, p->cp.minmapsize,
                    p->cp.quietmmap, "BLUE", NULL, "Blue color channel.");
 
-  /* Start the conversion. Note that the "Choroma" (`C') is fixed by our
+  /* Start the conversion. Note that the "Choroma" ('C') is fixed by our
      definition. */
   r=R->array;
   g=G->array;
@@ -195,7 +195,7 @@ color_from_mono_sls(struct converttparams *p)
                    channel->dsize, channel->wcs, 0, p->cp.minmapsize,
                    p->cp.quietmmap, "BLUE", NULL, "Blue color channel.");
 
-  /* Start the conversion. Note that the "Choroma" (`C') is fixed by our
+  /* Start the conversion. Note that the "Choroma" ('C') is fixed by our
      definition. */
   r=R->array;
   g=G->array;
@@ -206,7 +206,9 @@ color_from_mono_sls(struct converttparams *p)
       if(isnan(*f))
         *r=*g=*b=0.0;
       else
-        switch( (int)((*f-min)/(max-min)*200) )
+        switch( p->colormap->status==COLOR_SLS
+                ? (int)((*f-min)/(max-min)*200)
+                : 200 - (int)((*f-min)/(max-min)*200) )
           {
           case 0:   *r=0.000000; *g=0.000000; *b=0.000000; break;
           case 1:   *r=0.043442; *g=0.000000; *b=0.052883; break;
@@ -463,7 +465,7 @@ color_from_mono_viridis(struct converttparams *p)
                    channel->dsize, channel->wcs, 0, p->cp.minmapsize,
                    p->cp.quietmmap, "BLUE", NULL, "Blue color channel.");
 
-  /* Start the conversion. Note that the "Choroma" (`C') is fixed by our
+  /* Start the conversion. Note that the "Choroma" ('C') is fixed by our
      definition. */
   r=R->array;
   g=G->array;
@@ -764,9 +766,10 @@ color_map_prepare(struct converttparams *p)
   switch(p->colormap->status)
     {
     case COLOR_HSV:          color_from_mono_hsv(p); break;
-    case COLOR_SLS:          color_from_mono_sls(p); break;
     case COLOR_VIRIDIS:      color_from_mono_viridis(p); break;
     case COLOR_GRAY:         convertt_scale_to_uchar(p); break;
+    case COLOR_SLS:
+    case COLOR_SLS_INVERSE:  color_from_mono_sls(p); break;
     default:
       error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
             "the problem. The value %d is not a recognized color-space "
@@ -869,12 +872,12 @@ color_rgb_to_hsv(struct converttparams *p)
               if( *h<0.0 ) *h += 360.0;
             }
           else
-            /* When `max==0', then *r=*g=*b=0, so s=h=0. */
+            /* When 'max==0', then *r=*g=*b=0, so s=h=0. */
             *s=*h=0.0;
         }
       else
         /* When there is no difference, then its actually a grayscale
-           dataset, so `*v' is the only parameter that matters. */
+           dataset, so '*v' is the only parameter that matters. */
         *s=*h=0.0;
 
 
diff --git a/bin/convertt/color.h b/bin/convertt/color.h
index 7b477ea..54655ea 100644
--- a/bin/convertt/color.h
+++ b/bin/convertt/color.h
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/convertt/convertt.c b/bin/convertt/convertt.c
index 513f593..64bdd45 100644
--- a/bin/convertt/convertt.c
+++ b/bin/convertt/convertt.c
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -71,7 +71,7 @@ convertt_change(struct converttparams *p)
     for(channel=p->chll; channel!=NULL; channel=channel->next)
       {
         /* Make a condition array: all pixels with a value equal to
-           `change->from' will be set as 1 in this array. */
+           'change->from' will be set as 1 in this array. */
         cond=gal_arithmetic(GAL_ARITHMETIC_OP_EQ, 1, GAL_ARITHMETIC_NUMOK,
                             channel, change->from);
 
@@ -103,7 +103,7 @@ convertt_trunc_function(int operator, gal_data_t *data, 
gal_data_t *value)
 
 
   /* Make a condition array: all pixels with a value equal to
-     `change->from' will be set as 1 in this array. */
+     'change->from' will be set as 1 in this array. */
   cond=gal_arithmetic(operator, 1, GAL_ARITHMETIC_NUMOK, data, value);
 
 
@@ -115,7 +115,7 @@ convertt_trunc_function(int operator, gal_data_t *data, 
gal_data_t *value)
      data structure must not have changed. */
   if(out!=data)
     error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to solve the "
-          "problem. The `out' and `data' pointers are the same", __func__,
+          "problem. The 'out' and 'data' pointers are the same", __func__,
           PACKAGE_BUGREPORT);
 
 
@@ -246,7 +246,7 @@ convertt_scale_to_uchar(struct converttparams *p)
     {
       if(channel->status==0)
         {
-          /* Convert the values into a range between `0' and `maxbyte'. */
+          /* Convert the values into a range between '0' and 'maxbyte'. */
           ff=(f=channel->array)+size;
           if(p->invert)
             {
@@ -337,7 +337,7 @@ convertt(struct converttparams *p)
     /* Plain text: only one channel is acceptable. */
     case OUT_FORMAT_TXT:
       gal_checkset_writable_remove(p->cp.output, 0, p->cp.dontdelete);
-      gal_txt_write(p->chll, NULL, p->cp.output, 0);
+      gal_txt_write(p->chll, NULL, NULL, p->cp.output, 0);
       break;
 
     /* JPEG: */
diff --git a/bin/convertt/convertt.h b/bin/convertt/convertt.h
index d693421..96e2bb7 100644
--- a/bin/convertt/convertt.h
+++ b/bin/convertt/convertt.h
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/convertt/main.c b/bin/convertt/main.c
index 06976c3..6935f9b 100644
--- a/bin/convertt/main.c
+++ b/bin/convertt/main.c
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/convertt/main.h b/bin/convertt/main.h
index e051602..0d5cda6 100644
--- a/bin/convertt/main.h
+++ b/bin/convertt/main.h
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -56,6 +56,7 @@ enum colorspace_names
   COLOR_RGB,
   COLOR_HSV,
   COLOR_SLS,
+  COLOR_SLS_INVERSE,
   COLOR_VIRIDIS,
   COLOR_GRAY,
 };
diff --git a/bin/convertt/ui.c b/bin/convertt/ui.c
index f5f1df7..d6e306b 100644
--- a/bin/convertt/ui.c
+++ b/bin/convertt/ui.c
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -172,18 +172,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct converttparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -242,17 +242,19 @@ ui_colormap_sanity_check(struct converttparams *p)
   else if( !strcmp(strarr[0],"viridis")) { ccode=COLOR_VIRIDIS; nparams=0; }
   else if( !strcmp(strarr[0],"gray") || !strcmp(strarr[0],"grey"))
                                       { ccode=COLOR_GRAY; nparams=0; }
+  else if( !strcmp(strarr[0],"sls-inverse"))
+    { ccode=COLOR_SLS_INVERSE; nparams=0; }
   else
-    error(EXIT_FAILURE, 0, "`%s' not recognized as a colormap given "
-          "to `--colormap'", strarr[0]);
+    error(EXIT_FAILURE, 0, "'%s' not recognized as a colormap given "
+          "to '--colormap'", strarr[0]);
   p->colormap->status=ccode;
 
   /* Check if the proper number of parameters are given for this color
      space. Note that the actual colorspace name is the first element in
-     `monotocolor'. */
+     'monotocolor'. */
   if(p->colormap->size!=1 && p->colormap->size != (nparams+1) )
-    error(EXIT_FAILURE, 0, "%zu parameters given to `--monotocolor' for "
-          "the `%s' color space (which needs %zu)",
+    error(EXIT_FAILURE, 0, "%zu parameters given to '--monotocolor' for "
+          "the '%s' color space (which needs %zu)",
           p->colormap->size-1, strarr[0], nparams);
 
   /* Allocate the necessary space for the parameters (when
@@ -272,7 +274,7 @@ ui_colormap_sanity_check(struct converttparams *p)
             case COLOR_HSV: farray[0]=0; farray[1]=360; break;
             default:
               error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at "
-                    "%s to fix the problem. The value `%d' is not "
+                    "%s to fix the problem. The value '%d' is not "
                     "recognized for a color space that needs default "
                     "parameters", __func__, PACKAGE_BUGREPORT,
                     p->colormap->status);
@@ -282,8 +284,8 @@ ui_colormap_sanity_check(struct converttparams *p)
         {
           /* Increment the array pointer (temporarily) so we can read
              the rest of the parameters as float32. Note that we can't
-             use `+=' because it is a `void *' pointer. We'll have to
-             use `strarry', because its type is defined. */
+             use '+=' because it is a 'void *' pointer. We'll have to
+             use 'strarry', because its type is defined. */
           p->colormap->size-=1;
           p->colormap->array = strarr + 1;
           p->colormap->next=gal_data_copy_to_new_type(p->colormap,
@@ -309,7 +311,7 @@ ui_colormap_sanity_check(struct converttparams *p)
 
 
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct converttparams *p)
 {
@@ -321,7 +323,7 @@ ui_read_check_only_options(struct converttparams *p)
     {
       p->fluxlow=gal_data_copy_string_to_number(p->fluxlowstr);
       if(p->fluxlow==NULL)
-        error(EXIT_FAILURE, 0, "value to the `--fluxlow' (`-L', %s) "
+        error(EXIT_FAILURE, 0, "value to the '--fluxlow' ('-L', %s) "
               "couldn't be read as a number", p->fluxlowstr);
     }
 
@@ -329,7 +331,7 @@ ui_read_check_only_options(struct converttparams *p)
     {
       p->fluxhigh=gal_data_copy_string_to_number(p->fluxhighstr);
       if(p->fluxhigh==NULL)
-        error(EXIT_FAILURE, 0, "value to the `--fluxhigh' (`-H', %s) "
+        error(EXIT_FAILURE, 0, "value to the '--fluxhigh' ('-H', %s) "
               "couldn't be read as a number", p->fluxhighstr);
     }
 
@@ -339,8 +341,8 @@ ui_read_check_only_options(struct converttparams *p)
                           p->fluxhigh, p->fluxlow);
 
       if( *((unsigned char *)cond->array) == 0 )
-        error(EXIT_FAILURE, 0, "The value of `--fluxlow' must be less "
-              "than `--fluxhigh'");
+        error(EXIT_FAILURE, 0, "The value of '--fluxlow' must be less "
+              "than '--fluxhigh'");
 
       gal_data_free(cond);
     }
@@ -357,8 +359,8 @@ ui_read_check_only_options(struct converttparams *p)
 static void
 ui_check_options_and_arguments(struct converttparams *p)
 {
-  /* Reverse the `inputnames' linked list if it was given (recall that we
-     also accept input from the standard input). Note that the `hdu' linked
+  /* Reverse the 'inputnames' linked list if it was given (recall that we
+     also accept input from the standard input). Note that the 'hdu' linked
      list was reversed during option parsing, so we don't need to do it
      here any more. */
   gal_list_str_reverse(&p->inputnames);
@@ -394,7 +396,7 @@ ui_make_change_struct(char *arg)
   size_t len=0, counter=0;
   struct change *out=NULL, *last=NULL, *ch;
 
-  /* First set all the delimiters to `\0' and count the number of
+  /* First set all the delimiters to '\0' and count the number of
      characters in the full string. */
   while(*p!='\0')
     {
@@ -415,11 +417,11 @@ ui_make_change_struct(char *arg)
           ++counter;
           data=gal_data_copy_string_to_number(p);
           if(data==NULL)
-            error(EXIT_FAILURE, 0, "`%s' (input number %zu to the "
-                  "`--change' option) couldn't be read as a number", p,
+            error(EXIT_FAILURE, 0, "'%s' (input number %zu to the "
+                  "'--change' option) couldn't be read as a number", p,
                   counter);
 
-          /* Go to the end of this number (until you reach a `\0'). */
+          /* Go to the end of this number (until you reach a '\0'). */
           while(*p!='\0') {++p; continue;}
 
           /* Put the data structure in the correct place. When the counter is
@@ -430,15 +432,15 @@ ui_make_change_struct(char *arg)
               errno=0;
               ch=malloc(sizeof *ch);
               if(ch==NULL)
-                error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `ch'",
+                error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'ch'",
                       __func__, sizeof *ch);
 
               /* If the last structure has already been defined (!=NULL)
-                 then we should set its next element to `ch' and change it
-                 to point to `ch'. On the other hand, when this is the
-                 first structure to be created, then `last==NULL', so to
-                 start off the process, we should put `ch' into both the
-                 `out' and `last' lists.. */
+                 then we should set its next element to 'ch' and change it
+                 to point to 'ch'. On the other hand, when this is the
+                 first structure to be created, then 'last==NULL', so to
+                 start off the process, we should put 'ch' into both the
+                 'out' and 'last' lists.. */
               if(last)
                 {
                   last->next=ch;
@@ -447,7 +449,7 @@ ui_make_change_struct(char *arg)
               else
                 out=last=ch;
 
-              /* Put `data' in the `from' element, and since this is the
+              /* Put 'data' in the 'from' element, and since this is the
                  last structure, set its next element to NULL. */
               last->from=data;
               last->next=NULL;
@@ -520,7 +522,7 @@ ui_make_channels_ll(struct converttparams *p)
             hdu=gal_list_str_pop(&p->hdus);
           else
             error(EXIT_FAILURE, 0, "not enough HDUs. Every input FITS image "
-                  "needs a HDU, you can use the `--hdu' (`-h') option once "
+                  "needs a HDU, you can use the '--hdu' ('-h') option once "
                   "for each input FITS image (in the same order)");
 
           /* Read in the array and its WCS information. */
@@ -639,15 +641,15 @@ ui_prepare_input_channels(struct converttparams *p)
 
   /* If there are multiple color channels, then ignore the monotocolor
      option if it is given. But if there is only one, make sure that the
-     `colormap' option is actually given.*/
+     'colormap' option is actually given.*/
   if( p->numch==1 )
     {
       if( p->colormap==NULL )
         error(EXIT_FAILURE, 0, "no colormap! When there is only one input "
               "channel, it is necessary to specify a color map. For "
-              "example `gray', `hsv', `viridis' or `sls'.\n\n"
+              "example 'gray', 'hsv', 'viridis' or 'sls'.\n\n"
               "For more on ConvertType's color mapping, see the "
-              "description under `--colormap' in the Gnuastro book:\n\n"
+              "description under '--colormap' in the Gnuastro book:\n\n"
               "   $ info astconvertt");
     }
   else if( p->numch>1 && p->colormap )
@@ -728,7 +730,7 @@ ui_prepare_input_channels(struct converttparams *p)
         }
 
       /* This is the final (to be used) data structure, so keep its pointer
-         in case the next one is blank and this structure's `next' element
+         in case the next one is blank and this structure's 'next' element
          must be corrected. */
       prev=tmp;
     }
@@ -738,7 +740,7 @@ ui_prepare_input_channels(struct converttparams *p)
 
 
 
-/* We know cp->output is a known suffix, we just don't know if it has a `.`
+/* We know cp->output is a known suffix, we just don't know if it has a '.'
    before it or not. If it doesn't, one will be added to it and the output
    name will be set using the automatic output function. */
 void
@@ -755,7 +757,7 @@ ui_add_dot_use_automatic_output(struct converttparams *p)
         break;
       }
 
-  /* If the suffix does not start with a `.', put one there. */
+  /* If the suffix does not start with a '.', put one there. */
   if(suffix[0]!='.')
     {
       if( asprintf(&tmp, ".%s", suffix)<0 )
@@ -773,7 +775,7 @@ ui_add_dot_use_automatic_output(struct converttparams *p)
 
 
 /* Set output name, not that for ConvertType, the output option value is
-   mandatory (in `args.h'). So by the time the program reaches here, we
+   mandatory (in 'args.h'). So by the time the program reaches here, we
    know it exists. */
 static void
 ui_set_output(struct converttparams *p)
@@ -793,11 +795,11 @@ ui_set_output(struct converttparams *p)
     {
       /* Small sanity checks. */
       if(p->quality == GAL_BLANK_UINT8)
-        error(EXIT_FAILURE, 0, "the `--quality' (`-u') option is necessary "
+        error(EXIT_FAILURE, 0, "the '--quality' ('-u') option is necessary "
               "for jpeg outputs, but it has not been given");
       if(p->quality > 100)
-        error(EXIT_FAILURE, 0, "`%u' is larger than 100. The value to the "
-              "`--quality' (`-u') option must be between 1 and 100 "
+        error(EXIT_FAILURE, 0, "'%u' is larger than 100. The value to the "
+              "'--quality' ('-u') option must be between 1 and 100 "
               "(inclusive)", p->quality);
 
       /* Preparations. */
@@ -816,8 +818,8 @@ ui_set_output(struct converttparams *p)
   else if(gal_eps_name_is_eps(cp->output))
     {
       if(p->borderwidth==0 && p->widthincm==0)
-        error(EXIT_FAILURE, 0, "at least one of `--widthincm' (`-u'), or "
-              "`--borderwidth (`-b') options are necessary for an EPS "
+        error(EXIT_FAILURE, 0, "at least one of '--widthincm' ('-u'), or "
+              "'--borderwidth ('-b') options are necessary for an EPS "
               "output");
       p->outformat=OUT_FORMAT_EPS;
       if( gal_eps_suffix_is_eps(cp->output) )
@@ -828,8 +830,8 @@ ui_set_output(struct converttparams *p)
   else if(gal_pdf_name_is_pdf(cp->output))
     {
       if(p->borderwidth==0 && p->widthincm==0)
-        error(EXIT_FAILURE, 0, "at least one of `--widthincm' (`-u'), or "
-              "`--borderwidth (`-b') options are necessary for a PDF "
+        error(EXIT_FAILURE, 0, "at least one of '--widthincm' ('-u'), or "
+              "'--borderwidth ('-b') options are necessary for a PDF "
                  "output");
       p->outformat=OUT_FORMAT_PDF;
       if( gal_pdf_suffix_is_pdf(cp->output) )
@@ -841,7 +843,7 @@ ui_set_output(struct converttparams *p)
     {
       p->outformat=OUT_FORMAT_TXT;
 
-      /* If the given value is `stdout', then set p->cp.output to NULL, so
+      /* If the given value is 'stdout', then set p->cp.output to NULL, so
          the result will be printed to the standard output. */
       if( !strcmp(p->cp.output, "stdout") )
         {
@@ -851,8 +853,8 @@ ui_set_output(struct converttparams *p)
       else
         {
           /* Plain text files don't have any unique set of suffixes. So,
-             here, we will just adopt two of the most common ones: `txt' or
-             `dat'. If the output is just one of these two suffixes, then
+             here, we will just adopt two of the most common ones: 'txt' or
+             'dat'. If the output is just one of these two suffixes, then
              we will use automatic output to generate the full name,
              otherwise, we'll just take the user's given value as the
              filename. */
@@ -863,7 +865,7 @@ ui_set_output(struct converttparams *p)
           /* If output type is not an image, there should only be one color
              channel: */
           if(p->numch>1)
-            error(EXIT_FAILURE, 0, "text output (`--output=%s`) can only be "
+            error(EXIT_FAILURE, 0, "text output ('--output=%s') can only be "
                   "completed with one input color channel. You have given "
                   "%zu. Note that some formats (for example JPEG) can have "
                   "more than one color channel in each file. You can first "
@@ -923,9 +925,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
converttparams *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
+  /* 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
diff --git a/bin/convertt/ui.h b/bin/convertt/ui.h
index c31a8e6..f1deb68 100644
--- a/bin/convertt/ui.h
+++ b/bin/convertt/ui.h
@@ -5,7 +5,7 @@ ConvertType is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/convolve/Makefile.am b/bin/convolve/Makefile.am
index a5c42a3..1721343 100644
--- a/bin/convolve/Makefile.am
+++ b/bin/convolve/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astconvolve
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astconvolve_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                    $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astconvolve_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                    -lgnuastro $(CONFIG_LDADD)
 
 astconvolve_SOURCES = main.c ui.c convolve.c
 
diff --git a/bin/convolve/args.h b/bin/convolve/args.h
index 193b439..08654f0 100644
--- a/bin/convolve/args.h
+++ b/bin/convolve/args.h
@@ -5,7 +5,7 @@ Convolve is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -35,7 +35,7 @@ struct argp_option program_options[] =
     {
       "kernel",
       UI_KEY_KERNEL,
-      "STR",
+      "FITS",
       0,
       "File name of kernel for convolution.",
       GAL_OPTIONS_GROUP_INPUT,
@@ -166,7 +166,7 @@ struct argp_option program_options[] =
       UI_KEY_DOMAIN,
       "STR",
       0,
-      "Convolution domain: `spatial', `frequency'.",
+      "Convolution domain: 'spatial', 'frequency'.",
       GAL_OPTIONS_GROUP_OPERATING_MODE,
       &p->domainstr,
       GAL_TYPE_STRING,
diff --git a/bin/convolve/astconvolve.conf b/bin/convolve/astconvolve.conf
index fd78edc..fa4876b 100644
--- a/bin/convolve/astconvolve.conf
+++ b/bin/convolve/astconvolve.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astconvolve                    # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/convolve/authors-cite.h b/bin/convolve/authors-cite.h
index 4f842f5..69df96c 100644
--- a/bin/convolve/authors-cite.h
+++ b/bin/convolve/authors-cite.h
@@ -5,7 +5,7 @@ Convolve is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/convolve/convolve.c b/bin/convolve/convolve.c
index 8db100b..881dcae 100644
--- a/bin/convolve/convolve.c
+++ b/bin/convolve/convolve.c
@@ -5,7 +5,7 @@ Convolve is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -77,7 +77,7 @@ complextoreal(double *c, size_t size, int action, double 
**output)
       break;
     default:
       error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we can "
-            "correct it. The `action' code %d is not recognized", __func__,
+            "correct it. The 'action' code %d is not recognized", __func__,
             PACKAGE_BUGREPORT, action);
     }
 }
@@ -245,7 +245,7 @@ frequency_make_padded_complex(struct convolveparams *p)
     roundoff errors.
 
     NOTE: The padding to the input image (on the first axis for example)
-          was `p->kernel->dsize[0]-1'. Since `p->kernel->dsize[0]' is
+          was 'p->kernel->dsize[0]-1'. Since 'p->kernel->dsize[0]' is
           always odd, the padding will always be even.  */
 void
 removepaddingcorrectroundoff(struct convolveparams *p)
@@ -274,7 +274,7 @@ removepaddingcorrectroundoff(struct convolveparams *p)
       hi1 = ( p->kernel->dsize[1] - 1 )/2;
     }
 
-  /* To start with, `start' points to the first pixel in the final
+  /* To start with, 'start' points to the first pixel in the final
      image: */
   start=&rpad[hi0*ps1+hi1];
   for(i=0;i<isize[0];++i)
@@ -373,21 +373,21 @@ correctdeconvolve(struct convolveparams *p, double 
**spatial)
   errno=0;
   n=malloc(ps0*ps1*sizeof *n);
   if(n==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `n'",
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'n'",
           __func__, ps0*ps1*sizeof *n);
 
 
   /* Put the elements in their proper place: For example in one
-     dimention where the values are actually the true distances:
+     dimension where the values are actually the true distances:
 
         s[0]=0, s[1]=1, s[2]=2, s[3]=3, s[4]=4, s[5]=5
 
-     We want the value 0 to be in the `center'. Note that `s' is
+     We want the value 0 to be in the 'center'. Note that 's' is
      periodic, for example the next 6 elements have distances:
 
         s[6]=0, s[7]=1, s[8]=2, s[9]=3, s[10]=4, s[11]=5
 
-     So a `center'ed array would be like:
+     So a 'center'ed array would be like:
 
         s[0]=4, s[1]=5, s[2]=0, s[3]=1, s[4]=2, s[5]=3
 
@@ -520,6 +520,7 @@ twodimensionfft(struct convolveparams *p, struct 
fftonthreadparams *fp,
 {
   int err;
   pthread_t t;          /* All thread ids saved in this, not used. */
+  char *mmapname=NULL;
   pthread_attr_t attr;
   pthread_barrier_t b;
   size_t i, nb, *indexs, thrdcols;
@@ -531,7 +532,7 @@ twodimensionfft(struct convolveparams *p, struct 
fftonthreadparams *fp,
   else if(forward1backwardn1==-1) multiple=1;
   else
     error(EXIT_FAILURE, 0, "%s: a bug! The value of the variable "
-          "`forward1backwardn1' is %d not 1 or 2. Please contact us at %s "
+          "'forward1backwardn1' is %d not 1 or 2. Please contact us at %s "
           "so we can find the cause of the problem and fix it", __func__,
           forward1backwardn1, PACKAGE_BUGREPORT);
 
@@ -539,7 +540,10 @@ twodimensionfft(struct convolveparams *p, struct 
fftonthreadparams *fp,
   /* ==================== */
   /* 1D FFT on each row. */
   /* ==================== */
-  gal_threads_dist_in_threads(multiple*p->ps0, nt, &indexs, &thrdcols);
+  mmapname=gal_threads_dist_in_threads(multiple*p->ps0, nt,
+                                       p->input->minmapsize,
+                                       p->cp.quietmmap,
+                                       &indexs, &thrdcols);
   if(nt==1)
     {
       fp[0].stride=1;
@@ -577,15 +581,21 @@ twodimensionfft(struct convolveparams *p, struct 
fftonthreadparams *fp,
       pthread_attr_destroy(&attr);
       pthread_barrier_destroy(&b);
     }
-  free(indexs);
+
+  /* Clean up. */
+  if(mmapname) gal_pointer_mmap_free(&mmapname, p->cp.quietmmap);
+  else         free(indexs);
 
 
 
   /* ====================== */
   /* 1D FFT on each column. */
   /* ====================== */
-  /* No comments, exact duplicate, except the p->ps1s! */
-  gal_threads_dist_in_threads(multiple*p->ps1, nt, &indexs, &thrdcols);
+  /* No comments, exact duplicate of above, except the p->ps1s! */
+  mmapname=gal_threads_dist_in_threads(multiple*p->ps1, nt,
+                                       p->input->minmapsize,
+                                       p->cp.quietmmap,
+                                       &indexs, &thrdcols);
   if(nt==1)
     {
       fp[0].stride=p->ps1;
@@ -614,7 +624,10 @@ twodimensionfft(struct convolveparams *p, struct 
fftonthreadparams *fp,
       pthread_attr_destroy(&attr);
       pthread_barrier_destroy(&b);
     }
-  free(indexs);
+
+  /* Clean up, note that 'indexs' may be memory-mapped. */
+  if(mmapname) gal_pointer_mmap_free(&mmapname, p->cp.quietmmap);
+  else         free(indexs);
 }
 
 
@@ -800,7 +813,7 @@ convolve(struct convolveparams *p)
 
   /* Save the output (which is in p->input) array. */
   if(p->input->ndim==1)
-    gal_table_write(p->input, NULL, p->cp.tableformat, p->cp.output,
+    gal_table_write(p->input, NULL, NULL, p->cp.tableformat, p->cp.output,
                     "CONVOLVED", 0);
   else
     gal_fits_img_write_to_type(p->input, cp->output, NULL, PROGRAM_NAME,
diff --git a/bin/convolve/convolve.h b/bin/convolve/convolve.h
index fcd4c67..27f2771 100644
--- a/bin/convolve/convolve.h
+++ b/bin/convolve/convolve.h
@@ -5,7 +5,7 @@ Convolve is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/convolve/main.c b/bin/convolve/main.c
index f2eb0c5..877130d 100644
--- a/bin/convolve/main.c
+++ b/bin/convolve/main.c
@@ -5,7 +5,7 @@ Convolve is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/convolve/main.h b/bin/convolve/main.h
index 5fca63c..83888f0 100644
--- a/bin/convolve/main.h
+++ b/bin/convolve/main.h
@@ -5,7 +5,7 @@ Convolve is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/convolve/ui.c b/bin/convolve/ui.c
index c6c4f2e..e466975 100644
--- a/bin/convolve/ui.c
+++ b/bin/convolve/ui.c
@@ -5,7 +5,7 @@ Convolve is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -147,18 +147,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct convolveparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -217,8 +217,8 @@ ui_read_check_only_options(struct convolveparams *p)
   else if( !strcmp("frequency", p->domainstr) )
     p->domain=CONVOLVE_DOMAIN_FREQUENCY;
   else
-    error(EXIT_FAILURE, 0, "domain value `%s' not recognized. Please use "
-          "either `spatial' or `frequency'", p->domainstr);
+    error(EXIT_FAILURE, 0, "domain value '%s' not recognized. Please use "
+          "either 'spatial' or 'frequency'", p->domainstr);
 
 
   /* If we are in the spatial domain, make sure that the necessary
@@ -227,10 +227,10 @@ ui_read_check_only_options(struct convolveparams *p)
     if( cp->tl.tilesize==NULL || cp->tl.numchannels==NULL )
       {
         if( cp->tl.tilesize==NULL && cp->tl.numchannels==NULL )
-          error(EXIT_FAILURE, 0, "in spatial convolution, `--numchannels' "
-                "and `--tilesize' are mandatory");
+          error(EXIT_FAILURE, 0, "in spatial convolution, '--numchannels' "
+                "and '--tilesize' are mandatory");
         else
-          error(EXIT_FAILURE, 0, "in spatial convolution, `--%s' is "
+          error(EXIT_FAILURE, 0, "in spatial convolution, '--%s' is "
                 "mandatory: you should use it to set the %s",
                 cp->tl.tilesize ? "numchannels" : "tilesize",
                 ( cp->tl.tilesize
@@ -258,7 +258,7 @@ ui_check_options_and_arguments(struct convolveparams *p)
           if( p->cp.hdu==NULL )
             error(EXIT_FAILURE, 0, "no HDU specified. When the input is a "
                   "FITS file, a HDU must also be specified, you can use "
-                  "the `--hdu' (`-h') option and give it the HDU number "
+                  "the '--hdu' ('-h') option and give it the HDU number "
                   "(starting from zero), extension name, or anything "
                   "acceptable by CFITSIO");
 
@@ -267,7 +267,7 @@ ui_check_options_and_arguments(struct convolveparams *p)
           p->hdu_type=gal_fits_hdu_format(p->filename, p->cp.hdu);
           if(p->hdu_type==IMAGE_HDU && p->column)
             error(EXIT_FAILURE, 0, "%s (hdu: %s): is a FITS image "
-                  "extension. The `--column' option is only applicable "
+                  "extension. The '--column' option is only applicable "
                   "to tables.", p->filename, p->cp.hdu);
         }
     }
@@ -281,7 +281,7 @@ ui_check_options_and_arguments(struct convolveparams *p)
           if( p->khdu==NULL )
             error(EXIT_FAILURE, 0, "no HDU specified. When the kernel is a "
                   "FITS file, a HDU must also be specified, you can use "
-                  "the `--khdu' (`-u') option and give it the HDU number "
+                  "the '--khdu' ('-u') option and give it the HDU number "
                   "(starting from zero), extension name, or anything "
                   "acceptable by CFITSIO");
 
@@ -290,7 +290,7 @@ ui_check_options_and_arguments(struct convolveparams *p)
           kernel_type=gal_fits_hdu_format(p->kernelname, p->khdu);
           if(kernel_type==IMAGE_HDU && p->kernelcolumn)
             error(EXIT_FAILURE, 0, "%s (hdu: %s): is a FITS image "
-                  "extension. The `--kernelcolumn' option is only "
+                  "extension. The '--kernelcolumn' option is only "
                   "applicable to tables.", p->kernelname, p->khdu);
         }
     }
@@ -357,13 +357,13 @@ ui_read_column(struct convolveparams *p, int i0k1)
           error(EXIT_FAILURE, 0, "%s is a table containing more than one "
                 "column. However, the specific column to work on isn't "
                 "specified.\n\n"
-                "Please use the `--column' (`-c') or `--kernelcolumn' "
+                "Please use the '--column' ('-c') or '--kernelcolumn' "
                 "options (depending on which dataset it is) to specify a "
                 "column. You can either give it the column number "
                 "(couting from 1), or a match/search in its meta-data (e.g., "
                 "column names).\n\n"
                 "For more information, please run the following command "
-                "(press the `SPACE' key to go down and `q' to return to the "
+                "(press the 'SPACE' key to go down and 'q' to return to the "
                 "command-line):\n\n"
                 "    $ info gnuastro \"Selecting table columns\"\n",
                 ( filename
@@ -455,7 +455,7 @@ ui_read_input(struct convolveparams *p)
 
 
 
-/* Read the kernel. VERY IMPORTANT: We can't use the `fits_img_read_kernel'
+/* Read the kernel. VERY IMPORTANT: We can't use the 'fits_img_read_kernel'
    because the Convolve program also does de-convolution. */
 static void
 ui_read_kernel(struct convolveparams *p)
@@ -524,12 +524,12 @@ ui_preparations(struct convolveparams *p)
       if( gal_blank_present(p->input, 1) )
         fprintf(stderr, "\n----------------------------------------\n"
                 "######## %s WARNING ########\n"
-                "There are blank pixels in `%s' (hdu: `%s') and you have "
+                "There are blank pixels in '%s' (hdu: '%s') and you have "
                 "asked for frequency domain convolution. As a result, all "
-                "the pixels in the output (`%s') will be blank. Only "
+                "the pixels in the output ('%s') will be blank. Only "
                 "spatial domain convolution can account for blank pixels "
                 "in the input data. You can run %s again with "
-                "`--domain=spatial'\n"
+                "'--domain=spatial'\n"
                 "----------------------------------------\n\n",
                 PROGRAM_NAME, p->filename, cp->hdu, cp->output,
                 PROGRAM_NAME);
@@ -537,7 +537,7 @@ ui_preparations(struct convolveparams *p)
       /* Frequency domain is only implemented in 2D. */
       if( p->input->ndim==1 )
         error(EXIT_FAILURE, 0, "Frequency domain convolution is currently "
-              "not implemented on 1D datasets. Please use `--domain=spatial' "
+              "not implemented on 1D datasets. Please use '--domain=spatial' "
               "to convolve this dataset");
     }
   else
@@ -554,7 +554,7 @@ ui_preparations(struct convolveparams *p)
     {
       /* Currently this is not implemented in 1D. */
       if(p->kernel->ndim==1)
-        error(EXIT_FAILURE, 0, "`--makekernel' is currently not available "
+        error(EXIT_FAILURE, 0, "'--makekernel' is currently not available "
               "on 1D datasets");
       else
         {
@@ -564,9 +564,9 @@ ui_preparations(struct convolveparams *p)
           /* Make sure the size of the kernel is the same as the input */
           if( p->input->dsize[0]!=p->kernel->dsize[0]
               || p->input->dsize[1]!=p->kernel->dsize[1] )
-            error(EXIT_FAILURE, 0, "with the `--makekernel' (`-m') option, "
+            error(EXIT_FAILURE, 0, "with the '--makekernel' ('-m') option, "
                   "the input image and the image specified with the "
-                  "`--kernel' (`-k') option should have the same size. The "
+                  "'--kernel' ('-k') option should have the same size. The "
                   "lower resolution input image (%s) has %zux%zu pixels "
                   "while the sharper image (%s) specified with the kernel "
                   "option has %zux%zu pixels", p->filename,
@@ -704,9 +704,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
convolveparams *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
+  /* 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
diff --git a/bin/convolve/ui.h b/bin/convolve/ui.h
index 4e71766..bdf95c7 100644
--- a/bin/convolve/ui.h
+++ b/bin/convolve/ui.h
@@ -5,7 +5,7 @@ Convolve is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/cosmiccal/Makefile.am b/bin/cosmiccal/Makefile.am
index 984a93a..9ad628c 100644
--- a/bin/cosmiccal/Makefile.am
+++ b/bin/cosmiccal/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2016-2019, Free Software Foundation, Inc.
+## Copyright (C) 2016-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astcosmiccal
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astcosmiccal_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                     $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astcosmiccal_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                     -lgnuastro $(CONFIG_LDADD)
 
 astcosmiccal_SOURCES = main.c ui.c cosmiccal.c
 
diff --git a/bin/cosmiccal/args.h b/bin/cosmiccal/args.h
index 1876746..7c7f7bf 100644
--- a/bin/cosmiccal/args.h
+++ b/bin/cosmiccal/args.h
@@ -5,7 +5,7 @@ CosmicCalculator is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -45,6 +45,19 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET
     },
     {
+      "velocity",
+      UI_KEY_VELOCITY,
+      "FLT",
+      0,
+      "Velocity of interest in km/s.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->velocity,
+      GAL_TYPE_FLOAT64,
+      GAL_OPTIONS_RANGE_GE_0,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
       "obsline",
       UI_KEY_OBSLINE,
       "STR,FLT",
@@ -114,10 +127,12 @@ struct argp_option program_options[] =
 
 
 
+
+    /* Basic cosmology */
     {
       0, 0, 0, 0,
-      "Specific calculations",
-      UI_GROUP_SPECIFIC
+      "Basic cosmology calculations",
+      UI_GROUP_BASIC
     },
     {
       "usedredshift",
@@ -125,7 +140,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Used redshift in this run.",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -139,7 +154,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Age of universe now (Ga: Giga Annum).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -153,7 +168,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Critical density now (g/cm^3).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -167,7 +182,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Proper distance to z (Mpc).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -181,7 +196,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Angular diameter distance (Mpc).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -195,7 +210,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Tangential dist. covered by 1arcsec at z (kpc).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -209,7 +224,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Luminosity distance to z (Mpc).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -223,7 +238,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Distance modulus at z (no units).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -237,7 +252,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Conversion to absolute magnitude (no unit).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -251,7 +266,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Age of universe at z (Ga: Giga Annum).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -265,7 +280,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Look back time to z (Ga: Giga Annum).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -279,7 +294,7 @@ struct argp_option program_options[] =
       0,
       0,
       "Critical density at z (g/cm^3).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -293,7 +308,21 @@ struct argp_option program_options[] =
       0,
       0,
       "Comoving volume (4pi str) to z (Mpc^3).",
-      UI_GROUP_SPECIFIC,
+      UI_GROUP_BASIC,
+      &p->specific,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_add_to_single_value,
+    },
+    {
+      "usedvelocity",
+      UI_KEY_USEDVELOCITY,
+      0,
+      0,
+      "Used velocity (in km/s) for this run.",
+      UI_GROUP_BASIC,
       &p->specific,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -301,13 +330,50 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET,
       ui_add_to_single_value,
     },
+
+
+
+
+
+    /* Spectral line options. */
+    {
+      0, 0, 0, 0,
+      "Spectral lines",
+      UI_GROUP_SPECTRAL_LINES
+    },
+    {
+      "listlines",
+      UI_KEY_LISTLINES,
+      0,
+      0,
+      "List known lines and rest frame wavelength.",
+      UI_GROUP_SPECTRAL_LINES,
+      &p->listlines,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "listlinesatz",
+      UI_KEY_LISTLINESATZ,
+      0,
+      0,
+      "List known spectral lines at given redshift.",
+      UI_GROUP_SPECTRAL_LINES,
+      &p->listlinesatz,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
     {
       "lineatz",
       UI_KEY_LINEATZ,
       "STR/FLT",
       0,
-      "Wavelength of given line at chosen redshift",
-      UI_GROUP_SPECIFIC,
+      "Wavelength of line (name or wavelength) at z.",
+      UI_GROUP_SPECTRAL_LINES,
       &p->specific,
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
diff --git a/bin/cosmiccal/astcosmiccal.conf b/bin/cosmiccal/astcosmiccal.conf
index e40222e..6b040c8 100644
--- a/bin/cosmiccal/astcosmiccal.conf
+++ b/bin/cosmiccal/astcosmiccal.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astcosmiccal                   # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/cosmiccal/authors-cite.h b/bin/cosmiccal/authors-cite.h
index 4ebebd6..e752c6e 100644
--- a/bin/cosmiccal/authors-cite.h
+++ b/bin/cosmiccal/authors-cite.h
@@ -5,7 +5,7 @@ CosmicCalculator is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/cosmiccal/cosmiccal.c b/bin/cosmiccal/cosmiccal.c
index b42ec7d..15efffe 100644
--- a/bin/cosmiccal/cosmiccal.c
+++ b/bin/cosmiccal/cosmiccal.c
@@ -5,7 +5,7 @@ CosmicCalculator is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -72,7 +72,7 @@ cosmiccal_print_input(struct cosmiccalparams *p)
 static void
 cosmiccal_printall(struct cosmiccalparams *p)
 {
-  double ad, ld, vz, pd, absmagconv;
+  double ad, ld, vz, pd, vel, absmagconv;
   double curage, ccritd, distmod, outage, zcritd;
 
   /* The user wants everything, do all the calculations and print
@@ -104,6 +104,8 @@ cosmiccal_printall(struct cosmiccalparams *p)
   zcritd=gal_cosmology_critical_density(p->redshift, p->H0, p->olambda,
                                         p->omatter, p->oradiation);
 
+  vel=gal_cosmology_velocity_from_z(p->redshift);
+
   vz=gal_cosmology_comoving_volume(p->redshift, p->H0, p->olambda, p->omatter,
                                    p->oradiation);
 
@@ -115,6 +117,7 @@ cosmiccal_printall(struct cosmiccalparams *p)
   printf(    " ------------\n");
   printf(FLTFORMAT, "Age of Universe now (Ga*):", curage);
   printf(EXPFORMAT, "Critical density now (g/cm^3):",  ccritd);
+  printf(FLTFORMAT, "Velocity at z (km/s):", vel);
   printf(FLTFORMAT, "Proper distance to z (Mpc):", pd);
   printf(FLTFORMAT, "Angular diameter distance to z (Mpc):", ad);
   printf(FLTFORMAT, "Tangential distance covered by 1 arcsec at z (Kpc):",
@@ -155,7 +158,7 @@ cosmiccal(struct cosmiccalparams *p)
   if(isnan(p->redshift))
     {
       cosmiccal_print_input(p);
-      printf("\n\nPlease specify a redshift with the `--redshift' (or `-z') "
+      printf("\n\nPlease specify a redshift with the '--redshift' (or '-z') "
              "option.\n");
       return;
     }
@@ -256,6 +259,10 @@ cosmiccal(struct cosmiccalparams *p)
                                                           p->oradiation));
               break;
 
+            case UI_KEY_USEDVELOCITY:
+              printf("%g", gal_cosmology_velocity_from_z(p->redshift));
+              break;
+
             case UI_KEY_LINEATZ:
               printf("%g", gal_list_f64_pop(&p->specific_arg)*(1+p->redshift));
               break;
diff --git a/bin/cosmiccal/cosmiccal.h b/bin/cosmiccal/cosmiccal.h
index 64ca8e0..a648b08 100644
--- a/bin/cosmiccal/cosmiccal.h
+++ b/bin/cosmiccal/cosmiccal.h
@@ -5,7 +5,7 @@ CosmicCalculator is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/cosmiccal/main.c b/bin/cosmiccal/main.c
index 5dcce1a..1194783 100644
--- a/bin/cosmiccal/main.c
+++ b/bin/cosmiccal/main.c
@@ -5,7 +5,7 @@ CosmicCalculator is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/cosmiccal/main.h b/bin/cosmiccal/main.h
index 0801aa6..a1f6344 100644
--- a/bin/cosmiccal/main.h
+++ b/bin/cosmiccal/main.h
@@ -5,7 +5,7 @@ CosmicCalculator is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -47,11 +47,14 @@ struct cosmiccalparams
 
   /* Input: */
   double              redshift; /* Redshift of interest.                */
+  gal_data_t          *obsline; /* Observed wavelength of a line.       */
+  double              velocity; /* Velocity of interest.                */
   double                    H0; /* Current expansion rate (km/sec/Mpc). */
   double               olambda; /* Current cosmological constant dens.  */
   double               omatter; /* Current matter density.              */
   double            oradiation; /* Current radiation density.           */
-  gal_data_t          *obsline; /* Observed wavelength of a line.       */
+  uint8_t            listlines; /* List the known spectral lines.       */
+  uint8_t         listlinesatz; /* List the known spectral lines.       */
 
   /* Outputs. */
   gal_list_i32_t     *specific; /* Codes for single row calculations.   */
diff --git a/bin/cosmiccal/ui.c b/bin/cosmiccal/ui.c
index 0340ae7..5d516ec 100644
--- a/bin/cosmiccal/ui.c
+++ b/bin/cosmiccal/ui.c
@@ -5,7 +5,7 @@ CosmicCalculator is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -116,6 +116,7 @@ ui_initialize_options(struct cosmiccalparams *p,
 
   /* Program specific initializations. */
   p->redshift            = NAN;
+  p->velocity            = NAN;
 
   /* Modify the common options. */
   for(i=0; !gal_options_is_last(&cp->coptions[i]); ++i)
@@ -158,18 +159,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct cosmiccalparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -207,22 +208,22 @@ ui_add_to_single_value(struct argp_option *option, char 
*arg,
   /* In case of printing the option values. */
   if(lineno==-1)
     error(EXIT_FAILURE, 0, "currently the options to be printed in one row "
-          "(like `--age', `--luminositydist', and etc) do not support "
-          "printing with the `--printparams' (`-P'), or writing into "
+          "(like '--age', '--luminositydist', and etc) do not support "
+          "printing with the '--printparams' ('-P'), or writing into "
           "configuration files due to lack of time when implementing "
           "these features. You can put them into configuration files "
-          "manually. Please get in touch with us at `%s', so we can "
+          "manually. Please get in touch with us at '%s', so we can "
           "implement it", PACKAGE_BUGREPORT);
 
-  /* If this option is given in a configuration file, then `arg' will not
-     be NULL and we don't want to do anything if it is `0'. */
+  /* If this option is given in a configuration file, then 'arg' will not
+     be NULL and we don't want to do anything if it is '0'. */
   switch(option->key)
     {
     /* Options with arguments. */
     case UI_KEY_LINEATZ:
       /* Make sure an argument is given. */
       if(arg==NULL)
-        error(EXIT_FAILURE, 0, "option `--lineatz' needs an argument");
+        error(EXIT_FAILURE, 0, "option '--lineatz' needs an argument");
 
       /* If the argument is a number, read it, if not, see if its a known
          specral line name. */
@@ -231,7 +232,7 @@ ui_add_to_single_value(struct argp_option *option, char 
*arg,
         {
           linecode=gal_speclines_line_code(arg);
           if(linecode==GAL_SPECLINES_INVALID)
-            error(EXIT_FAILURE, 0, "`%s' not a known spectral line name",
+            error(EXIT_FAILURE, 0, "'%s' not a known spectral line name",
                   arg);
           val=gal_speclines_line_angstrom(linecode);
         }
@@ -242,11 +243,11 @@ ui_add_to_single_value(struct argp_option *option, char 
*arg,
     default:
       if(arg)
         {
-          /* Make sure the value is only `0' or `1'. */
+          /* Make sure the value is only '0' or '1'. */
           if( arg[1]!='\0' && *arg!='0' && *arg!='1' )
-            error_at_line(EXIT_FAILURE, 0, filename, lineno, "the `--%s' "
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "the '--%s' "
                           "option takes no arguments. In a configuration "
-                          "file it can only have the values `1' or `0', "
+                          "file it can only have the values '1' or '0', "
                           "indicating if it should be used or not",
                           option->name);
 
@@ -278,7 +279,7 @@ ui_parse_obsline(struct argp_option *option, char *arg,
   /* We want to print the stored values. */
   if(lineno==-1)
     {
-      /* Set the value pointer to `obsline'. */
+      /* Set the value pointer to 'obsline'. */
       obsline=*(gal_data_t **)(option->value);
       dptr = obsline->array;
 
@@ -297,7 +298,7 @@ ui_parse_obsline(struct argp_option *option, char *arg,
     }
   else
     {
-      /* The first part of `arg' (before the first comma) is not
+      /* The first part of 'arg' (before the first comma) is not
          necessarily a number. So we need to separate the first part from
          the rest.*/
       linename=arg;
@@ -310,7 +311,7 @@ ui_parse_obsline(struct argp_option *option, char *arg,
 
       /* Only one number must be given as second argument. */
       if(obsline==NULL || obsline->size!=1)
-        error(EXIT_FAILURE, 0, "Wrong format given to `--obsline'. Only "
+        error(EXIT_FAILURE, 0, "Wrong format given to '--obsline'. Only "
               "two values (line name/wavelengh, and observed wavelengh) "
               "must be given to it");
 
@@ -318,14 +319,14 @@ ui_parse_obsline(struct argp_option *option, char *arg,
          put that number in a second element of the array. */
       dptr=&manualwl;
       if( gal_type_from_string((void **)(&dptr), linename, GAL_TYPE_FLOAT64) )
-        { /* `linename' isn't a number. */
+        { /* 'linename' isn't a number. */
           obsline->status=gal_speclines_line_code(linename);
           if(obsline->status==GAL_SPECLINES_INVALID)
-            error(EXIT_FAILURE, 0, "`%s' not recognized as a standard spectral 
"
+            error(EXIT_FAILURE, 0, "'%s' not recognized as a standard spectral 
"
                   "line name", linename);
         }
       else
-        { /* `linename' is a number. */
+        { /* 'linename' is a number. */
 
           /* Allocate the new space. */
           tobsline=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &two, NULL,
@@ -343,7 +344,7 @@ ui_parse_obsline(struct argp_option *option, char *arg,
           obsline=tobsline;
         }
 
-      /* Point `option->value' to the dataset. */
+      /* Point 'option->value' to the dataset. */
       *(gal_data_t **)(option->value) = obsline;
 
       /* Our job is done, return NULL. */
@@ -374,24 +375,45 @@ ui_parse_obsline(struct argp_option *option, char *arg,
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct cosmiccalparams *p)
 {
+  int hasobsline=p->obsline!=NULL;
+  int hasredshift=!isnan(p->redshift);
+  int hasvelocity=!isnan(p->velocity);
   double sum = p->olambda + p->omatter + p->oradiation;
 
   /* Check if the density fractions add up to 1 (within floating point
      error). */
   if( sum > (1+1e-8) || sum < (1-1e-8) )
     error(EXIT_FAILURE, 0, "sum of fractional densities is not 1, but %.8f. "
-          "The cosmological constant (`olambda'), matter (`omatter') "
-          "and radiation (`oradiation') densities are given as %.8f, %.8f, "
+          "The cosmological constant ('olambda'), matter ('omatter') "
+          "and radiation ('oradiation') densities are given as %.8f, %.8f, "
           "%.8f", sum, p->olambda, p->omatter, p->oradiation);
 
-  /* Make sure that `--redshift' and `--obsline' aren't called together. */
-  if(!isnan(p->redshift) && p->obsline)
-    error(EXIT_FAILURE, 0, "`--redshift' and `--obsline' cannot be called "
-          "together");
+  /* Make sure that '--listlines' and '--listlinesatz' aren't called
+     together. */
+  if(p->listlines && p->listlinesatz)
+    error(EXIT_FAILURE, 0, "'--listlines' and '--listlinesatz' can't be "
+          "called together");
+
+  /* Make sure that atleast one of '--redshift', '--obsline', or
+     '--velocity' are given (different ways to set/estimate the
+     redshift). However, when '--listlines' and/or '--printparams' are
+     called (i.e., when they have a non-zero value) we don't need a
+     redshift all and the program can run without any of the three options
+     above. */
+  if(isnan(p->redshift) && p->obsline==NULL && isnan(p->velocity)
+     && p->listlines==0 && p->cp.printparams==0)
+    error(EXIT_FAILURE, 0, "no redshift/velocity specified! Please use "
+          "'--redshift', '--velocity' (in km/s), or '--obsline' to specify "
+          "a redshift, run with '--help' for more");
+
+  /* Make sure that '--redshift' and '--obsline' aren't called together. */
+  if( (hasredshift + hasvelocity + hasobsline) > 1 )
+    error(EXIT_FAILURE, 0, "only one of '--redshift', '--velocity', or "
+          "'--obsline' can be called in each run");
 }
 
 
@@ -417,23 +439,75 @@ ui_read_check_only_options(struct cosmiccalparams *p)
 /***************       Preparations         *******************/
 /**************************************************************/
 static void
+ui_list_lines(struct cosmiccalparams *p)
+{
+  size_t i;
+
+  /* Print basic information. Note that '--listlinesatz' is requested, also
+     print the redshift used. */
+  printf("# %s\n", PROGRAM_STRING);
+  if(p->listlinesatz)
+    printf("# Assumed redshift: %g\n", p->redshift);
+
+  /* Print column metadata. */
+  printf("# Column 1: Wavelength [Angstrom,f32] %s.\n",
+         ( p->listlinesatz
+           ? "Line wavelength at assumed redshift"
+           : "Rest frame wavelength of the line"));
+  printf("# Column 2: Name       [name,  str10] Line name in Gnuastro.\n");
+
+  /* Print the line information. */
+  for(i=1;i<GAL_SPECLINES_INVALID_MAX;++i)
+    printf("%-15g%s\n",
+           ( p->listlinesatz
+             ? ( gal_speclines_line_angstrom(i) * (1+p->redshift) )
+             : gal_speclines_line_angstrom(i) ),
+           gal_speclines_line_name(i));
+
+  /* Abort the program. */
+  exit(EXIT_SUCCESS);
+}
+
+
+
+
+
+static void
 ui_preparations(struct cosmiccalparams *p)
 {
   double *obsline = p->obsline ? p->obsline->array : NULL;
 
-  /* If `--obsline' has been given, set the redshift based on it. */
+  /* If '--listlines' is given, print them and abort the program
+     successfully, don't continue with the preparations. Note that
+     '--listlines' is the rest-frame lines. So we don't need any
+     redshift. */
+  if(p->listlines)
+    ui_list_lines(p);
+
+  /* If '--obsline' has been given, set the redshift based on it (it can't
+     be called with '--velocity'). */
   if(p->obsline)
     p->redshift = ( (p->obsline->status==GAL_SPECLINES_INVALID)
                     ? gal_speclines_line_redshift(obsline[0], obsline[1])
                     : gal_speclines_line_redshift_code(obsline[0],
                                                        p->obsline->status) );
 
+  /* If '--velocity' has been given, set the redshift based on it (it can't
+     be called with '--obsline'). */
+  if( !isnan(p->velocity) )
+    p->redshift = gal_cosmology_z_from_velocity(p->velocity);
+
   /* Currently GSL will fail for z=0. So if a value of zero is given (bug
      #56299). As a work-around, in such cases, we'll change it to an
-     extremely small value. NOTE: This has to be after the `obsline'
+     extremely small value. NOTE: This has to be after the 'obsline'
      check.*/
   if(p->redshift==0.0f) p->redshift=MAIN_REDSHIFT_ZERO;
 
+  /* Now that we have the redshift, we can print the 'listlinesatz'
+     option. */
+  if(p->listlinesatz)
+    ui_list_lines(p);
+
   /* The list is filled out in a first-in-last-out order. By the time
      control reaches here, the list is finalized. So we should just reverse
      it so the user gets values in the same order they requested them. */
@@ -469,9 +543,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
cosmiccalparams *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
+  /* 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
diff --git a/bin/cosmiccal/ui.h b/bin/cosmiccal/ui.h
index 0b36e52..fe12cf9 100644
--- a/bin/cosmiccal/ui.h
+++ b/bin/cosmiccal/ui.h
@@ -5,7 +5,7 @@ CosmicCalculator is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -33,7 +33,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Option groups particular to this program. */
 enum program_args_groups
 {
-  UI_GROUP_SPECIFIC = GAL_OPTIONS_GROUP_AFTER_COMMON,
+  UI_GROUP_BASIC = GAL_OPTIONS_GROUP_AFTER_COMMON,
+  UI_GROUP_SPECTRAL_LINES,
 };
 
 
@@ -42,13 +43,14 @@ enum program_args_groups
 
 /* Available letters for short options:
 
-   f j k n p t w x y
-   B E J Q R W X Y
+   f j k n p t w x
+   B E J Q R W X
 */
 enum option_keys_enum
 {
   /* With short-option version. */
   UI_KEY_REDSHIFT            = 'z',
+  UI_KEY_VELOCITY            = 'y',
   UI_KEY_OBSLINE             = 'O',
   UI_KEY_H0                  = 'H',
   UI_KEY_OLAMBDA             = 'l',
@@ -68,10 +70,13 @@ enum option_keys_enum
   UI_KEY_LOOKBACKTIME        = 'b',
   UI_KEY_CRITICALDENSITY     = 'c',
   UI_KEY_VOLUME              = 'v',
+  UI_KEY_USEDVELOCITY        = 'Y',
   UI_KEY_LINEATZ             = 'i',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
+  UI_KEY_LISTLINES           = 1000,
+  UI_KEY_LISTLINESATZ,
 };
 
 
diff --git a/bin/crop/Makefile.am b/bin/crop/Makefile.am
index b644d67..c4a5450 100644
--- a/bin/crop/Makefile.am
+++ b/bin/crop/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astcrop
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astcrop_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astcrop_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                -lgnuastro $(CONFIG_LDADD)
 
 astcrop_SOURCES = main.c ui.c crop.c wcsmode.c onecrop.c
 
diff --git a/bin/crop/args.h b/bin/crop/args.h
index 27ab92d..7eddd38 100644
--- a/bin/crop/args.h
+++ b/bin/crop/args.h
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,7 +36,7 @@ struct argp_option program_options[] =
       UI_KEY_MODE,
       "STR",
       0,
-      "Coordinate mode `img' or `wcs'.",
+      "Coordinate mode 'img' or 'wcs'.",
       GAL_OPTIONS_GROUP_INPUT,
       &p->mode,
       GAL_TYPE_STRING,
@@ -90,6 +90,19 @@ struct argp_option program_options[] =
 
     /* Output. */
     {
+      "primaryimghdu",
+      UI_KEY_PRIMARYIMGHDU,
+      0,
+      0,
+      "Write crop in primary/zero-th HDU of output.",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->primaryimghdu,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
       "noblank",
       UI_KEY_NOBLANK,
       0,
@@ -182,7 +195,7 @@ struct argp_option program_options[] =
     {
       "catalog",
       UI_KEY_CATALOG,
-      "STR",
+      "FITS/TXT",
       0,
       "Input catalog filename.",
       UI_GROUP_CENTER_CATALOG,
@@ -265,16 +278,30 @@ struct argp_option program_options[] =
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
-      GAL_OPTIONS_NOT_SET
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_colon_sep_csv
     },
     {
-      "outpolygon",
-      UI_KEY_OUTPOLYGON,
+      "polygonout",
+      UI_KEY_POLYGONOUT,
       0,
       0,
       "Keep the polygon's outside, mask the inside.",
       UI_GROUP_REGION,
-      &p->outpolygon,
+      &p->polygonout,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "polygonsort",
+      UI_KEY_POLYGONSORT,
+      0,
+      0,
+      "Sort polygon vertices as counter-clockwise.",
+      UI_GROUP_REGION,
+      &p->polygonsort,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
       GAL_OPTIONS_NOT_MANDATORY,
diff --git a/bin/crop/astcrop.conf b/bin/crop/astcrop.conf
index 07f701d..b3c45f8 100644
--- a/bin/crop/astcrop.conf
+++ b/bin/crop/astcrop.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astcrop                        # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/crop/authors-cite.h b/bin/crop/authors-cite.h
index b910952..a31f3cf 100644
--- a/bin/crop/authors-cite.h
+++ b/bin/crop/authors-cite.h
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/crop/crop.c b/bin/crop/crop.c
index 88802a9..1bc2183 100644
--- a/bin/crop/crop.c
+++ b/bin/crop/crop.c
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -31,6 +31,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #include <gnuastro/fits.h>
 #include <gnuastro/threads.h>
+#include <gnuastro/pointer.h>
 
 #include <gnuastro-internal/timing.h>
 #include <gnuastro-internal/checkset.h>
@@ -115,7 +116,7 @@ crop_verbose_final(struct cropparams *p)
           case 3:
             /* When the center wasn't checked it has a value of -1, and
                when it was checked and the center was filled, it has a
-               value of 1. So if `array[i]==0', we know that the file was
+               value of 1. So if 'array[i]==0', we know that the file was
                removed. */
             for(i=0;i<p->numout;++i)
               {
@@ -184,7 +185,7 @@ crop_write_to_log(struct onecropparams *crp)
 
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-                "the problem. The value of %zu is not valid for `counter'",
+                "the problem. The value of %zu is not valid for 'counter'",
                 __func__, PACKAGE_BUGREPORT, counter);
         }
     }
@@ -225,7 +226,7 @@ crop_mode_img(void *inparam)
       onecrop(crp);
 
       /* If there was no overlap, then no FITS pointer is created, so
-         `numimg' should be set to zero. */
+         'numimg' should be set to zero. */
       if(crp->outfits==NULL) crp->numimg=0;
 
       /* Check the final output: */
@@ -312,10 +313,12 @@ crop_mode_wcs(void *inparam)
             if(crp->name==NULL) onecrop_name(crp);
 
             /* Increment the number of images used (necessary for the
-               header keywords that are written in `onecrop'). Then do the
-               crop. */
+               header keywords that are written in 'onecrop'). Then do the
+               crop. However, the previously WCS-based overlap can be
+               slightly different from the final overlap, so if we finally
+               don't find any overlap we'll decrement the 'numimg'. */
             ++crp->numimg;
-            onecrop(crp);
+            if( onecrop(crp)==0 ) --crp->numimg;
 
             /* Close the file. */
             status=0;
@@ -325,9 +328,9 @@ crop_mode_wcs(void *inparam)
       while ( ++(crp->in_ind) < p->numin );
 
 
-      /* Correct in_ind. The loop above went until `in_ind' is one more
+      /* Correct in_ind. The loop above went until 'in_ind' is one more
          than the index for the last input image (that is how it exited the
-         loop). But `crp->in_ind' is needed later, so correct it here. */
+         loop). But 'crp->in_ind' is needed later, so correct it here. */
       --crp->in_ind;
 
 
@@ -402,6 +405,7 @@ crop(struct cropparams *p)
   int err=0;
   char *tmp;
   pthread_t t; /* We don't use the thread id, so all are saved here. */
+  char *mmapname;
   pthread_attr_t attr;
   pthread_barrier_t b;
   struct onecropparams *crp;
@@ -420,14 +424,15 @@ crop(struct cropparams *p)
   errno=0;
   crp=malloc(nt*sizeof *crp);
   if(crp==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `crp'",
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'crp'",
           __func__, nt*sizeof *crp);
 
 
   /* Distribute the indexs into the threads (for clarity, this is needed
      even if we only have one object). */
-  gal_threads_dist_in_threads(p->catname ? p->numout : 1, nt,
-                              &indexs, &thrdcols);
+  mmapname=gal_threads_dist_in_threads(p->catname ? p->numout : 1, nt,
+                                       p->cp.minmapsize, p->cp.quietmmap,
+                                       &indexs, &thrdcols);
 
 
   /* Run the job, if there is only one thread, don't go through the
@@ -485,7 +490,8 @@ crop(struct cropparams *p)
     }
 
   /* Print the final verbose info, save log, and clean up: */
+  if(mmapname) gal_pointer_mmap_free(&mmapname, p->cp.quietmmap);
+  else         free(indexs);
   crop_verbose_final(p);
-  free(indexs);
   free(crp);
 }
diff --git a/bin/crop/crop.h b/bin/crop/crop.h
index d9f6fd2..5d7fbba 100644
--- a/bin/crop/crop.h
+++ b/bin/crop/crop.h
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/crop/main.c b/bin/crop/main.c
index a303b3f..16693dc 100644
--- a/bin/crop/main.c
+++ b/bin/crop/main.c
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/crop/main.h b/bin/crop/main.h
index 528645e..f18a88c 100644
--- a/bin/crop/main.h
+++ b/bin/crop/main.h
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -85,9 +85,10 @@ struct cropparams
   size_t               hendwcs;  /* Header keyword No. to end read WCS.   */
   int                     mode;  /* Image or WCS mode.                    */
   uint8_t       zeroisnotblank;  /* ==1: In float or double, keep 0.0.    */
+  uint8_t        primaryimghdu;  /* ==1: write in primary/0-th HDU.       */
   uint8_t              noblank;  /* ==1: no blank (out of image) pixels.  */
   char                 *suffix;  /* Ending of output file name.           */
-  gal_data_t    *incheckcenter;  /* Value given to `--checkcenter'.       */
+  gal_data_t    *incheckcenter;  /* Value given to '--checkcenter'.       */
   gal_data_t           *center;  /* Center position of crop.              */
   gal_data_t            *width;  /* Width of crop when defined by center. */
   char                *catname;  /* Name of input catalog.                */
@@ -95,8 +96,9 @@ struct cropparams
   char                *namecol;  /* Filename (without suffix) of crop col.*/
   gal_list_str_t     *coordcol;  /* Column in cat containing coordinates. */
   char                *section;  /* Section string.                       */
-  char                *polygon;  /* Input string of polygon vertices.     */
-  uint8_t           outpolygon;  /* ==1: Keep the inner polygon region.   */
+  gal_data_t          *polygon;  /* Input string of polygon vertices.     */
+  uint8_t           polygonout;  /* ==1: Keep the inner polygon region.   */
+  uint8_t          polygonsort;  /* Don't sort polygon vertices.          */
 
   /* Internal */
   size_t                 numin;  /* Number of input images.               */
diff --git a/bin/crop/onecrop.c b/bin/crop/onecrop.c
index 55c85cd..470e966 100644
--- a/bin/crop/onecrop.c
+++ b/bin/crop/onecrop.c
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -91,8 +91,8 @@ onecrop_parse_section(struct cropparams *p, size_t *dsize,
   p->checkcenter=0;
 
 
-  /* Initialize the fpixel and lpixel arrays (note that `section' is only
-     defined in image mode, so there will only be one element in `imgs'. */
+  /* Initialize the fpixel and lpixel arrays (note that 'section' is only
+     defined in image mode, so there will only be one element in 'imgs'. */
   for(i=0;i<ndim;++i)
     {
       fpixel[i] = 1;
@@ -100,7 +100,7 @@ onecrop_parse_section(struct cropparams *p, size_t *dsize,
     }
 
 
-  /* Parse the string: `forl': "first-or-last". */
+  /* Parse the string: 'forl': "first-or-last". */
   while(*pt!='\0')
     {
       add=0;
@@ -109,7 +109,7 @@ onecrop_parse_section(struct cropparams *p, size_t *dsize,
         case ',':
           ++dim;
           if(dim>=ndim)
-            error(EXIT_FAILURE, 0, "Extra `,` in `%s`", p->section);
+            error(EXIT_FAILURE, 0, "Extra ',' in '%s'", p->section);
           forl='f';
           ++pt;
           break;
@@ -119,7 +119,7 @@ onecrop_parse_section(struct cropparams *p, size_t *dsize,
           break;
         case '.':
           error(EXIT_FAILURE, 0, "the numbers in the argument to "
-                "`--section` (`-s') have to be integers. You input "
+                "'--section' ('-s') have to be integers. You input "
                 "includes a float number: %s", p->section);
           break;
         case ' ': case '\t':
@@ -138,10 +138,10 @@ onecrop_parse_section(struct cropparams *p, size_t *dsize,
 
         /* An un-recognized character should crash the program. */
         default:
-          error(EXIT_FAILURE, 0, "value to `--section' must only contain "
+          error(EXIT_FAILURE, 0, "value to '--section' must only contain "
                 "integer numbers and these special characters between them: "
-                "`,', `:', `*' when necessary. But it is `%s' (the first "
-                "non-acceptable character is `%c').\n\n"
+                "',', ':', '*' when necessary. But it is '%s' (the first "
+                "non-acceptable character is '%c').\n\n"
                 "Please run the command below to learn more about this "
                 "option in Gnuastro's Crop program:\n\n"
                 "    $ info gnuastro \"Crop section syntax\"\n", p->section,
@@ -159,7 +159,7 @@ onecrop_parse_section(struct cropparams *p, size_t *dsize,
       /* Make sure if a number was read at all? */
       if(tailptr==pt)           /* No number was read!                 */
         {
-          if(add) read=0;       /* We have a * followed by `:' or `,'. */
+          if(add) read=0;       /* We have a * followed by ':' or ','. */
           else    continue;
         }
 
@@ -195,112 +195,6 @@ onecrop_parse_section(struct cropparams *p, size_t *dsize,
 
 
 
-
-void
-onecrop_parse_polygon(struct cropparams *p)
-{
-  size_t dim=0;
-  char *tailptr;
-  char *pt=p->polygon;
-  double read, *array;
-  gal_list_f64_t *vertices=NULL;
-
-  /* If control reached here, then the cropped region is not defined by its
-     center. So it makes no sense to check if the center is blank. */
-  p->checkcenter=0;
-
-  /* Parse the string. */
-  while(*pt!='\0')
-    {
-      switch(*pt)
-        {
-        case ',':
-          ++dim;
-          if(dim==2)
-            error(EXIT_FAILURE, 0, "Extra `,` in `%s`", p->polygon);
-          ++pt;
-          break;
-        case ':':
-          if(dim==0)
-            error(EXIT_FAILURE, 0, "not enough coordinates for at least "
-                  "one polygon vertex (in %s)", p->polygon);
-          dim=0;
-          ++pt;
-          break;
-        default:
-          break;
-        }
-
-      /* strtod will skip white spaces if they are before a number,
-         but not when they are before a : or ,. So we need to remove
-         all white spaces. White spaces are usually put beside each
-         other, so if one is encountered, go along the string until
-         the white space characters finish.  */
-      if(isspace(*pt))
-        ++pt;
-      else
-        {
-          /* Read the number: */
-          read=strtod(pt, &tailptr);
-
-          /* Check if there actually was a number.
-          printf("\n\n------\n%zu: %f (%s)\n", dim, read, tailptr);
-          */
-
-          /* Make sure if a number was read at all? */
-          if(tailptr==pt) /* No number was read! */
-            error(EXIT_FAILURE, 0, "%s could not be parsed as a floating "
-                  "point number", tailptr);
-
-          /* Check if there are no extra characters in the number, for
-             example we don't have a case like `1.00132.17', or
-             1.01i:2.0. Such errors are not uncommon when typing large
-             numbers, and if ignored, they can lead to unpredictable
-             results, so its best to abort and inform the user. */
-          if( *tailptr!='\0'
-              && !isspace(*tailptr)
-              && strchr(":,", *tailptr)==NULL )
-            error(EXIT_FAILURE, 0, "'%s' is an invalid floating point number "
-                  "sequence in the value to the `--polygon' option, error "
-                  "detected at '%s'", pt, tailptr);
-
-          /* Add the read coordinate to the list of coordinates. */
-          gal_list_f64_add(&vertices, read);
-
-          /* The job here is done, start from tailptr */
-          pt=tailptr;
-        }
-    }
-
-  /* Put the coordinates into an array while reversing their order so they
-     correspond to the user's order, then put it in the right place.*/
-  array=gal_list_f64_to_array(vertices, 1, &p->nvertices);
-  if(p->mode==IMGCROP_MODE_IMG) { p->ipolygon=array; p->wpolygon=NULL;  }
-  else                          { p->ipolygon=NULL;  p->wpolygon=array; }
-
-  /* The number of vertices is actually the number of nodes in the list
-     divided by the dimension of the dataset (note that we were counting
-     the dimension from 0. */
-  p->nvertices/=(dim+1);
-
-  /* For a check:
-  {
-    size_t i;
-    double *polygon=p->mode==IMGCROP_MODE_IMG?p->ipolygon:p->wpolygon;
-    for(i=0;i<p->nvertices;++i)
-      printf("(%f, %f)\n", polygon[i*2], polygon[i*2+1]);
-  }
-  exit(0);
-  */
-
-  /* Clean up: */
-  gal_list_f64_free(vertices);
-}
-
-
-
-
-
 static void
 onecrop_ipolygon_fl(double *ipolygon, size_t nvertices, long *fpixel,
                     long *lpixel)
@@ -334,7 +228,7 @@ onecrop_ipolygon_fl(double *ipolygon, size_t nvertices, 
long *fpixel,
     for(i=0;i<size;++i)                                                 \
       {                                                                 \
         point[0]=i%s1+1; point[1]=i/s1+1;                               \
-        if(gal_polygon_pin(ipolygon, point, nvertices)==outpolygon)     \
+        if((*isinside)(ipolygon, point, nvertices)==polygonout)         \
           ba[i]=*bb;                                                    \
       }                                                                 \
     free(bb);                                                           \
@@ -347,10 +241,10 @@ polygonmask(struct onecropparams *crp, void *array, long 
*fpixel_i,
 {
   int type=crp->p->type;
   double *ipolygon, point[2];
-  int outpolygon=crp->p->outpolygon;
+  int polygonout=crp->p->polygonout;
+  int (*isinside)(double *, double *, size_t);
   size_t i, *ordinds, size=s0*s1, nvertices=crp->p->nvertices;
 
-
   /* First of all, allocate enough space to put a copy of the input
      coordinates (we will be using that after sorting in an
      anti-clickwise manner.) */
@@ -363,17 +257,49 @@ polygonmask(struct onecropparams *crp, void *array, long 
*fpixel_i,
     error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for ordinds",
           __func__, nvertices*sizeof *ordinds);
 
+  /* If the user wants to sort the edges, do it. If not, make sure its in
+     counter-clockwise orientation. */
+  if(crp->p->polygonsort)
+    gal_polygon_vertices_sort(crp->ipolygon, nvertices, ordinds);
+  else
+    {
+      /* Keep the original order of the vertices, just make it
+         counter-clockwise. */
+      for(i=0;i<nvertices;++i) ordinds[i]=i;
+      gal_polygon_to_counterclockwise(crp->ipolygon, nvertices);
+    }
 
-  /* Find the order of the polygons and put the elements in the proper
-     order. Also subtract the fpixel_i coordinates from all the
-     vertices to bring them into the crop image coordinates.*/
-  gal_polygon_ordered_corners(crp->ipolygon, crp->p->nvertices, ordinds);
-  for(i=0;i<crp->p->nvertices;++i)
+  /* Fill the final polygon vertice positions within 'ipolygon' and also
+     the fpixel_i coordinates from all the vertices to bring them into the
+     crop image coordinates. */
+  for(i=0;i<nvertices;++i)
     {
       ipolygon[i*2  ] = crp->ipolygon[ordinds[i]*2]   - fpixel_i[0];
       ipolygon[i*2+1] = crp->ipolygon[ordinds[i]*2+1] - fpixel_i[1];
     }
 
+  /* Print a warning if we done the sorting, _and_ the sorted polygon is
+     concave, _and_ the user hasn't activated the quiet mode. Note that we
+     could'n do this immediately after calling 'gal_polygon_vertices_sort'
+     because that function doesn't touch the actual vertice values, it only
+     fills 'ordinds'. */
+  if(crp->p->polygonsort
+     && !crp->p->cp.quiet
+     && !gal_polygon_is_convex(ipolygon, nvertices) )
+    error(0, 0, "%s: warning: the given vertices belong to a concave "
+          "polygon, but there is no unique solution to the sorting of "
+          "concave polygons. Please check the cropped image, if it doesn't "
+          "correspond to your desired polygon, sort the vertices yourself "
+          "in counter-clockwise order _and_ don't use the '--polygonsort' "
+          "option", __func__);
+
+  /* Set the function for checking if a point is inside the polygon. For
+     concave polygons the process is more complex and thus
+     slower. Therefore when the polygon is convex, its better to use the
+     simpler/faster function. */
+  isinside = ( gal_polygon_is_convex(ipolygon, nvertices)
+               ? gal_polygon_is_inside_convex
+               : gal_polygon_is_inside );
 
   /* Go over all the pixels in the image and if they are within the
      polygon keep them if the user has asked for it.*/
@@ -521,7 +447,7 @@ onecrop_flpixel(struct onecropparams *crp)
         onecrop_parse_section(p, dsize, fpixel, lpixel);
       else if(p->polygon)       /* Defined by a polygon.  */
         {
-          if(p->outpolygon==0)
+          if(p->polygonout==0)
             onecrop_ipolygon_fl(p->ipolygon, p->nvertices, fpixel, lpixel);
         }
       else                      /* Defined by its center. */
@@ -537,12 +463,12 @@ onecrop_flpixel(struct onecropparams *crp)
         {
           /* Fill crp->ipolygon in wcspolygonpixel, then set flpixel*/
           fillcrpipolygon(crp);
-          if(p->outpolygon==0)
+          if(p->polygonout==0)
             onecrop_ipolygon_fl(crp->ipolygon, p->nvertices, fpixel, lpixel);
         }
       else
         {
-          /* Convert `crp->world' (in WCS) into `pixcrd' (image coord). */
+          /* Convert 'crp->world' (in WCS) into 'pixcrd' (image coord). */
           if(wcss2p(p->imgs[crp->in_ind].wcs, ncoord, ndim, crp->world,
                     phi, theta, imgcrd, pixcrd, &status) )
             if(status)
@@ -564,7 +490,7 @@ onecrop_flpixel(struct onecropparams *crp)
 
   /* If the user only wants regions outside to the polygon, then set
      the fpixel and lpixel to cover the full input image. */
-  if(p->polygon && p->outpolygon)
+  if(p->polygon && p->polygonout)
     {
       crp->fpixel[0]=crp->fpixel[1]=1;
       crp->lpixel[0]=dsize[1];
@@ -622,14 +548,17 @@ onecrop_make_array(struct onecropparams *crp, long 
*fpixel_i,
 
 
   /* Create the FITS file with a blank first extension, then close it, so
-     with the next `fits_open_file', we build the image in the second
+     with the next 'fits_open_file', we build the image in the second
      extension. This way, atleast for Gnuastro's outputs, we can
-     consistently use `-h1' (something like how you count columns, or
+     consistently use '-h1' (something like how you count columns, or
      generally everything from 1). */
   if(fits_create_file(&ofp, outname, &status))
     gal_fits_io_error(status, "creating file");
-  fits_create_img(ofp, SHORT_IMG, 0, naxes, &status);
-  fits_close_file(ofp, &status);
+  if(crp->p->primaryimghdu==0)
+    {
+      fits_create_img(ofp, SHORT_IMG, 0, naxes, &status);
+      fits_close_file(ofp, &status);
+    }
 
 
   /* Create the output crop image. */
@@ -643,7 +572,7 @@ onecrop_make_array(struct onecropparams *crp, long 
*fpixel_i,
   /* When CFITSIO creates a FITS extension it adds two comments linking to
      the FITS paper. Since we are mentioning the version of CFITSIO and
      only use its routines to read/write from/to FITS files, this is
-     redundant. If `status!=0', then `gal_fits_io_error' will abort, but in
+     redundant. If 'status!=0', then 'gal_fits_io_error' will abort, but in
      case CFITSIO doesn't write the comments, status will become
      non-zero. So we are resetting it to zero after these (because not
      being able to delete them isn't an error). */
@@ -664,7 +593,7 @@ onecrop_make_array(struct onecropparams *crp, long 
*fpixel_i,
                       &status);
       gal_fits_io_error(status, "writing BUNIT");
     }
-  rkey->name=NULL;              /* `name' wasn't allocated. */
+  rkey->name=NULL;              /* 'name' wasn't allocated. */
   gal_data_free(rkey);
 
 
@@ -720,31 +649,32 @@ onecrop_make_array(struct onecropparams *crp, long 
*fpixel_i,
 /* The starting and ending points are set in the onecropparams structure
    for one crop from one image. Crop that region out of the input.
 
-   On`basekeyname': To be safe, GCC 8.1 (and persumably later versions)
+   On'basekeyname': To be safe, GCC 8.1 (and persumably later versions)
    assumes that we are writing the full statically allocated space into
-   `regioinkey'! So it prints a warning that you may be writing outside the
+   'regioinkey'! So it prints a warning that you may be writing outside the
    allocated space! With these variables, we are ultimately just writing
    the file counters, so we can never (with current techologies!!!) exceed
-   `FLEN_KEYWORD' (which is 75 characters). To avoid compiler warnings, we
-   are just removing a few characters (`FLEN_KEYWORD-5') to allow the
+   'FLEN_KEYWORD' (which is 75 characters). To avoid compiler warnings, we
+   are just removing a few characters ('FLEN_KEYWORD-5') to allow the
    suffix and remove the warnings. */
-void
+int
 onecrop(struct onecropparams *crp)
 {
   struct cropparams *p=crp->p;
   struct inputimgs *img=&p->imgs[crp->in_ind];
 
   void *array;
+  int returnvalue=1;
   int status=0, anynul=0;
   fitsfile *ifp=crp->infits, *ofp;
-  char basekeyname[FLEN_KEYWORD-5];     /* `-5': avoid gcc 8.1+ warnings! */
+  char basekeyname[FLEN_KEYWORD-5];     /* '-5': avoid gcc 8.1+ warnings! */
   gal_fits_list_key_t *headers=NULL;    /* See above comment for more.    */
   size_t i, j, cropsize=1, ndim=img->ndim;
   char region[FLEN_VALUE], regionkey[FLEN_KEYWORD];
   long fpixel_o[MAXDIM], lpixel_o[MAXDIM], inc[MAXDIM];
   long naxes[MAXDIM], fpixel_i[MAXDIM], lpixel_i[MAXDIM];
 
-  /* Fill the `naxes' and `inc' arrays. */
+  /* Fill the 'naxes' and 'inc' arrays. */
   for(i=0;i<ndim;++i)
     {
       inc[ i ]   = 1;
@@ -753,7 +683,7 @@ onecrop(struct onecropparams *crp)
 
 
   /* Find the first and last pixel of this crop box from this input
-     image. Then copy the first and last pixels into the `_i' arrays.*/
+     image. Then copy the first and last pixels into the '_i' arrays.*/
   onecrop_flpixel(crp);
   memcpy(fpixel_i, crp->fpixel, ndim*sizeof *fpixel_i);
   memcpy(lpixel_i, crp->lpixel, ndim*sizeof *lpixel_i);
@@ -815,7 +745,7 @@ onecrop(struct onecropparams *crp)
 
 
       /* Write the selected region of this image as a string to include as
-         a FITS keyword. Then we want to delete the last coma `,'.*/
+         a FITS keyword. Then we want to delete the last coma ','.*/
       j=0;
       for(i=0;i<ndim;++i)
         j += sprintf(&region[j], "%ld:%ld,", fpixel_i[i], lpixel_i[i]);
@@ -829,7 +759,7 @@ onecrop(struct onecropparams *crp)
       sprintf(regionkey, "%sPIX", basekeyname);
       gal_fits_key_list_add_end(&headers, GAL_TYPE_STRING, regionkey,
                                 0, region, 0, "Range of pixels used for "
-                                "this output.", 0, NULL);
+                                "this output.", 0, NULL, 0);
       gal_fits_key_write_in_ptr(&headers, ofp);
 
 
@@ -837,11 +767,14 @@ onecrop(struct onecropparams *crp)
       free(array);
     }
   else
-    if(p->polygon && p->outpolygon==0 && p->mode==IMGCROP_MODE_WCS)
-      free(crp->ipolygon);
+    {
+      returnvalue=0;
+      if(p->polygon && p->polygonout==0 && p->mode==IMGCROP_MODE_WCS)
+        free(crp->ipolygon);
+    }
 
   /* The crop is complete. */
-  return;
+  return returnvalue;
 }
 
 
@@ -934,7 +867,7 @@ onecrop_center_filled(struct onecropparams *crp)
   free(array);
 
   /* CFITSIO already checks if there are any blank pixels. If there are,
-     then `anynul' will be 1, if there aren't it will be 0. So the output
+     then 'anynul' will be 1, if there aren't it will be 0. So the output
      of this function is just the inverse of that number. */
   return !anynul;
 }
diff --git a/bin/crop/onecrop.h b/bin/crop/onecrop.h
index 8c32101..b89e477 100644
--- a/bin/crop/onecrop.h
+++ b/bin/crop/onecrop.h
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -60,12 +60,9 @@ struct onecropparams
 };
 
 void
-onecrop_parse_polygon(struct cropparams *p);
-
-void
 onecrop_name(struct onecropparams *crp);
 
-void
+int
 onecrop(struct onecropparams *crp);
 
 int
diff --git a/bin/crop/ui.c b/bin/crop/ui.c
index e2dd9de..a1bc980 100644
--- a/bin/crop/ui.c
+++ b/bin/crop/ui.c
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -173,18 +173,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct cropparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -229,14 +229,14 @@ ui_parse_coordinate_mode(struct argp_option *option, char 
*arg,
       if      (!strcmp(arg, "img")) *(int *)(option->value)=IMGCROP_MODE_IMG;
       else if (!strcmp(arg, "wcs")) *(int *)(option->value)=IMGCROP_MODE_WCS;
       else
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' (value to "
-                      "`--mode') not recognized as an input mode. "
-                      "Recognized values are `img' and `wcs'. This option "
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
+                      "'--mode') not recognized as an input mode. "
+                      "Recognized values are 'img' and 'wcs'. This option "
                       "is necessary to identify the nature of your input "
                       "coordinates.\n\n"
                       "Please run the following command for more "
-                      "information (press the `SPACE' key to go down and "
-                      "`q' to return to the command-line):\n\n"
+                      "information (press the 'SPACE' key to go down and "
+                      "'q' to return to the command-line):\n\n"
                       "    $ info gnuastro \"Crop modes\"\n", arg);
       return NULL;
     }
@@ -265,7 +265,7 @@ ui_parse_coordinate_mode(struct argp_option *option, char 
*arg,
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct cropparams *p)
 {
@@ -281,8 +281,8 @@ ui_read_check_only_options(struct cropparams *p)
     {
     case 0:
       error(EXIT_FAILURE, 0, "no crop definition. You can use any of the "
-            "following options to define the crop(s): `--center', "
-            "`--catalog', `--section', or `--polygon'. Please run this "
+            "following options to define the crop(s): '--center', "
+            "'--catalog', '--section', or '--polygon'. Please run this "
             "command for more information:\n\n"
             "    $ info gnuastro \"Crop modes\"\n");
     case 1:
@@ -293,10 +293,10 @@ ui_read_check_only_options(struct cropparams *p)
             "run, only one crop definition is acceptable on the "
             "command-line or in configuration files. You have called: "
             "%s%s%s%s\b\b.",
-            p->center!=NULL  ? "`--center', " : "",
-            p->catname!=NULL ? "`--catalog', " : "",
-            p->section!=NULL ? "`--section', " : "",
-            p->polygon!=NULL ? "`--polygon', " : "");
+            p->center!=NULL  ? "'--center', " : "",
+            p->catname!=NULL ? "'--catalog', " : "",
+            p->section!=NULL ? "'--section', " : "",
+            p->polygon!=NULL ? "'--polygon', " : "");
     }
 
 
@@ -306,7 +306,7 @@ ui_read_check_only_options(struct cropparams *p)
       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' "
+          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);
     }
@@ -317,23 +317,23 @@ ui_read_check_only_options(struct cropparams *p)
     {
       /* We only want a single number. */
       if(p->incheckcenter->size>1)
-        error(EXIT_FAILURE, 0, "%zu values given to `--checkcenter'. This "
+        error(EXIT_FAILURE, 0, "%zu values given to '--checkcenter'. This "
               "option only takes one value currently",
               p->incheckcenter->size);
 
       darray=p->incheckcenter->array;
       if(*darray<0.0f)
         error(EXIT_FAILURE, 0, "negative value (%f) given to "
-              "`--checkcenter'. This option only takes positive values",
+              "'--checkcenter'. This option only takes positive values",
               *darray);
     }
 
 
   /* Section is currently only defined in Image mode. */
   if(p->section && p->mode!=IMGCROP_MODE_IMG)
-    error(EXIT_FAILURE, 0, "The `--section' option is only available in "
+    error(EXIT_FAILURE, 0, "The '--section' option is only available in "
           "image coordinate mode, currently it doesn't work with WCS mode. "
-          "Please run with `--mode=img' and if necessary, change the "
+          "Please run with '--mode=img' and if necessary, change the "
           "values accordingly");
 
 
@@ -343,15 +343,15 @@ ui_read_check_only_options(struct cropparams *p)
       /* If the searchin option has been given. */
       if(p->cp.searchin==GAL_TABLE_SEARCH_INVALID)
         error(EXIT_FAILURE, 0, "%s: no field specified to search for "
-              "columns. Please use the `--searchin' option to specify "
-              "which column meta-data you would like to search in: `name', "
-              "`unit' and `comment'. You may also select columns by their "
+              "columns. Please use the '--searchin' option to specify "
+              "which column meta-data you would like to search in: 'name', "
+              "'unit' and 'comment'. You may also select columns by their "
               "number, which won't use this option, but for complentess its "
               "best for this option to have a value", p->catname);
 
       /* If it is a FITS file, we need the HDU. */
       if( gal_fits_name_is_fits(p->catname) && p->cathdu==NULL )
-        error(EXIT_FAILURE, 0, "%s: no hdu given. Please use the `--cathdu' "
+        error(EXIT_FAILURE, 0, "%s: no hdu given. Please use the '--cathdu' "
               "option to specify which extension contains the table",
               p->catname);
 
@@ -360,7 +360,7 @@ ui_read_check_only_options(struct cropparams *p)
          given, so we only need to check one of the two in each couple. */
       if(p->coordcol==NULL)
         error(EXIT_FAILURE, 0, "no crop center columns given to read from "
-              "the input catalog (`%s'). Please use `--coordcol' several "
+              "the input catalog ('%s'). Please use '--coordcol' several "
               "times (depending on dimensionality) to specify the column "
               "keeping the center position the respective dimension.\n\n"
               "For more information on how to select columns in Gnuastro, "
@@ -373,16 +373,32 @@ ui_read_check_only_options(struct cropparams *p)
      in the proper format. */
   if(p->polygon)
     {
-      onecrop_parse_polygon(p);
+      /* The number of vertices is half the total number of given values
+         (currently only 2D spaces are considered so each vertice has 2
+         coordinates. */
+      p->nvertices=p->polygon->size/2;
+
+      /* Basic sanity checks. */
       if(p->nvertices<3)
         error(EXIT_FAILURE, 0, "a polygon has to have 3 or more vertices, "
-              "you have only given %zu (%s)", p->nvertices, p->polygon);
-      if(p->outpolygon && p->numin>1)
-        error(EXIT_FAILURE, 0, "currently in WCS mode, outpolygon can only "
-              "be set to zero when there is one image, you have given %zu "
-              "images. For multiple images the region will be very large. "
-              "It is best if you first crop out the larger region you want "
-              "into one image, then mask the polygon", p->numin);
+              "you have only given %zu", p->nvertices);
+      if(p->polygonout && p->numin>1)
+        error(EXIT_FAILURE, 0, "currently in WCS mode, '--polygonout' can "
+              "only be set to zero when there is one image, you have given "
+              "%zu images. For multiple images the region will be very "
+              "large. It is best if you first crop out the larger region "
+              "you want into one image, then mask the polygon", p->numin);
+
+      /* Put the coordinates into an array while reversing their order so
+         they correspond to the user's order, then put it in the right
+         place.*/
+      darray=p->polygon->array;
+      if(p->mode==IMGCROP_MODE_IMG) {p->ipolygon=darray; p->wpolygon=NULL;  }
+      else                          {p->ipolygon=NULL;   p->wpolygon=darray;}
+
+      /* We know that the cropped region is not defined by its center. So
+         it makes no sense to check if the center is blank. */
+      p->checkcenter=0;
     }
   else
     p->wpolygon=p->ipolygon=NULL;
@@ -390,7 +406,7 @@ ui_read_check_only_options(struct cropparams *p)
 
   /* If we are in WCS mode, noblanks must be off */
   if(p->mode==IMGCROP_MODE_WCS && p->noblank)
-    error(EXIT_FAILURE, 0, "`--noblanks` (`-b`) is only for image mode. "
+    error(EXIT_FAILURE, 0, "'--noblanks' ('-b') is only for image mode. "
           "You have called it with WCS mode");
 }
 
@@ -408,8 +424,8 @@ ui_check_options_and_arguments(struct cropparams *p)
   /* Make sure that a HDU is also given. */
   if(p->cp.hdu==NULL )
     error(EXIT_FAILURE, 0, "no HDU specified. When the input is a FITS "
-          "file, a HDU must also be specified, you can use the `--hdu' "
-          "(`-h') option and give it the HDU number (starting from "
+          "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");
 
   /* If in image mode, there should only be one input image. */
@@ -426,14 +442,14 @@ ui_check_options_and_arguments(struct cropparams *p)
   if(p->catname)
     {
       /* When multiple threads need to access a file, CFITSIO needs to be
-         configured with the `--enable-reentrant` option and we can only
-         know from the `fits_is_reentrant' function that came from CFITSIO
+         configured with the '--enable-reentrant' option and we can only
+         know from the 'fits_is_reentrant' function that came from CFITSIO
          version 3.30. */
 #if GAL_CONFIG_HAVE_FITS_IS_REENTRANT == 1
       if(p->cp.numthreads>1 && fits_is_reentrant()==0)
         {
           fprintf(stderr, "WARNING: CFITSIO was not configured with the "
-                  "`--enable-reentrant' option but you have asked to crop "
+                  "'--enable-reentrant' option but you have asked to crop "
                   "on %zu threads. Therefore only one thread will be used.\n\n"
                   "Please run the following command to learn more about "
                   "configuring CFITSIO:\n\n"
@@ -444,11 +460,11 @@ ui_check_options_and_arguments(struct cropparams *p)
       if(p->cp.numthreads>1)
         {
           fprintf(stderr, "WARNING: the installed CFITSIO version doesn't "
-                  "have `fits_is_reentrant' function (it is older than "
+                  "have 'fits_is_reentrant' function (it is older than "
                   "version 3.30). But you have asked to crop on %zu threads."
                   "Therefore only one thread will be used.\n\n"
                   "To avoid this warning, you can set the number of threads "
-                  "to one with `-N1' or update your installation of CFITSIO.",
+                  "to one with '-N1' or update your installation of CFITSIO.",
                   p->cp.numthreads);
           p->cp.numthreads=1;
         }
@@ -500,13 +516,13 @@ ui_set_img_sizes(struct cropparams *p)
   /* Make sure a width value is actually given. */
   if(p->width==NULL)
     error(EXIT_FAILURE, 0, "no crop width specified. When crops are "
-          "defined by their center (with `--center' or `--catalog') a "
-          "width is necessary (using the `--width' option)");
+          "defined by their center (with '--center' or '--catalog') a "
+          "width is necessary (using the '--width' option)");
 
   /* Make sure that the width array only has one element or the same number
      of elements as the input's dimensions. */
   if(p->width->size!=ndim && p->width->size!=1)
-    error(EXIT_FAILURE, 0, "%zu values give to `--width', but input is %zu "
+    error(EXIT_FAILURE, 0, "%zu values give to '--width', but input is %zu "
           "dimensions. It can only take either one value (same width in all "
           "dimensions), or the same number as the input's dimensions",
           p->width->size, ndim);
@@ -538,7 +554,7 @@ ui_set_img_sizes(struct cropparams *p)
         {
           /* Convert the width in units of the input's WCS into pixels. */
           pwidth = warray[i]/p->pixscale[i];
-          if(pwidth<3 || pwidth>50000)
+          if(pwidth<1 || pwidth>50000)
             error(EXIT_FAILURE, 0, "value %g (requested width along "
                   "dimension %zu) translates to %.0f pixels on this "
                   "dataset. This is probably not what you wanted. Note "
@@ -547,7 +563,7 @@ ui_set_img_sizes(struct cropparams *p)
                   "You can do the conversion to the dataset's WCS units "
                   "prior to calling Crop. Alternatively, you can specify "
                   "all the coordinates/sizes in image (not WCS) units and "
-                  "use the `--mode=img' option", warray[i], i+1, pwidth,
+                  "use the '--mode=img' option", warray[i], i+1, pwidth,
                   p->pixscale[i]);
 
           /* Write the single valued width in WCS and the image width for
@@ -594,7 +610,7 @@ ui_set_img_sizes(struct cropparams *p)
       /* Convert the floating point value to an integer. */
       p->checkcenter=GAL_DIMENSION_FLT_TO_INT(pcheckcenter);
 
-      /* If `checkcenter' isn't zero, but is even, convert it to an odd
+      /* If 'checkcenter' isn't zero, but is even, convert it to an odd
          number (so the actual center can be checked). */
       if(p->checkcenter && p->checkcenter%2==0) p->checkcenter += 1;
     }
@@ -625,9 +641,9 @@ ui_read_cols(struct cropparams *p)
          as the number of dimensions in the input dataset(s). */
       ncoordcols=gal_list_str_number(p->coordcol);
       if( ncoordcols < ndim)
-        error(EXIT_FAILURE, 0, "`--coordcol' was called %zu times, but the "
+        error(EXIT_FAILURE, 0, "'--coordcol' was called %zu times, but the "
               "input dataset%s %zu dimensions. Recall that through "
-              "`--coordcol' you are specifying the columns containing the "
+              "'--coordcol' you are specifying the columns containing the "
               "coordinates of the center of the crop in a catalog",
               ncoordcols, (p->numin==1?" has":"s have"), ndim);
 
@@ -636,7 +652,7 @@ ui_read_cols(struct cropparams *p)
       else if( ncoordcols > ndim )
         {
           /* Go over the columns find the last, but first initialize the
-             two (`lastcolstr' to avoid compiler warnings). */
+             two ('lastcolstr' to avoid compiler warnings). */
           lastcolstr=extracolstr=p->coordcol;
           for(i=0;i<ndim;++i)
             {
@@ -647,7 +663,7 @@ ui_read_cols(struct cropparams *p)
               extracolstr=extracolstr->next;
             }
 
-          /* Set the `next' element of the last node to NULL and free the
+          /* Set the 'next' element of the last node to NULL and free the
              extra ones. */
           lastcolstr->next=NULL;
           gal_list_str_free(extracolstr, 1);
@@ -657,7 +673,7 @@ ui_read_cols(struct cropparams *p)
     error(EXIT_FAILURE, 0, "no coordinate columns specified. When a catalog"
           "is given, it is necessary to identify which columns identify "
           "the coordinate values in which dimension.\n\n"
-          "You can do this by calling `--coordcol' multiple times, the "
+          "You can do this by calling '--coordcol' multiple times, the "
           "order must be in the same order as the input's dimensions. "
           "For more information on how to select columns in Gnuastro, "
           "please run the following command:\n\n"
@@ -695,7 +711,7 @@ ui_read_cols(struct cropparams *p)
     gal_tableintern_error_col_selection(p->catname, p->cathdu, "too many "
                                         "columns were selected by the given "
                                         "values to the options ending in "
-                                        "`col'.");
+                                        "'col'.");
 
 
   /* Put the information in each column in the proper place. */
@@ -731,7 +747,7 @@ ui_read_cols(struct cropparams *p)
         }
 
       /* Sanity check and clean up.  Note that it might happen that the
-         input structure is already freed. In that case, `corrtype' will be
+         input structure is already freed. In that case, 'corrtype' will be
          NULL. */
       if(corrtype)
         {
@@ -742,8 +758,8 @@ ui_read_cols(struct cropparams *p)
                   gal_fits_name_save_as_string(p->catname, p->cathdu),
                   colname);
 
-          /* Free the unnecessary sturcture information. The correct-type
-             (`corrtype') data structure's array is necessary for later
+          /* Free the unnecessary structure information. The correct-type
+             ('corrtype') data structure's array is necessary for later
              steps, so its pointer has been copied in the main program's
              structure. Hence, we should set the structure's pointer to
              NULL so the important data isn't freed.*/
@@ -768,10 +784,9 @@ ui_prepare_center(struct cropparams *p)
   errno=0;
   p->centercoords=malloc(ndim*sizeof *p->centercoords);
   if( p->centercoords==NULL )
-    error(EXIT_FAILURE, 0, "%s: %zu bytes for `p->centercoords'",
+    error(EXIT_FAILURE, 0, "%s: %zu bytes for 'p->centercoords'",
           __func__, ndim*sizeof *p->centercoords);
 
-
   /* Set the integer widths of the crop(s) when defined by center. */
   ui_set_img_sizes(p);
 
@@ -832,16 +847,146 @@ ui_make_log(struct cropparams *p)
 
 
 
+/* When there is a single image, to avoid complications in the WCS checks,
+   simply convert all the values to pixels and switch to image mode. */
+void
+ui_preparations_to_img_mode(struct cropparams *p)
+{
+  size_t i;
+  int nwcs;
+  double *darr, *pixscale;
+  struct wcsprm *wcs=gal_wcs_read(p->inputs->v, p->cp.hdu, p->hstartwcs,
+                                  p->hendwcs, &nwcs);
+
+  /* Make sure a WCS actually exists. */
+  if(wcs==NULL)
+    error(EXIT_FAILURE, 0, "%s (hdu %s): the WCS structure is not "
+          "recognized or isn't present. Hence the WCS mode cannot be "
+          "used as input coordinates. You can try with pixel coordinates "
+          "using '--mode=img'", p->inputs->v, p->cp.hdu);
+
+  /* If the coordinates are in galactic mode, inform the user. */
+  if( !p->cp.quiet
+      && (strcmp(wcs->lngtyp, "RA") || strcmp(wcs->lattyp,"DEC")) )
+    error(EXIT_SUCCESS, 0, "%s (hdu %s): uses the Galactic "
+          "coordinate system for its WCS. If your given point(s) "
+          "are in the Equatorial coordinate system (RA, Dec), you "
+          "will get wrong results. You can suppress this warning "
+          "with '--quiet'", p->inputs->v, p->cp.hdu);
+
+  /* Convert the given coordinates. */
+  if(p->center || p->catname)
+    {
+      /* Check the requested width and convert it to pixels. */
+      darr=p->width->array;
+      if(wcs->naxis<p->width->size)
+        error(EXIT_FAILURE, 0, "%s (hdu %s): its WCS has %d dimensions "
+              "but %zu dimensions given to '--width'", p->inputs->v,
+              p->cp.hdu, wcs->naxis, p->width->size);
+      pixscale=gal_wcs_pixel_scale(wcs);
+      for(i=0;i<p->width->size;++i) darr[i] /= pixscale[i];
+      free(pixscale);
+    }
+
+  /* Switch the mode and return. */
+  p->mode=IMGCROP_MODE_IMG;
+  wcsfree(wcs);
+}
+
+
+
+
+
+static void
+ui_preparations_to_img_mode_values(struct cropparams *p)
+{
+  struct wcsprm *wcs=p->imgs[0].wcs;
+
+  int i, status, *stat=NULL;
+  gal_data_t *tmp, *coords=NULL;
+  size_t ndim=wcs->naxis, plysize=p->nvertices;
+  double *phi=NULL, *theta=NULL, *pixcrd=NULL, *imgcrd=NULL;
+
+  /* When central coordinates (either from '--center' or '--catalog' are
+     given). */
+  if(p->centercoords)
+    {
+      /* Put the coordinate arrays in a list (note that they are in reverse
+         mode). */
+      for(i=ndim-1;i>=0;--i)
+        gal_list_data_add_alloc(&coords, p->centercoords[i],
+                                GAL_TYPE_FLOAT64, 1, &p->numout, NULL,
+                                0, p->cp.minmapsize, p->cp.quietmmap,
+                                NULL, NULL, NULL);
+
+      /* Convert the world coordinates to image coordinates. */
+      gal_wcs_world_to_img(coords, wcs, 1);
+
+      /* Clean up: we want the 'array' elements, so we'll set them to
+         NULL first, then clean up the list. */
+      for(tmp=coords;tmp!=NULL;tmp=tmp->next) tmp->array=NULL;
+      gal_list_data_free(coords);
+    }
+  else if (p->wpolygon)
+    {
+      /* Allocate the necessary arrays, directly calling WCSLIB is easier
+         in this case, because the polygon array for all the points is a
+         single array, not many columns. */
+      phi    = gal_pointer_allocate(GAL_TYPE_FLOAT64, plysize, 0,
+                                    __func__, "phi");
+      stat   = gal_pointer_allocate(GAL_TYPE_INT32, plysize, 1,
+                                    __func__, "stat");
+      theta  = gal_pointer_allocate(GAL_TYPE_FLOAT64, plysize, 0,
+                                    __func__, "theta");
+      imgcrd = gal_pointer_allocate(GAL_TYPE_FLOAT64, ndim*plysize, 0,
+                                    __func__, "imgcrd");
+      pixcrd = gal_pointer_allocate(GAL_TYPE_FLOAT64, ndim*plysize, 0,
+                                    __func__, "pixcrd");
+
+      /* Do the conversion. */
+      status=wcss2p(wcs, plysize, ndim, p->wpolygon, phi, theta, imgcrd,
+                    pixcrd, stat);
+      if(status)
+        error(EXIT_FAILURE, 0, "%s: wcss2p ERROR %d: %s", __func__, status,
+              wcs_errmsg[status]);
+
+      /* Replace the arrays. */
+      p->ipolygon=pixcrd;
+      free(p->wpolygon);
+      p->wpolygon=NULL;
+
+      /* Clean up. */
+      free(phi);
+      free(stat);
+      free(theta);
+      free(imgcrd);
+    }
+  else
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+          "the problem. the centercoords and wpolygon are both NULL",
+          __func__, PACKAGE_BUGREPORT);
+}
+
+
 
 void
 ui_preparations(struct cropparams *p)
 {
   fitsfile *tmpfits;
+  int internalimgmode=0;
   struct inputimgs *img;
   int status, firsttype=0;
   size_t input_counter, firstndim=0;
 
 
+  /* If there is only one dataset, convert the given coordinates to pixels
+     and switch to image mode. */
+  if(p->numin==1 && p->mode==IMGCROP_MODE_WCS)
+    {
+      internalimgmode=1;
+      ui_preparations_to_img_mode(p);
+    }
+
   /* For polygon and section, there should be no center checking. */
   if(p->polygon || p->section)
     p->checkcenter=0;
@@ -862,7 +1007,7 @@ ui_preparations(struct cropparams *p)
           __func__, p->numin*sizeof *p->imgs);
 
 
-  /* Fill in the WCS information of each image. */
+  /* Do basic checks of all input images. */
   input_counter=p->numin;
   while(p->inputs)
     {
@@ -885,10 +1030,11 @@ ui_preparations(struct cropparams *p)
         }
       else
         if(p->mode==IMGCROP_MODE_WCS)
-          error(EXIT_FAILURE, 0, "the WCS structure of %s (hdu: %s) "
-                "image is not recognized. So WCS mode cannot be used "
-                "as input coordinates. You can try with pixel coordinates "
-                "with `--mode=img'", img->name, p->cp.hdu);
+          error(EXIT_FAILURE, 0, "%s (hdu %s): the WCS structure is "
+                "not recognized or isn't present. Hence the WCS mode "
+                "cannot be used as input coordinates. You can try with "
+                "pixel coordinates using '--mode=img'", img->name,
+                p->cp.hdu);
       fits_close_file(tmpfits, &status);
       gal_fits_io_error(status, NULL);
 
@@ -911,17 +1057,17 @@ ui_preparations(struct cropparams *p)
              correspond to the dimensionality of the data. */
           if(p->center && p->center->size!=firstndim)
             error(EXIT_FAILURE, 0, "%s (hdu %s) has %zu dimensions, but "
-                  "%zu coordinates were given to `--center'", img->name,
+                  "%zu coordinates were given to '--center'", img->name,
                   p->cp.hdu, firstndim, p->center->size);
         }
       else
         {
           if(firsttype!=p->type)
-            error(EXIT_FAILURE, 0, "%s: type is `%s' while previous input(s) "
-                  "were `%s' type. All inputs must have the same pixel data "
+            error(EXIT_FAILURE, 0, "%s: type is '%s' while previous input(s) "
+                  "were '%s' type. All inputs must have the same pixel data "
                   "type.\n\nYou can use Gnuastro's Arithmetic program to "
-                  "convert `%s' to `%s', please run this command for more "
-                  "information (press `SPACE' for going down and `q' to "
+                  "convert '%s' to '%s', please run this command for more "
+                  "information (press 'SPACE' for going down and 'q' to "
                   "return to the command-line):\n\n"
                   "    $ info Arithmetic\n",
                   img->name, gal_type_name(p->type, 1),
@@ -946,16 +1092,22 @@ ui_preparations(struct cropparams *p)
           p->imgs->name, p->imgs->ndim);
 
 
-  /* Unify central crop methods into `p->centercoords'. */
+  /* Unify central crop methods into 'p->centercoords'. */
   if(p->catname || p->center)
     ui_prepare_center(p);
 
 
-  /* `ui_read_cols' set the number of output crops when a catalog was
+  /* 'ui_read_cols' set the number of output crops when a catalog was
      given, in all other cases, we only have one output. */
   if(p->catname==NULL) p->numout=1;
 
 
+  /* If there was only one input file and the WCS-mode was internally
+     changed to image-mode, then convert the coordinates too. */
+  if(internalimgmode)
+    ui_preparations_to_img_mode_values(p);
+
+
   /* Prepare the log file if the user has asked for it. */
   ui_make_log(p);
 }
@@ -991,9 +1143,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
cropparams *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
+  /* 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
diff --git a/bin/crop/ui.h b/bin/crop/ui.h
index de2cbae..9a34fa2 100644
--- a/bin/crop/ui.h
+++ b/bin/crop/ui.h
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -68,8 +68,10 @@ enum option_keys_enum
   UI_KEY_CATHDU         = 1000,
   UI_KEY_HSTARTWCS,
   UI_KEY_HENDWCS,
-  UI_KEY_OUTPOLYGON,
+  UI_KEY_POLYGONOUT,
+  UI_KEY_POLYGONSORT,
   UI_KEY_CHECKCENTER,
+  UI_KEY_PRIMARYIMGHDU,
 };
 
 
diff --git a/bin/crop/wcsmode.c b/bin/crop/wcsmode.c
index f57ef3d..a980f11 100644
--- a/bin/crop/wcsmode.c
+++ b/bin/crop/wcsmode.c
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -95,8 +95,8 @@ wcsmode_check_prepare(struct cropparams *p, struct inputimgs 
*img)
   if( strcmp(wcs->ctype[0], "RA---TAN")
       || strcmp(wcs->ctype[1], "DEC--TAN") )
     error(EXIT_FAILURE, 0, "currently the only WCS types usable are "
-          "`RA---TAN' and `DEC--TAN' for the first and second axises "
-          "respectively. The WCS types of `%s' (hdu %s) are `%s' and `%s' "
+          "'RA---TAN' and 'DEC--TAN' for the first and second axises "
+          "respectively. The WCS types of '%s' (hdu %s) are '%s' and '%s' "
           "respectively", img->name, p->cp.hdu, wcs->ctype[0], wcs->ctype[1]);
 
 
@@ -116,7 +116,7 @@ wcsmode_check_prepare(struct cropparams *p, struct 
inputimgs *img)
   if(p->pixscale)
     {
       for(i=0;i<ndim;++i)
-        if(p->pixscale[i] != pixscale[i])
+        if(fabs(p->pixscale[i]-pixscale[i])>1e-10) /* Floating point errors. */
           error(EXIT_FAILURE, 0, "%s (hdu %s): has resolution of %g along "
                 "dimension %d. However, previously checked input(s) had "
                 "a resolution of %g in this dimension", img->name, p->cp.hdu,
@@ -127,7 +127,7 @@ wcsmode_check_prepare(struct cropparams *p, struct 
inputimgs *img)
     p->pixscale=pixscale;
 
 
-  /* Set the coordinates of the dataset's corners. Note that `dsize' is in
+  /* Set the coordinates of the dataset's corners. Note that 'dsize' is in
      C order, while pixcrd is in FITS order.*/
   switch(ndim)
     {
@@ -168,8 +168,8 @@ wcsmode_check_prepare(struct cropparams *p, struct 
inputimgs *img)
 
 
   /* Fill in the size of the dataset in WCS from the first pixel in the
-     image. Note that `dsize' is in C axises, while the `pixscale',
-     `corners' and `sized' are in FITS axises. */
+     image. Note that 'dsize' is in C axises, while the 'pixscale',
+     'corners' and 'sized' are in FITS axises. */
   if(ndim==2)
     {
       img->sized[0] = ( img->dsize[1] * p->pixscale[0]
@@ -187,17 +187,17 @@ wcsmode_check_prepare(struct cropparams *p, struct 
inputimgs *img)
 
   /* In case the image crosses the equator, we will calculate these values
      here so later on, we don't have to calculate them on every check. See
-     the explanation above `point_in_dataset'.
+     the explanation above 'point_in_dataset'.
 
      Note that in both 2D and 3D data, the declination is in the second
      coordinate (index 1). */
   if( img->corners[1] * (img->corners[1]+img->sized[1]) < 0 )
     {
-      /* re in the comments above `point_in_dataset'. */
+      /* re in the comments above 'point_in_dataset'. */
       img->equatorcorr[0]=img->corners[0]
         -0.5*img->sized[0]*(1-cos(img->corners[1]*M_PI/180));
 
-      /* sre in the comments above `point_in_dataset'. */
+      /* sre in the comments above 'point_in_dataset'. */
       img->equatorcorr[1]=img->sized[0]*cos(img->corners[1]*M_PI/180);
     }
 
@@ -385,14 +385,14 @@ wcsmode_crop_corners(struct onecropparams *crp)
 
 
   /* In case the crop crosses the equator, then we need these two
-     corrections. See the complete explanations above `point_in_dataset'. */
+     corrections. See the complete explanations above 'point_in_dataset'. */
   if(crp->corners[1]*(crp->corners[1]+crp->sized[1]) < 0 )
     {
-      /* re in the explanations above `point_in_dataset'. */
+      /* re in the explanations above 'point_in_dataset'. */
       crp->equatorcorr[0]=crp->corners[0]
         -0.5*crp->sized[0]*(1-cos(crp->corners[1]*M_PI/180));
 
-      /* sre in the explanations above `point_in_dataset'. */
+      /* sre in the explanations above 'point_in_dataset'. */
       crp->equatorcorr[1]=crp->sized[0]*cos(crp->corners[1]*M_PI/180);
     }
 
@@ -559,7 +559,7 @@ fillcrpipolygon(struct onecropparams *crp)
                                         dp>=d1     &&      dp<=d1+sd
 
    For RA, things become a little more complicated (recall that
-   r1>r3). `n` is defined as half of the extra space between the top
+   r1>r3). 'n' is defined as half of the extra space between the top
    and bottom lines of the two trapezoids.
 
    (North) n=0.5*sr*(1/cos(dp-d1)-1) ==> rp<=r1+n   &&   rp>=r1-sr-n
@@ -572,7 +572,7 @@ fillcrpipolygon(struct onecropparams *crp)
    -------------------------
 
    When d1*(d1+sd)<0, the image crosses the equator (d1 is negative
-   and d1+sd is positive). In this case, we define `re` and `sre` as
+   and d1+sd is positive). In this case, we define 're' and 'sre' as
    an equivalent of r1 and sr but on the equator:
 
        re=r1-0.5*sr*(1-cos(d1))   &&   sre=sr*cos(d1)
diff --git a/bin/crop/wcsmode.h b/bin/crop/wcsmode.h
index 989a6e9..cb3ac23 100644
--- a/bin/crop/wcsmode.h
+++ b/bin/crop/wcsmode.h
@@ -5,7 +5,7 @@ Crop is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/fits/Makefile.am b/bin/fits/Makefile.am
index a112c75..869cbc1 100644
--- a/bin/fits/Makefile.am
+++ b/bin/fits/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astfits
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astfits_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astfits_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                -lgnuastro $(CONFIG_LDADD)
 
 astfits_SOURCES = main.c ui.c extension.c fits.c keywords.c
 
diff --git a/bin/fits/args.h b/bin/fits/args.h
index b4c6584..af1d90c 100644
--- a/bin/fits/args.h
+++ b/bin/fits/args.h
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -31,11 +31,71 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Array of acceptable options. */
 struct argp_option program_options[] =
   {
-    /* Input options */
     {
       0, 0, 0, 0,
-      "HDUs (extensions):",
-      UI_GROUP_EXTENSION
+      "HDU (extension) information:",
+      UI_GROUP_EXTENSION_INFORMATION
+    },
+    {
+      "numhdus",
+      UI_KEY_NUMHDUS,
+      0,
+      0,
+      "Print number of HDUs in the given FITS file.",
+      UI_GROUP_EXTENSION_INFORMATION,
+      &p->numhdus,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "datasum",
+      UI_KEY_DATASUM,
+      0,
+      0,
+      "Calculate HDU's datasum and print in stdout.",
+      UI_GROUP_EXTENSION_INFORMATION,
+      &p->datasum,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "pixelscale",
+      UI_KEY_PIXELSCALE,
+      0,
+      0,
+      "Return the pixel-scale of the HDU's WCS.",
+      UI_GROUP_EXTENSION_INFORMATION,
+      &p->pixelscale,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "skycoverage",
+      UI_KEY_SKYCOVERAGE,
+      0,
+      0,
+      "Image coverage in the WCS coordinates.",
+      UI_GROUP_EXTENSION_INFORMATION,
+      &p->skycoverage,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+
+
+
+
+    {
+      0, 0, 0, 0,
+      "HDU (extension) manipulation:",
+      UI_GROUP_EXTENSION_MANIPULATION
     },
     {
       "remove",
@@ -43,7 +103,7 @@ struct argp_option program_options[] =
       "STR",
       0,
       "Remove extension from input file.",
-      UI_GROUP_EXTENSION,
+      UI_GROUP_EXTENSION_MANIPULATION,
       &p->remove,
       GAL_TYPE_STRLL,
       GAL_OPTIONS_RANGE_ANY,
@@ -56,7 +116,7 @@ struct argp_option program_options[] =
       "STR",
       0,
       "Copy extension to output file.",
-      UI_GROUP_EXTENSION,
+      UI_GROUP_EXTENSION_MANIPULATION,
       &p->copy,
       GAL_TYPE_STRLL,
       GAL_OPTIONS_RANGE_ANY,
@@ -69,7 +129,7 @@ struct argp_option program_options[] =
       "STR",
       0,
       "Copy extension to output and remove from input.",
-      UI_GROUP_EXTENSION,
+      UI_GROUP_EXTENSION_MANIPULATION,
       &p->cut,
       GAL_TYPE_STRLL,
       GAL_OPTIONS_RANGE_ANY,
@@ -77,25 +137,12 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET
     },
     {
-      "numhdus",
-      UI_KEY_NUMHDUS,
-      0,
-      0,
-      "Print number of HDUs in the given FITS file.",
-      UI_GROUP_EXTENSION,
-      &p->numhdus,
-      GAL_OPTIONS_NO_ARG_TYPE,
-      GAL_OPTIONS_RANGE_0_OR_1,
-      GAL_OPTIONS_NOT_MANDATORY,
-      GAL_OPTIONS_NOT_SET
-    },
-    {
       "primaryimghdu",
       UI_KEY_PRIMARYIMGHDU,
       0,
       0,
       "Copy/cut image HDUs to primary/zero-th HDU.",
-      UI_GROUP_EXTENSION,
+      UI_GROUP_EXTENSION_MANIPULATION,
       &p->primaryimghdu,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
@@ -117,7 +164,7 @@ struct argp_option program_options[] =
       UI_KEY_ASIS,
       "STR",
       0,
-      "Write the argument string as is into the header.",
+      "Write value as-is (may corrupt FITS file).",
       UI_GROUP_KEYWORD,
       &p->asis,
       GAL_TYPE_STRLL,
@@ -126,6 +173,19 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET
     },
     {
+      "keyvalue",
+      UI_KEY_KEYVALUE,
+      "STR[,STR,...]",
+      0,
+      "Only print the value of requested keyword(s).",
+      UI_GROUP_KEYWORD,
+      &p->keyvalue,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
       "delete",
       UI_KEY_DELETE,
       "STR",
@@ -268,6 +328,19 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "wcsdistortion",
+      UI_KEY_WCSDISTORTION,
+      "STR",
+      0,
+      "Convert WCS distortion to another type.",
+      UI_GROUP_KEYWORD,
+      &p->wcsdistortion,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
 
 
 
@@ -287,6 +360,21 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "colinfoinstdout",
+      UI_KEY_COLINFOINSTDOUT,
+      0,
+      0,
+      "Column info/metadata when printing to stdout.",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->colinfoinstdout,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+
+
 
 
 
diff --git a/bin/fits/astfits.conf b/bin/fits/astfits.conf
index a69b5d9..f8f6add 100644
--- a/bin/fits/astfits.conf
+++ b/bin/fits/astfits.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astfits                        # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/fits/authors-cite.h b/bin/fits/authors-cite.h
index 9ae97d0..b0932a2 100644
--- a/bin/fits/authors-cite.h
+++ b/bin/fits/authors-cite.h
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/fits/extension.c b/bin/fits/extension.c
index b9325bc..a9c6423 100644
--- a/bin/fits/extension.c
+++ b/bin/fits/extension.c
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/fits/extension.h b/bin/fits/extension.h
index b54ff6a..709c7a3 100644
--- a/bin/fits/extension.h
+++ b/bin/fits/extension.h
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/fits/fits.c b/bin/fits/fits.c
index 6ecb6d6..19a7a1b 100644
--- a/bin/fits/fits.c
+++ b/bin/fits/fits.c
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -27,10 +27,12 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <string.h>
 #include <unistd.h>
 
+#include <gnuastro/wcs.h>
 #include <gnuastro/list.h>
 #include <gnuastro/fits.h>
 #include <gnuastro/blank.h>
 #include <gnuastro/pointer.h>
+#include <gnuastro/statistics.h>
 
 #include <gnuastro-internal/timing.h>
 #include <gnuastro-internal/checkset.h>
@@ -58,8 +60,8 @@ fits_has_error(struct fitsparams *p, int actioncode, char 
*string, int status)
     case FITS_ACTION_REMOVE:        action="removed";      break;
 
     default:
-      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at `%s' so we "
-            "can fix this problem. The value of `actioncode' must not be %d",
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' so we "
+            "can fix this problem. The value of 'actioncode' must not be %d",
             __func__, PACKAGE_BUGREPORT, actioncode);
     }
 
@@ -86,17 +88,18 @@ fits_print_extension_info(struct fitsparams *p)
 {
   uint16_t *ui16;
   fitsfile *fptr;
-  gal_data_t *cols=NULL, *tmp;
-  char **tstra, **estra, **sstra;
   size_t i, numext, *dsize, ndim;
+  int hascomments=0, hasblankname=0;
+  char **tstra, **estra, **sstra, **cmstra;
   int j, nc, numhdu, hdutype, status=0, type;
+  gal_data_t *tmp, *cols=NULL, *sizecol, *commentscol;
   char *msg, *tstr=NULL, sstr[1000], extname[FLEN_VALUE];
 
 
   /* Open the FITS file and read the first extension type, upon moving to
      the next extension, we will read its type, so for the first we will
      need to do it explicitly. */
-  fptr=gal_fits_hdu_open(p->filename, "0", READONLY);
+  fptr=gal_fits_hdu_open(p->input->v, "0", READONLY);
   if (fits_get_hdu_type(fptr, &hdutype, &status) )
     gal_fits_io_error(status, "reading first extension");
 
@@ -110,11 +113,13 @@ fits_print_extension_info(struct fitsparams *p)
   /* Allocate all the columns (in reverse order, since this is a simple
      linked list). */
   gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_STRING, 1, &numext, NULL, 1,
+                          -1, 1, "HDU_COMMENT", "note", "Possible comment");
+  gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_STRING, 1, &numext, NULL, 1,
                           -1, 1, "HDU_SIZE", "name", "Size of image or table "
                           "number of rows and columns.");
   gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_STRING, 1, &numext, NULL, 1,
                           -1, 1, "HDU_TYPE", "name", "Image data type or "
-                          "`table' format (ASCII or binary).");
+                          "'table' format (ASCII or binary).");
   gal_list_data_add_alloc(&cols, NULL, GAL_TYPE_STRING, 1, &numext, NULL, 1,
                           -1, 1, "EXTNAME", "name", "Extension name of this "
                           "HDU (EXTNAME in FITS).");
@@ -127,7 +132,12 @@ fits_print_extension_info(struct fitsparams *p)
   ui16  = cols->array;
   estra = cols->next->array;
   tstra = cols->next->next->array;
-  sstra = cols->next->next->next->array;
+
+  sizecol = cols->next->next->next;
+  sstra = sizecol->array;
+
+  commentscol = cols->next->next->next->next;
+  cmstra = commentscol->array;
 
   cols->next->disp_width=15;
   cols->next->next->disp_width=15;
@@ -154,7 +164,7 @@ fits_print_extension_info(struct fitsparams *p)
           break;
 
         default:
-          error(EXIT_FAILURE, 0, "%s: a bug! the `hdutype' code %d not "
+          error(EXIT_FAILURE, 0, "%s: a bug! the 'hdutype' code %d not "
                 "recognized", __func__, hdutype);
         }
 
@@ -169,6 +179,7 @@ fits_print_extension_info(struct fitsparams *p)
 
         case KEY_NO_EXIST:
           sprintf(extname, "%s", GAL_BLANK_STRING);
+          hasblankname=1;
           status=0;
           break;
 
@@ -178,9 +189,19 @@ fits_print_extension_info(struct fitsparams *p)
       status=0;
 
 
-      /* Write the size into a string. `sprintf' returns the number of
-         written characters (excluding the `\0'). So for each dimension's
-         size that is written, we add to `nc' (the number of
+      /* Check if its a healpix grid. */
+      if( gal_fits_hdu_is_healpix(fptr) )
+        {
+          hascomments=1;
+          gal_checkset_allocate_copy("HEALpix", cmstra+i);
+        }
+      else
+        gal_checkset_allocate_copy(GAL_BLANK_STRING, cmstra+i);
+
+
+      /* Write the size into a string. 'sprintf' returns the number of
+         written characters (excluding the '\0'). So for each dimension's
+         size that is written, we add to 'nc' (the number of
          characters). Note that FITS allows blank extensions, in those
          cases, return "0". */
       if(ndim>0)
@@ -225,21 +246,33 @@ fits_print_extension_info(struct fitsparams *p)
   /* Close the file. */
   fits_close_file(fptr, &status);
 
+  /* If there weren't any comments, don't print the comment column. */
+  if(hascomments==0)
+    {
+      sizecol->next=NULL;
+      gal_data_free(commentscol);
+    }
 
   /* Print the results. */
   if(!p->cp.quiet)
     {
       printf("%s\nRun on %s-----\n", PROGRAM_STRING, ctime(&p->rawtime));
-      printf("HDU (extension) information: `%s'.\n", p->filename);
-      printf(" Column 1: Index (counting from 0, usable with `--hdu').\n");
-      printf(" Column 2: Name (`EXTNAME' in FITS standard, usable with "
-             "`--hdu').\n");
-      printf(" Column 3: Image data type or `table' format (ASCII or "
+      printf("HDU (extension) information: '%s'.\n", p->input->v);
+      printf(" Column 1: Index (counting from 0, usable with '--hdu').\n");
+      printf(" Column 2: Name ('EXTNAME' in FITS standard, usable with "
+             "'--hdu').\n");
+      if(hasblankname)
+        printf("           ('%s' means that no name is specified for this "
+               "HDU)\n", GAL_BLANK_STRING);
+      printf(" Column 3: Image data type or 'table' format (ASCII or "
              "binary).\n");
       printf(" Column 4: Size of data in HDU.\n");
+      if(hascomments)
+        printf(" Column 5: Comments about the HDU (e.g., if its HEALpix, or "
+               "etc).\n");
       printf("-----\n");
     }
-  gal_table_write(cols, NULL, GAL_TABLE_FORMAT_TXT, NULL, NULL, 0);
+  gal_table_write(cols, NULL, NULL, GAL_TABLE_FORMAT_TXT, NULL, NULL, 0);
   gal_list_data_free(cols);
 }
 
@@ -254,7 +287,7 @@ fits_hdu_number(struct fitsparams *p)
   int numhdu, status=0;
 
   /* Read the first extension (necessary for reading the rest). */
-  fptr=gal_fits_hdu_open(p->filename, "0", READONLY);
+  fptr=gal_fits_hdu_open(p->input->v, "0", READONLY);
 
   /* Get the number of HDUs. */
   if( fits_get_num_hdus(fptr, &numhdu, &status) )
@@ -272,6 +305,195 @@ fits_hdu_number(struct fitsparams *p)
 
 
 static void
+fits_datasum(struct fitsparams *p)
+{
+  printf("%ld\n", gal_fits_hdu_datasum(p->input->v, p->cp.hdu));
+}
+
+
+
+
+
+static void
+fits_pixelscale(struct fitsparams *p)
+{
+  int nwcs=0;
+  size_t i, ndim=0;
+  struct wcsprm *wcs;
+  double multip, *pixelscale;
+
+  /* Read the desired WCS. */
+  wcs=gal_wcs_read(p->input->v, p->cp.hdu, 0, 0, &nwcs);
+
+  /* If a WCS doesn't exist, let the user know and return. */
+  if(wcs)
+    ndim=wcs->naxis;
+  else
+    error(EXIT_FAILURE, 0, "%s (hdu %s): no WCS could be read by WCSLIB, "
+          "hence the pixel-scale cannot be determined", p->input->v,
+          p->cp.hdu);
+
+  /* Calculate the pixel-scale in each dimension. */
+  pixelscale=gal_wcs_pixel_scale(wcs);
+
+  /* If not in quiet-mode, print some extra information. We don't want the
+     last number to have a space after it, so we'll write the last one
+     outside the loop.*/
+  if(p->cp.quiet==0)
+    {
+      printf("Basic information for --pixelscale (remove extra info "
+             "with '--quiet' or '-q')\n");
+      printf("  Input: %s (hdu %s) has %zu dimensions.\n", p->input->v,
+             p->cp.hdu, ndim);
+      printf("  Pixel scale in each FITS dimension:\n");
+      for(i=0;i<ndim;++i)
+        {
+          if( !strcmp(wcs->cunit[i], "deg") )
+            printf("    %zu: %g (%s/pixel) = %g (arcsec/pixel)\n", i+1,
+                   pixelscale[i], wcs->cunit[i], pixelscale[i]*3600);
+          else
+            printf("    %zu: %g (%s/slice)\n", i+1,
+                   pixelscale[i], wcs->cunit[i]);
+        }
+
+      /* Pixel area/volume. */
+      if(ndim>=2)
+        {
+          /* Multiply the values in each dimension. */
+          multip=pixelscale[0]*pixelscale[1];
+
+          /* Pixel scale (applicable to 2 or 3 dimensions). */
+          printf("  Pixel area%s:\n", ndim==2?"":" (on each 2D slice) ");
+          if( strcmp(wcs->cunit[0], wcs->cunit[1]) )
+            printf("    %g (%s*%s)\n", multip, wcs->cunit[0],
+                   wcs->cunit[1]);
+          else
+            if( strcmp(wcs->cunit[0], "deg") )
+              printf("    %g (%s^2)\n", multip, wcs->cunit[0]);
+            else
+              printf("    %g (deg^2) = %g (arcsec^2)\n",
+                     multip, multip*3600*3600 );
+
+          /* For a 3 dimensional dataset, we need need extra info. */
+          if(ndim>=3)
+            {
+              multip*=pixelscale[2];
+              printf("  Voxel volume:\n");
+              if( strcmp(wcs->cunit[0], wcs->cunit[1]) )
+                printf("    %g (%s*%s*%s)\n", multip, wcs->cunit[0],
+                       wcs->cunit[1], wcs->cunit[2]);
+              else
+                if( strcmp(wcs->cunit[0], "deg") )
+                  printf("    %g (%s^2*%s)\n", multip, wcs->cunit[0],
+                         wcs->cunit[2]);
+                else
+                  {
+                    if( strcmp(wcs->cunit[2], "m") )
+                      printf("    %g (deg^2*%s) = %g (arcsec^2*%s)\n",
+                             multip, wcs->cunit[2], multip*3600*3600,
+                             wcs->cunit[2]);
+                    else
+                      printf("    %g (deg^2*m) = %g (arcsec^2*m) = "
+                             "%g (arcsec^2*A)\n", multip, multip*3600*3600,
+                             multip*3600*3600*1e10);
+                  }
+            }
+        }
+    }
+  else
+    {
+      multip=1;
+      for(i=0;i<ndim;++i)
+        {
+          multip*=pixelscale[i];
+          printf("%g ", pixelscale[i]);
+        }
+      switch(ndim)
+        {
+        case 2: printf("%g\n", multip); break;
+        case 3: printf("%g %g\n", pixelscale[0]*pixelscale[1], multip); break;
+        }
+    }
+
+  /* Clean up. */
+  wcsfree(wcs);
+  free(pixelscale);
+}
+
+
+
+
+
+static void
+fits_skycoverage(struct fitsparams *p)
+{
+  int nwcs=0;
+  size_t i, ndim;
+  struct wcsprm *wcs;
+  double *center, *width, *min, *max;
+
+  /* Find the coverage. */
+  if( gal_wcs_coverage(p->input->v, p->cp.hdu, &ndim,
+                       &center, &width, &min, &max)==0 )
+    error(EXIT_FAILURE, 0, "%s (hdu %s): is not usable for finding "
+          "sky coverage (either doesn't have a WCS, or isn't an image "
+          "or cube HDU with 2 or 3 dimensions", p->input->v, p->cp.hdu);
+
+  /* Inform the user. */
+  if(p->cp.quiet)
+    {
+      /* First print the center and full-width. */
+      for(i=0;i<ndim;++i) printf("%-15.10g ", center[i]);
+      for(i=0;i<ndim;++i) printf("%-15.10g ", width[i]);
+      printf("\n");
+
+      /* Then print the range in coordinates. */
+      for(i=0;i<ndim;++i) printf("%-15.10g %-15.10g ", min[i], max[i]);
+      printf("\n");
+    }
+  else
+    {
+      printf("Input file: %s (hdu: %s)\n", p->input->v, p->cp.hdu);
+      printf("\nSky coverage by center and (full) width:\n");
+      switch(ndim)
+        {
+        case 2:
+          printf("  Center: %-15.10g%-15.10g\n", center[0], center[1]);
+          printf("  Width:  %-15.10g%-15.10g\n", width[0],  width[1]);
+          break;
+        case 3:
+          printf("  Center: %-15.10g%-15.10g%-15.10g\n", center[0],
+                 center[1], center[2]);
+          printf("  width:  %-15.10g%-15.10g%-15.10g\n", width[0],
+                 width[1], width[2]);
+          break;
+        default:
+          error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+                "the problem. 'ndim' value %zu is not recognized", __func__,
+                PACKAGE_BUGREPORT, ndim);
+        }
+
+      /* For the range type of coverage. */
+      wcs=gal_wcs_read(p->input->v, p->cp.hdu, 0, 0, &nwcs);
+      printf("\nSky coverage by range along dimensions:\n");
+      for(i=0;i<ndim;++i)
+        printf("  %-8s %-15.10g%-15.10g\n", gal_wcs_dimension_name(wcs, i),
+               min[i], max[i]);
+      wcsfree(wcs);
+    }
+
+  /* Clean up. */
+  free(min);
+  free(max);
+  free(width);
+  free(center);
+}
+
+
+
+
+
+static void
 fits_hdu_remove(struct fitsparams *p, int *r)
 {
   char *hdu;
@@ -284,7 +506,7 @@ fits_hdu_remove(struct fitsparams *p, int *r)
       hdu=gal_list_str_pop(&p->remove);
 
       /* Open the FITS file at the specified HDU. */
-      fptr=gal_fits_hdu_open(p->filename, hdu, READWRITE);
+      fptr=gal_fits_hdu_open(p->input->v, hdu, READWRITE);
 
       /* Delete the extension. */
       if( fits_delete_hdu(fptr, &hdutype, &status) )
@@ -300,7 +522,7 @@ fits_hdu_remove(struct fitsparams *p, int *r)
 
 
 
-/* This is similar to the library's `gal_fits_open_to_write', except that
+/* This is similar to the library's 'gal_fits_open_to_write', except that
    it won't create an empty first extension.*/
 fitsfile *
 fits_open_to_write_no_blank(char *filename)
@@ -346,12 +568,12 @@ fits_hdu_copy(struct fitsparams *p, int cut1_copy0, int 
*r)
       hdu=gal_list_str_pop(&list);
 
       /* Open the FITS file at the specified HDU. */
-      in=gal_fits_hdu_open(p->filename, hdu,
+      in=gal_fits_hdu_open(p->input->v, hdu,
                            cut1_copy0 ? READWRITE : READONLY);
 
       /* If the output isn't opened yet, open it.  */
       if(out==NULL)
-        out = ( ( gal_fits_hdu_format(p->filename, hdu)==IMAGE_HDU
+        out = ( ( gal_fits_hdu_format(p->input->v, hdu)==IMAGE_HDU
                   && p->primaryimghdu )
                 ? fits_open_to_write_no_blank(p->cp.output)
                 : gal_fits_open_to_write(p->cp.output) );
@@ -362,7 +584,7 @@ fits_hdu_copy(struct fitsparams *p, int cut1_copy0, int *r)
         *r=fits_has_error(p, FITS_ACTION_COPY, hdu, status);
       status=0;
 
-      /* If this is a `cut' operation, then remove the extension. */
+      /* If this is a 'cut' operation, then remove the extension. */
       if(cut1_copy0)
         {
           if( fits_delete_hdu(in, &hdutype, &status) )
@@ -389,7 +611,7 @@ fits(struct fitsparams *p)
 
   switch(p->mode)
     {
-    /* Keywords, we have a separate set of functions in `keywords.c'. */
+    /* Keywords, we have a separate set of functions in 'keywords.c'. */
     case FITS_MODE_KEY:
       r=keywords(p);
       break;
@@ -398,8 +620,10 @@ fits(struct fitsparams *p)
     case FITS_MODE_HDU:
 
       /* Options that must be called alone. */
-      if(p->numhdus)
-        fits_hdu_number(p);
+      if(p->numhdus) fits_hdu_number(p);
+      else if(p->datasum) fits_datasum(p);
+      else if(p->pixelscale) fits_pixelscale(p);
+      else if(p->skycoverage) fits_skycoverage(p);
 
       /* Options that can be called together. */
       else
diff --git a/bin/fits/fits.h b/bin/fits/fits.h
index 06f461c..10a10fe 100644
--- a/bin/fits/fits.h
+++ b/bin/fits/fits.h
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/fits/keywords.c b/bin/fits/keywords.c
index f7346ff..9a325f6 100644
--- a/bin/fits/keywords.c
+++ b/bin/fits/keywords.c
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -28,7 +28,9 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <string.h>
 #include <stdlib.h>
 
+#include <gnuastro/wcs.h>
 #include <gnuastro/fits.h>
+#include <gnuastro/pointer.h>
 #include <gnuastro-internal/timing.h>
 
 #include <gnuastro-internal/checkset.h>
@@ -51,7 +53,7 @@ static void
 keywords_open(struct fitsparams *p, fitsfile **fptr, int iomode)
 {
   if(*fptr==NULL)
-    *fptr=gal_fits_hdu_open(p->filename, p->cp.hdu, iomode);
+    *fptr=gal_fits_hdu_open(p->input->v, p->cp.hdu, iomode);
 }
 
 
@@ -92,7 +94,7 @@ keywords_rename_keys(struct fitsparams *p, fitsfile **fptr, 
int *r)
       str=gal_list_str_pop(&p->rename);
 
       /* Take a copy of the input string for error reporting, because
-         `strtok' will write into the array. */
+         'strtok' will write into the array. */
       gal_checkset_allocate_copy(str, &copy);
 
       /* Tokenize the input. */
@@ -101,18 +103,18 @@ keywords_rename_keys(struct fitsparams *p, fitsfile 
**fptr, int *r)
 
       /* Make sure both elements were read. */
       if(from==NULL || to==NULL)
-        error(EXIT_FAILURE, 0, "`%s' could not be tokenized in order to "
+        error(EXIT_FAILURE, 0, "'%s' could not be tokenized in order to "
               "complete rename. There should be a space character "
               "or a comma (,) between the two keyword names. If you have "
               "used the space character, be sure to enclose the value to "
-              "the `--rename' option in double quotation marks", copy);
+              "the '--rename' option in double quotation marks", copy);
 
       /* Rename the keyword */
       fits_modify_name(*fptr, from, to, &status);
       if(status) *r=fits_has_error(p, FITS_ACTION_RENAME, from, status);
       status=0;
 
-      /* Clean up the user's input string. Note that `strtok' just changes
+      /* Clean up the user's input string. Note that 'strtok' just changes
          characters within the allocated string, no extra allocation is
          done. */
       free(str);
@@ -140,7 +142,7 @@ keywords_write_set_value(struct fitsparams *p, fitsfile 
**fptr,
         return 1;
       else
         {
-          /* Calculate and write the `CHECKSUM' and `DATASUM' keywords. */
+          /* Calculate and write the 'CHECKSUM' and 'DATASUM' keywords. */
           if( fits_write_chksum(*fptr, &status) )
             gal_fits_io_error(status, NULL);
 
@@ -161,7 +163,7 @@ keywords_write_set_value(struct fitsparams *p, fitsfile 
**fptr,
     }
   else
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
-          "fix the problem. The `keyname' value `%s' is not "
+          "fix the problem. The 'keyname' value '%s' is not "
           "recognized as one with no value", __func__,
           PACKAGE_BUGREPORT, keyll->keyname);
 
@@ -236,9 +238,9 @@ keywords_write_update(struct fitsparams *p, fitsfile **fptr,
                 gal_fits_io_error(status, NULL);
             }
           else
-            error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at `%s' so "
+            error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' so "
                   "we can fix this problem. The value %d is not valid for "
-                  "`u1w2'", __func__, PACKAGE_BUGREPORT, u1w2);
+                  "'u1w2'", __func__, PACKAGE_BUGREPORT, u1w2);
 
           /* Add the unit (if one was given). */
           if(keyll->unit
@@ -314,13 +316,13 @@ keywords_verify(struct fitsparams *p, fitsfile **fptr)
            "Checking integrity of %s (hdu %s)\n"
            "%s"
            "--------\n"
-           "Basic info (remove all extra info with `--quiet'):\n"
+           "Basic info (remove all extra info with '--quiet'):\n"
            "    - DATASUM: verifies only the data (not keywords).\n"
            "    - CHECKSUM: verifies data and keywords.\n"
            "They can be added-to/updated-in an extension/HDU with:\n"
            "    $ astfits %s -h%s --write=checksum\n"
-           "--------\n", PROGRAM_STRING, p->filename, p->cp.hdu,
-           ctime(&p->rawtime), p->filename, p->cp.hdu);
+           "--------\n", PROGRAM_STRING, p->input->v, p->cp.hdu,
+           ctime(&p->rawtime), p->input->v, p->cp.hdu);
 
   /* Print the verification result. */
   printf("DATASUM:  %s\n",
@@ -345,13 +347,13 @@ keywords_copykeys(struct fitsparams *p, char *inkeys, 
size_t numinkeys)
   long initial;
   fitsfile *fptr;
 
-  /* Initial sanity check. Since `numinkeys' includes `END' (counting from
+  /* Initial sanity check. Since 'numinkeys' includes 'END' (counting from
      1, as we do here), the first keyword must not be larger OR EQUAL to
-     `numinkeys'. */
+     'numinkeys'. */
   if(p->copykeysrange[0]>=numinkeys)
     error(EXIT_FAILURE, 0, "%s (hdu %s): first keyword number give to "
-          "`--copykeys' (%ld) is larger than the number of keywords in this "
-          "header (%zu, including the `END' keyword)", p->filename, p->cp.hdu,
+          "'--copykeys' (%ld) is larger than the number of keywords in this "
+          "header (%zu, including the 'END' keyword)", p->input->v, p->cp.hdu,
           p->copykeysrange[0], numinkeys);
 
   /* If the user wanted to count from the end (by giving a negative value),
@@ -365,16 +367,16 @@ keywords_copykeys(struct fitsparams *p, char *inkeys, 
size_t numinkeys)
       /* Sanity check. */
       if(p->copykeysrange[0]>=p->copykeysrange[1])
         error(EXIT_FAILURE, 0, "%s (hdu %s): the last keyword given to "
-              "`--copykeys' (%ld, or %ld after counting from the bottom) "
-              "is earlier than the first (%ld)", p->filename, p->cp.hdu,
+              "'--copykeys' (%ld, or %ld after counting from the bottom) "
+              "is earlier than the first (%ld)", p->input->v, p->cp.hdu,
               initial, p->copykeysrange[1], p->copykeysrange[0]);
     }
 
   /* Final sanity check (on range limit). */
   if(p->copykeysrange[1]>=numinkeys)
     error(EXIT_FAILURE, 0, "%s (hdu %s): second keyword number give to "
-          "`--copykeys' (%ld) is larger than the number of keywords in this "
-          "header (%zu, including the `END' keyword)", p->filename, p->cp.hdu,
+          "'--copykeys' (%ld) is larger than the number of keywords in this "
+          "header (%zu, including the 'END' keyword)", p->input->v, p->cp.hdu,
           p->copykeysrange[1], numinkeys);
 
 
@@ -403,28 +405,447 @@ keywords_date_to_seconds(struct fitsparams *p, fitsfile 
*fptr)
   int status=0;
   double subsec;
   size_t seconds;
-  char *subsecstr;
+  char *subsecstr=NULL;
   char fitsdate[FLEN_KEYWORD];
 
   /* Read the requested FITS keyword. */
   if( fits_read_key(fptr, TSTRING, p->datetosec, &fitsdate, NULL, &status) )
     gal_fits_io_error(status, NULL);
 
-  /* Return the number of seconds (and subseconds) that it corresponds
-     to. */
+  /* Return the number of seconds (and subseconds).*/
   seconds=gal_fits_key_date_to_seconds(fitsdate, &subsecstr, &subsec);
+  if(seconds==GAL_BLANK_SIZE_T)
+    error(EXIT_FAILURE, 0, "the time string couldn't be interpretted");
 
   /* Print the result (for the sub-seconds, print everything after the */
   if( !p->cp.quiet )
     {
-      printf("%s (hdu %s), key `%s': %s\n", p->filename, p->cp.hdu,
+      printf("%s (hdu %s), key '%s': %s\n", p->input->v, p->cp.hdu,
              p->datetosec, fitsdate);
       printf("Seconds since 1970/01/01 (00:00:00): %zu%s\n\n", seconds,
-             subsecstr);
-      printf("(To suppress verbose output, run with `-q')\n");
+             subsecstr?subsecstr:"");
+      printf("(To suppress verbose output, run with '-q')\n");
     }
   else
-    printf("%zu%s\n", seconds, subsecstr);
+    printf("%zu%s\n", seconds, subsecstr?subsecstr:"");
+
+  /* Clean up. */
+  if(subsecstr) free(subsecstr);
+}
+
+
+
+
+
+static void
+keywords_distortion_wcs(struct fitsparams *p)
+{
+  int nwcs;
+  size_t ndim, *insize;
+  char *suffix, *output;
+  gal_data_t *data=NULL;
+  struct wcsprm *inwcs, *outwcs;
+  size_t *dsize, defaultsize[2]={2000,2000};
+
+  /* If the extension has any data, read it, otherwise just make an empty
+     array. */
+  if(gal_fits_hdu_format(p->input->v, p->cp.hdu)==IMAGE_HDU)
+    {
+      /* Read the size of the dataset (we don't need the actual size!). */
+      insize=gal_fits_img_info_dim(p->input->v, p->cp.hdu, &ndim);
+      free(insize);
+
+      /* If the number of dimensions is two, then read the dataset,
+         otherwise, ignore it. */
+      if(ndim==2)
+        data=gal_fits_img_read(p->input->v, p->cp.hdu, p->cp.minmapsize,
+                               p->cp.quietmmap);
+    }
+
+  /* Read the input's WCS and make sure one exists. */
+  inwcs=gal_wcs_read(p->input->v, p->cp.hdu, 0, 0, &nwcs);
+  if(inwcs==NULL)
+    error(EXIT_FAILURE, 0, "%s (hdu %s): doesn't have any WCS structure "
+          "for converting its distortion",
+          p->input->v, p->cp.hdu);
+
+  /* In case there is no dataset and the conversion is between TPV to SIP,
+     we need to set a default size and use that for the conversion, but we
+     also need to warn the user. */
+  if(data==NULL)
+    {
+      if( !p->cp.quiet
+          && gal_wcs_distortion_identify(inwcs)==GAL_WCS_DISTORTION_TPV
+          && p->distortionid==GAL_WCS_DISTORTION_SIP )
+        error(0, 0, "no data associated with WCS for distortion "
+              "conversion.\n\n"
+              "The requested conversion can't be done analytically, so a "
+              "solution has to be found by fitting the parameters over a "
+              "grid of pixels. We will use a default grid of %zux%zu pixels "
+              "and will proceed with the conversion. But it would be more "
+              "accurate if it is the size of the image that this WCS is "
+              "associated with",
+              defaultsize[1], defaultsize[0]);
+      dsize=defaultsize;
+    }
+  else dsize=data->dsize;
+
+  /* Do the conversion. */
+  outwcs=gal_wcs_distortion_convert(inwcs, p->distortionid, dsize);
+
+  /* Set the output filename. */
+  if(p->cp.output)
+    output=p->cp.output;
+  else
+    {
+      if( asprintf(&suffix, "-%s.fits", p->wcsdistortion)<0 )
+        error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
+      output=gal_checkset_automatic_output(&p->cp, p->input->v, suffix);
+    }
+  gal_checkset_writable_remove(output, 0, p->cp.dontdelete);
+
+  /* Write the output file. */
+  if(data)
+    {
+      /* Add the output WCS to the dataset and write it. */
+      data->wcs=outwcs;
+      gal_fits_img_write(data, output, NULL, PROGRAM_NAME);
+
+      /* Clean up, but remove the pointer first (so it doesn't free it
+         here). */
+      data->wcs=NULL;
+      gal_data_free(data);
+    }
+  else
+    gal_wcs_write(outwcs, output, p->wcsdistortion, NULL, PROGRAM_NAME);
+
+  /* Clean up. */
+  wcsfree(inwcs);
+  wcsfree(outwcs);
+  if(output!=p->cp.output) free(output);
+}
+
+
+
+
+
+static void
+keywords_value_in_output_copy(gal_data_t *write, gal_data_t *key,
+                              size_t in_counter)
+{
+  char **strarrk, **strarrw;
+
+  /* Small sanity check. */
+  if(write->type != key->type)
+    error(EXIT_FAILURE, 0, "%s: the input datasets must have "
+          "the same data type. The 'write' and 'key' arguments "
+          "are respectively '%s' and '%s'", __func__,
+          gal_type_name(write->type, 1),
+          gal_type_name(key->type, 1));
+
+  /* Copy the value. */
+  if(key->type==GAL_TYPE_STRING)
+    {
+      strarrk=key->array;
+      strarrw=write->array;
+      strarrw[ in_counter ] = strarrk[0];
+      strarrk[0]=NULL;
+    }
+  else
+    memcpy(gal_pointer_increment(write->array, in_counter,
+                                 write->type),
+           key->array, gal_type_sizeof(write->type));
+}
+
+
+
+
+
+/* Write the value in the first row. The first row is unique here: if there
+   is only one input dataset, the dataset name will not be in the
+   output. But when there is more than one dataset, we include a column for
+   the name of the dataset. */
+static gal_data_t *
+keywords_value_in_output_first(struct fitsparams *p, gal_data_t *topout,
+                               char *filename, gal_data_t *keysll,
+                               size_t ninput)
+{
+  char **strarr;
+  gal_data_t *out=NULL;
+  gal_data_t *write, *key;
+  size_t in_counter=0; /* This function is only for the first row. */
+
+  /* If a name column is necessary. */
+  if(topout)
+    {
+      /* Small sanity check. */
+      if(topout->next)
+        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+              "fix the problem. The 'next' pointer of 'topout' should "
+              "be NULL", __func__, PACKAGE_BUGREPORT);
+
+      /* The size of the output should be the same as 'ninput'. */
+      if(topout->size!=ninput)
+        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+              "fix the problem. The number of elements in 'topout' "
+              "(%zu) is different from 'ninput' (%zu)", __func__,
+              PACKAGE_BUGREPORT, out->size, ninput);
+
+      /* Write the filename. */
+      strarr=topout->array;
+      gal_checkset_allocate_copy(filename, &strarr[in_counter]);
+    }
+
+  /* Add the new columns into the raw output (only keyword values). */
+  for(key=keysll; key!=NULL; key=key->next)
+    {
+      /* If the keyword couldn't be read for any reason then 'key->status'
+         will be non-zero. In this case, return a string type and put a
+         blank string value. */
+      if( key->status )
+        {
+          key->type=GAL_TYPE_STRING;
+          if(p->cp.quiet==0)
+            error(EXIT_SUCCESS, 0, "%s (hdu %s): does not contain a "
+                  "keyword '%s'", filename, p->cp.hdu, key->name);
+        }
+
+      /* Allocate the full column for this key and add it to the end of
+         the existing output list of columns: IMPORTANT NOTE: it is
+         necessary to initialize the values because we may need to
+         change the types before fully writing values within it. */
+      write=gal_data_alloc(NULL, key->type, 1, &ninput, NULL,
+                           1, p->cp.minmapsize, p->cp.quietmmap,
+                           key->name, key->unit, key->comment);
+
+      /* Copy the value of this key into the output. Note that for strings,
+         the arrays are initialized to NULL. */
+      if( key->status )
+        {
+          strarr=write->array;
+          gal_checkset_allocate_copy(GAL_BLANK_STRING,
+                                     &strarr[in_counter]);
+        }
+      else
+        keywords_value_in_output_copy(write, key, in_counter);
+
+      /* Put the allocated column into the output list. */
+      gal_list_data_add(&out, write);
+    }
+
+  /* Reverse the list (to be the same order as the user's request). */
+  gal_list_data_reverse(&out);
+
+  /* If a first row (containing the filename) is given, then add the
+     allocated datasets to its end */
+  if(topout) { topout->next=out; out=topout; }
+
+  /* Return the output. */
+  return out;
+}
+
+
+
+
+
+static void
+keywords_value_in_output_rest_replace(gal_data_t *list, gal_data_t *old,
+                                      gal_data_t *new)
+{
+  gal_data_t *parse;
+  new->next=old->next;
+  for(parse=list; parse!=NULL; parse=parse->next)
+    if(parse->next==old)
+      {
+        gal_data_free(old);
+        parse->next=new;
+        break;
+      }
+}
+
+
+
+
+
+/* This function is for the case that we have more than one row. In this
+   case, we always want the input file's name to be printed. */
+static void
+keywords_value_in_output_rest(struct fitsparams *p, gal_data_t *out,
+                              char *filename, gal_data_t *keysll,
+                              size_t in_counter)
+{
+  int goodtype;
+  char **strarr;
+  gal_data_t *write, *key;
+  gal_data_t *goodkey, *goodwrite;
+
+  /* Write the file name in the first column. */
+  strarr=out->array;
+  gal_checkset_allocate_copy(filename, &strarr[in_counter]);
+
+  /* Go over all the keys are write them in. */
+  write=out;
+  for(key=keysll; key!=NULL; key=key->next)
+    {
+      /* Increment the write column also. */
+      write=write->next;
+
+      /* If the status is non-zero then the keyword couldn't be read. In
+         this case, put a blank value in this row. */
+      if(key->status)
+        {
+          gal_blank_write(gal_pointer_increment(write->array, in_counter,
+                                                write->type),
+                          write->type);
+          if(p->cp.quiet==0)
+            error(EXIT_SUCCESS, 0, "%s (hdu %s): does not contain a "
+                  "keyword '%s'", filename, p->cp.hdu, key->name);
+          continue;
+        }
+
+      /* This key is good and the type is string (which is the type for a
+         key that doesn't exist in the previous file(s)). In this case,
+         check if all the previous rows are blank. If they are all blank
+         then this keyword didn't exist in any of the previous files and
+         this is the first one that has the keyword. So change the type of
+         the column to the final type. */
+      else
+        {
+          if( write->type==GAL_TYPE_STRING
+              && write->type!=key->type
+              && gal_blank_number(write, 1)==write->size )
+            {
+              goodwrite=gal_data_alloc(NULL, key->type, 1, out->dsize,
+                                       NULL, 0, p->cp.minmapsize,
+                                       p->cp.quietmmap, key->name,
+                                       key->unit, key->comment);
+              gal_blank_initialize(goodwrite);
+              keywords_value_in_output_rest_replace(out, write,
+                                                    goodwrite);
+              write=goodwrite;
+            }
+        }
+
+      /* If the previous files didn't have metadata for this keyword but
+         this file does, use the metadata here. */
+      if(write->unit==NULL && key->unit)
+        { write->unit=key->unit; key->unit=NULL; }
+      if(write->comment==NULL && key->comment)
+        { write->comment=key->comment; key->comment=NULL; }
+
+      /* If the column types are the same, then put them in. */
+      if(key->type==write->type)
+        keywords_value_in_output_copy(write, key, in_counter);
+      else
+        {
+          /* Find the most inclusive type. */
+          goodtype=gal_type_out(key->type, write->type);
+
+          /* Convert each of the two into the same type. */
+          goodkey = ( key->type==goodtype
+                      ? key
+                      : gal_data_copy_to_new_type(key, goodtype) );
+          goodwrite = ( write->type==goodtype
+                        ? write
+                        : gal_data_copy_to_new_type(write, goodtype) );
+
+          /* Copy the row into the output. */
+          keywords_value_in_output_copy(goodwrite, goodkey, in_counter);
+
+          /* If the "good" writing dataset has been changed, then
+             replace it in the output (correct its 'next' pointer, and
+             set the previous column to point to it. */
+          if(goodwrite!=write)
+            {
+              keywords_value_in_output_rest_replace(out, write,
+                                                    goodwrite);
+              write=goodwrite;
+            }
+
+          /* If a different key has been used, clean it. */
+          if(goodkey!=key) gal_data_free(goodkey);
+        }
+    }
+}
+
+
+
+
+
+static void
+keywords_value(struct fitsparams *p)
+{
+  int status;
+  fitsfile *fptr=NULL;
+  gal_list_str_t *input, *tmp;
+  size_t i, ii=0, ninput, nkeys;
+  gal_data_t *out=NULL, *keysll=NULL;
+
+  /* Count how many inputs there are and allocate the first column with the
+     name. */
+  ninput=gal_list_str_number(p->input);
+  if(ninput>1)
+    out=gal_data_alloc(NULL, GAL_TYPE_STRING, 1, &ninput, NULL, 0,
+                       p->cp.minmapsize, p->cp.quietmmap, "FILENAME",
+                       "name", "Name of input file.");
+
+  /* Allocate the structure to host the desired keywords read from each
+     FITS file and their values. But first convert the list of strings (for
+     keyword names), (where each string can be a comma-separated list) into
+     a list with a single value per string. */
+  gal_options_merge_list_of_csv(&p->keyvalue);
+  nkeys=gal_list_str_number(p->keyvalue);
+
+  /* Parse each input file, read the keywords and put them in the output
+     list. */
+  for(input=p->input; input!=NULL; input=input->next)
+    {
+      /* Open the input FITS file. */
+      fptr=gal_fits_hdu_open(input->v, p->cp.hdu, READONLY);
+
+      /* Allocate the array to keep the keys. */
+      i=0;
+      keysll=gal_data_array_calloc(nkeys);
+      for(tmp=p->keyvalue; tmp!=NULL; tmp=tmp->next)
+        {
+          if(tmp->next) keysll[i].next=&keysll[i+1];
+          keysll[i].name=tmp->v;
+          ++i;
+        }
+
+      /* Read the keys. Note that we only need the comments and units if
+         '--colinfoinstdout' is called. */
+      gal_fits_key_read_from_ptr(fptr, keysll, p->colinfoinstdout,
+                                 p->colinfoinstdout);
+
+      /* Close the input FITS file. */
+      status=0;
+      if(fits_close_file(fptr, &status))
+        gal_fits_io_error(status, NULL);
+
+      /* Write the values of this column into the final output. */
+      if(ii==0)
+        {
+          ++ii;
+          out=keywords_value_in_output_first(p, out, input->v,
+                                             keysll, ninput);
+        }
+      else
+        keywords_value_in_output_rest(p, out, input->v, keysll,
+                                      ii++);
+
+      /* Clean up. */
+      for(i=0;i<nkeys;++i) keysll[i].name=NULL;
+      gal_data_array_free(keysll, nkeys, 1);
+    }
+
+  /* Write the values. */
+  gal_checkset_writable_remove(p->cp.output, 0, p->cp.dontdelete);
+  gal_table_write(out, NULL, NULL, p->cp.tableformat,
+                  p->cp.output, "KEY-VALUES", p->colinfoinstdout);
+
+  /* Clean up. */
+  gal_list_str_free(p->keyvalue, 0);
 }
 
 
@@ -451,12 +872,12 @@ keywords_date_to_seconds(struct fitsparams *p, fitsfile 
*fptr)
 /***********************************************************************/
 /* NOTE ON CALLING keywords_open FOR EACH OPERATION:
 
-   `keywords_open' is being called individually for each separate operation
+   'keywords_open' is being called individually for each separate operation
    because the necessary permissions differ: when the user only wants to
    read keywords, they don't necessarily need write permissions. So if they
    haven't asked for any writing/editing operation, we shouldn't open in
    write-mode. Because the user might not have the permissions to write and
-   they might not want to write. `keywords_open' will only open the file
+   they might not want to write. 'keywords_open' will only open the file
    once (if the pointer is already allocated, it won't do anything). */
 int
 keywords(struct fitsparams *p)
@@ -467,6 +888,10 @@ keywords(struct fitsparams *p)
   gal_list_str_t *tstll;
   int status=0, numinkeys;
 
+  /* Print the requested keywords. Note that this option isn't called with
+     the rest. It is independent of them. */
+  if(p->keyvalue)
+    keywords_value(p);
 
   /* Delete the requested keywords. */
   if(p->delete)
@@ -597,7 +1022,7 @@ keywords(struct fitsparams *p)
 
 
   /* Close the FITS file */
-  if(fits_close_file(fptr, &status))
+  if(fptr && fits_close_file(fptr, &status))
     gal_fits_io_error(status, NULL);
 
 
@@ -608,6 +1033,10 @@ keywords(struct fitsparams *p)
       free(inkeys);
     }
 
+  /* Convert the input's distortion to the desired output distortion. */
+  if(p->wcsdistortion)
+    keywords_distortion_wcs(p);
+
   /* Return. */
   return r;
 }
diff --git a/bin/fits/keywords.h b/bin/fits/keywords.h
index 9d95675..be29916 100644
--- a/bin/fits/keywords.h
+++ b/bin/fits/keywords.h
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/fits/main.c b/bin/fits/main.c
index 7eda419..e105fcb 100644
--- a/bin/fits/main.c
+++ b/bin/fits/main.c
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/fits/main.h b/bin/fits/main.h
index 6b50be2..9c66221 100644
--- a/bin/fits/main.h
+++ b/bin/fits/main.h
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -54,30 +54,37 @@ struct fitsparams
 {
   /* From the environment. */
   struct gal_options_common_params cp;  /* Common parameters.           */
-  int  hdu_in_commandline;     /* HDU wasn't given in config. file.     */
-  char          *filename;     /* Name of input file.                   */
-  char            *outhdu;     /* HDU of output (only when necessary).  */
-  gal_list_str_t  *remove;     /* Remove extensions from a file.        */
-  gal_list_str_t    *copy;     /* Copy extensions to output.            */
-  gal_list_str_t     *cut;     /* Copy ext. to output and remove.       */
-  uint8_t         numhdus;     /* Print number of HDUs in FITS file.    */
-  uint8_t   primaryimghdu;     /* Copy/cut HDU into primary HDU.        */
-  uint8_t    printallkeys;     /* Print all the header keywords.        */
-  uint8_t            date;     /* Set DATE to current time.             */
-  gal_list_str_t    *asis;     /* Strings to write asis.                */
-  gal_list_str_t  *delete;     /* Keywords to remove.                   */
-  gal_list_str_t  *rename;     /* Rename a keyword.                     */
-  gal_list_str_t  *update;     /* For keywords to update.               */
-  gal_list_str_t   *write;     /* Full arg. for keywords to add.        */
-  gal_list_str_t *history;     /* HISTORY value.                        */
-  gal_list_str_t *comment;     /* COMMENT value.                        */
-  uint8_t         *verify;     /* Verify the CHECKSUM and DATASUM keys. */
-  char          *copykeys;     /* Range of keywords to copy in output.  */
-  char         *datetosec;     /* Convert FITS date to seconds.         */
-  uint8_t     quitonerror;     /* Quit if an error occurs.              */
+  int    hdu_in_commandline;   /* HDU wasn't given in config. file.     */
+  gal_list_str_t     *input;   /* Name of input file.                   */
+  char              *outhdu;   /* HDU of output (only when necessary).  */
+  gal_list_str_t    *remove;   /* Remove extensions from a file.        */
+  gal_list_str_t      *copy;   /* Copy extensions to output.            */
+  gal_list_str_t       *cut;   /* Copy ext. to output and remove.       */
+  uint8_t           numhdus;   /* Print number of HDUs in FITS file.    */
+  uint8_t           datasum;   /* Calculate and print HDU's datasum.    */
+  uint8_t        pixelscale;   /* Calculate and print HDU's pixelscale. */
+  uint8_t       skycoverage;   /* Calculate and image coverage in WCS.  */
+  uint8_t     primaryimghdu;   /* Copy/cut HDU into primary HDU.        */
+  uint8_t      printallkeys;   /* Print all the header keywords.        */
+  uint8_t              date;   /* Set DATE to current time.             */
+  gal_list_str_t      *asis;   /* Strings to write asis.                */
+  gal_list_str_t  *keyvalue;   /* Strings to write asis.                */
+  gal_list_str_t    *delete;   /* Keywords to remove.                   */
+  gal_list_str_t    *rename;   /* Rename a keyword.                     */
+  gal_list_str_t    *update;   /* For keywords to update.               */
+  gal_list_str_t     *write;   /* Full arg. for keywords to add.        */
+  gal_list_str_t   *history;   /* HISTORY value.                        */
+  gal_list_str_t   *comment;   /* COMMENT value.                        */
+  uint8_t           *verify;   /* Verify the CHECKSUM and DATASUM keys. */
+  char            *copykeys;   /* Range of keywords to copy in output.  */
+  char           *datetosec;   /* Convert FITS date to seconds.         */
+  char       *wcsdistortion;   /* WCS distortion to write in output.    */
+  uint8_t       quitonerror;   /* Quit if an error occurs.              */
+  uint8_t   colinfoinstdout;   /* Print column info in output.          */
 
   /* Internal: */
   int                         mode;  /* Operating on HDUs or keywords.  */
+  int                 distortionid;  /* ID of desired distortion.       */
   long            copykeysrange[2];  /* Start and end of copy.          */
   gal_fits_list_key_t  *write_keys;  /* Keys to write in the header.    */
   gal_fits_list_key_t *update_keys;  /* Keys to update in the header.   */
diff --git a/bin/fits/ui.c b/bin/fits/ui.c
index ca86eee..c9cbc63 100644
--- a/bin/fits/ui.c
+++ b/bin/fits/ui.c
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -28,6 +28,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdio.h>
 #include <string.h>
 
+#include <gnuastro/wcs.h>
 #include <gnuastro/fits.h>
 
 #include <gnuastro-internal/options.h>
@@ -119,7 +120,6 @@ ui_initialize_options(struct fitsparams *p,
         case GAL_OPTIONS_KEY_SEARCHIN:
         case GAL_OPTIONS_KEY_IGNORECASE:
         case GAL_OPTIONS_KEY_TYPE:
-        case GAL_OPTIONS_KEY_TABLEFORMAT:
         case GAL_OPTIONS_KEY_DONTDELETE:
         case GAL_OPTIONS_KEY_LOG:
         case GAL_OPTIONS_KEY_NUMTHREADS:
@@ -153,18 +153,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct fitsparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -176,12 +176,7 @@ parse_opt(int key, char *arg, struct argp_state *state)
     case ARGP_KEY_ARG:
       /* Only FITS files are acceptable. */
       if( gal_fits_name_is_fits(arg) )
-        {
-          if(p->filename)
-            argp_error(state, "only one input file should be given");
-          else
-            p->filename=arg;
-        }
+        gal_list_str_add(&p->input, arg, 1);
       else
         argp_error(state, "%s is not a recognized FITS file", arg);
       break;
@@ -228,13 +223,13 @@ ui_check_copykeys(struct fitsparams *p)
   /* For copykeys, an output filename is mandatory. */
   if(p->cp.output==NULL || p->outhdu==NULL)
     error(EXIT_FAILURE, 0, "an output FITS extension (in an existing "
-          "FITS file, specified with the `--output' and `--outhdu') are "
-          "mandatory for running `--copykeys'");
+          "FITS file, specified with the '--output' and '--outhdu') are "
+          "mandatory for running '--copykeys'");
 
   /* Initialize the values. */
   p->copykeysrange[0]=p->copykeysrange[1]=GAL_BLANK_LONG;
 
-  /* Parse the string: `forl': "first-or-last". */
+  /* Parse the string: 'forl': "first-or-last". */
   while(*pt!='\0')
     {
       switch(*pt)
@@ -245,7 +240,7 @@ ui_check_copykeys(struct fitsparams *p)
           break;
         case '.':
           error(EXIT_FAILURE, 0, "the numbers in the argument to "
-                "`--section` (`-s') have to be integers. You input "
+                "'--section' ('-s') have to be integers. You input "
                 "includes a float number: %s", p->copykeys);
           break;
         case ' ': case '\t':
@@ -268,10 +263,10 @@ ui_check_copykeys(struct fitsparams *p)
           break;
           */
         default:
-          error(EXIT_FAILURE, 0, "value to `--copykeys' must only contain "
+          error(EXIT_FAILURE, 0, "value to '--copykeys' must only contain "
                 "integer numbers and these special characters between them: "
-                "`:' when necessary. But it is `%s' (the first "
-                "non-acceptable character is `%c').\n", p->copykeys, *pt);
+                "':' when necessary. But it is '%s' (the first "
+                "non-acceptable character is '%c').\n", p->copykeys, *pt);
           break;
         }
 
@@ -292,17 +287,17 @@ ui_check_copykeys(struct fitsparams *p)
 
   /* Basic sanity checks. */
   if( p->copykeysrange[1]==GAL_BLANK_LONG )
-    error(EXIT_FAILURE, 0, "no ending keyword number given to `--copykeys'. "
+    error(EXIT_FAILURE, 0, "no ending keyword number given to '--copykeys'. "
           "If you want to copy all the keywords after a certain one "
-          "(without worrying about how many there are), you can use `-1'.\n\n"
+          "(without worrying about how many there are), you can use '-1'.\n\n"
           "For example if you want to copy all the keywords after the 20th, "
-          "you can use `--copykeys=20,-1'. Generally, you can use negative "
+          "you can use '--copykeys=20,-1'. Generally, you can use negative "
           "numbers for the last keyword number to count from the end.");
   if( p->copykeysrange[0]<=0 )
-    error(EXIT_FAILURE, 0, "the first number given to `--copykeys' must be "
+    error(EXIT_FAILURE, 0, "the first number given to '--copykeys' must be "
           "positive");
   if( p->copykeysrange[1]>=0 && p->copykeysrange[0]>=p->copykeysrange[1] )
-    error(EXIT_FAILURE, 0, "the first number (%ld) given to `--copykeys' "
+    error(EXIT_FAILURE, 0, "the first number (%ld) given to '--copykeys' "
           "must be smaller than the second (%ld)", p->copykeysrange[0],
           p->copykeysrange[1]);
 
@@ -317,63 +312,100 @@ ui_check_copykeys(struct fitsparams *p)
 
 
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct fitsparams *p)
 {
+  int checkkeys;
+  uint8_t stdoutcheck=0;
+
   /* If any of the keyword manipulation options are requested, then set the
      mode flag to keyword-mode. */
-  if( p->date || p->comment || p->history || p->asis || p->delete
-      || p->rename || p->update || p->write || p->verify || p->printallkeys
-      || p->copykeys || p->datetosec )
+  if( p->date || p->comment || p->history || p->asis || p->keyvalue
+      || p->delete || p->rename || p->update || p->write || p->verify
+      || p->printallkeys || p->copykeys || p->datetosec
+      || p->wcsdistortion )
     {
-      /* Set the mode. */
-      p->mode=FITS_MODE_KEY;
-
       /* Check if a HDU is given. */
       if(p->cp.hdu==NULL)
-        error(EXIT_FAILURE, 0, "a HDU (extension) is necessary for keywrod "
+        error(EXIT_FAILURE, 0, "a HDU (extension) is necessary for keyword "
               "related options but none was defined. Please use the "
-              "`--hdu' (or `-h') option to select one");
+              "'--hdu' (or '-h') option to select one");
 
       /* If Copy keys has been given, read it and make sure its setup. */
       if(p->copykeys)
         ui_check_copykeys(p);
+
+      /* Keyword-related options that must be called alone. */
+      checkkeys = ( (p->keyvalue!=NULL)
+                    + (p->datetosec!=NULL)
+                    + (p->wcsdistortion!=NULL) );
+      if( ( checkkeys
+            && ( p->date || p->comment || p->history || p->asis
+                 || p->delete || p->rename || p->update || p->write
+                 || p->verify || p->printallkeys || p->copykeys ) )
+          || checkkeys>1 )
+        error(EXIT_FAILURE, 0, "'--keyvalue', '--datetosec' and "
+              "'--wcsdistortion' cannot currently be called with "
+              "any other option");
+
+      /* Identify the requested distortion. Note that this also acts as a
+         sanity check because it will crash with an error if the given
+         string isn't recognized. */
+      if(p->wcsdistortion)
+        p->distortionid=gal_wcs_distortion_from_string(p->wcsdistortion);
+
+      /* Set the operating mode. */
+      p->mode=FITS_MODE_KEY;
     }
 
   /* Same for the extension-related options */
-  if( p->remove || p->copy || p->cut || p->numhdus)
+  if( p->remove || p->copy || p->cut || p->numhdus || p->datasum
+      || p->pixelscale || p->skycoverage )
     {
       /* A small sanity check. */
       if(p->mode!=FITS_MODE_INVALID)
         error(EXIT_FAILURE, 0, "extension and keyword manipulation options "
               "cannot be called together");
 
-      /* Unlike the rest of the HDU-related options, `--numhdus' must be
-         called alone. */
-      if(p->numhdus==1 && (p->remove || p->copy || p->cut) )
-        error(EXIT_FAILURE, 0, "`--numhdus' option must be called alone (it "
-              "cannot be called with other extension or keyword options)");
+      /* Some HDU options cannot be called with other options. */
+      stdoutcheck = p->numhdus + p->datasum + p->pixelscale + p->skycoverage;
+
+      /* Make sure if an output file is needed. */
+      if(stdoutcheck)
+        {
+          /* Make sure the other HDU-related options aren't called. */
+          if(p->remove || p->copy || p->cut)
+            error(EXIT_FAILURE, 0, "'--numhdus', '--datasum', '--pixelscale' "
+                  "or '--skycoverage' options cannot be called with any of "
+                  "the '--remove', '--copy' or '--cut' options");
+
+          /* Make sure these options are called alone. */
+          if(stdoutcheck>1)
+            error(EXIT_FAILURE, 0, "'--numhdus', '--datasum', '--pixelscale' "
+                  "or '--skycoverage' options cannot be called together, "
+                  "only one at a time");
+
+          /* Make sure the HDU is given if any of the options except
+             '--numhdus' are called. */
+          if( stdoutcheck-p->numhdus && p->cp.hdu==NULL )
+            error(EXIT_FAILURE, 0, "a HDU (extension) is necessary for the "
+                  "'--datasum', '--pixelscale' or '--skycoverage' options. "
+                  "Please use the '--hdu' (or '-h') option to select one");
+        }
+      else
+        {
+          if(p->cp.output)
+            gal_checkset_writable_remove(p->cp.output, 1, p->cp.dontdelete);
+          else
+            p->cp.output=gal_checkset_automatic_output(&p->cp, p->input->v,
+                                                       "_ext.fits");
+        }
 
       /* Set the operating mode. */
       p->mode=FITS_MODE_HDU;
-
-      /* Make sure the output name is set. */
-      if(p->cp.output)
-        gal_checkset_writable_remove(p->cp.output, 1, p->cp.dontdelete);
-      else
-        p->cp.output=gal_checkset_automatic_output(&p->cp, p->filename,
-                                                   "_ext.fits");
     }
 
-  /* Currently `datetosec' must be called alone. */
-  if( p->datetosec
-      && (p->date || p->comment || p->history || p->asis || p->delete
-          || p->rename || p->update || p->write || p->verify
-          || p->printallkeys || p->copykeys || p->mode==FITS_MODE_HDU) )
-    error(EXIT_FAILURE, 0, "`--datetosec' cannot currently be called with "
-          "any other option");
-
   /* If no options are given, go into HDU mode, which will print the HDU
      information when nothing is asked. */
   if(p->mode==FITS_MODE_INVALID)
@@ -397,8 +429,15 @@ ui_check_options_and_arguments(struct fitsparams *p)
 {
   /* Make sure an input file name was given and if it was a FITS file, that
      a HDU is also given. */
-  if(p->filename==NULL)
+  if(p->input==NULL)
     error(EXIT_FAILURE, 0, "no input file is specified");
+  gal_list_str_reverse(&p->input);
+
+  /* More than one input is currently only acceptable with the '--keyvalue'
+     option. */
+  if( gal_list_str_number(p->input) > 1 && p->keyvalue==NULL)
+    error(EXIT_FAILURE, 0, "one input file is expected but %zu input "
+          "files are given", gal_list_str_number(p->input));
 }
 
 
@@ -423,9 +462,9 @@ ui_check_options_and_arguments(struct fitsparams *p)
 /**************************************************************/
 /*****************       Preparations      ********************/
 /**************************************************************/
-/* The `--update' and `--write' options take multiple values for each
+/* The '--update' and '--write' options take multiple values for each
    keyword, so here, we tokenize them and put them into a
-   `gal_fits_list_key_t' list. */
+   'gal_fits_list_key_t' list. */
 static void
 ui_fill_fits_headerll(gal_list_str_t *input, gal_fits_list_key_t **output,
                       char *option_name)
@@ -443,12 +482,12 @@ ui_fill_fits_headerll(gal_list_str_t *input, 
gal_fits_list_key_t **output,
       i=0;
       tailptr=NULL;
 
-      /* `c' is created in case of an error, so the input value can be
+      /* 'c' is created in case of an error, so the input value can be
          reported. */
       errno=0;
       original=malloc(strlen(tmp->v)+1);
       if(original==NULL)
-        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `original'",
+        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'original'",
               __func__, strlen(tmp->v)+1);
       strcpy(original, tmp->v);
 
@@ -505,14 +544,14 @@ ui_fill_fits_headerll(gal_list_str_t *input, 
gal_fits_list_key_t **output,
 
       /* Make sure the keyname and value (if necessary) is given. */
       if( keyname==NULL || (needsvalue && value==NULL) )
-        error(EXIT_FAILURE, 0, "`--%s' option string (%s) can't be parsed. "
+        error(EXIT_FAILURE, 0, "'--%s' option string (%s) can't be parsed. "
               "The general expected format is (a comment string and unit "
               "are optional):\n\n"
               "    --%s=KEYWORD,value,\"a comment string\",unit\n\n"
               "Any space characters around the the comma (,) characters "
               "will be seen as part of the respective token.\n\n"
               "Note that there are some exceptions (where no value is need)"
-              "please see the manual for more (`$ info astfits')",
+              "please see the manual for more ('$ info astfits')",
               option_name, original, option_name);
       /*
       printf("\n\n-%s-\n-%s-\n-%s-\n-%s-\n", keyname, value, comment, unit);
@@ -531,7 +570,7 @@ ui_fill_fits_headerll(gal_list_str_t *input, 
gal_fits_list_key_t **output,
               errno=0;
               fvalue=lp=malloc(sizeof *lp);
               if(lp==NULL)
-                error(EXIT_FAILURE, errno, "%s: %zu bytes for `lp'",
+                error(EXIT_FAILURE, errno, "%s: %zu bytes for 'lp'",
                       __func__, sizeof *lp);
               *lp=l;
             }
@@ -547,7 +586,7 @@ ui_fill_fits_headerll(gal_list_str_t *input, 
gal_fits_list_key_t **output,
                   fvalue=dp=malloc(sizeof *dp);
                   if(dp==NULL)
                     error(EXIT_FAILURE, errno, "%s: allocating %zu bytes "
-                          "for `dp'", __func__, sizeof *dp);
+                          "for 'dp'", __func__, sizeof *dp);
                   *dp=d;
                 }
               else
@@ -562,7 +601,7 @@ ui_fill_fits_headerll(gal_list_str_t *input, 
gal_fits_list_key_t **output,
 
       /* Add it to the list of keywords. */
       gal_fits_key_list_add(output, type, keyname, 0, fvalue, vfree,
-                            comment, 0, unit);
+                            comment, 0, unit, 0);
       free(original);
     }
 
@@ -612,9 +651,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
fitsparams *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
+  /* 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
diff --git a/bin/fits/ui.h b/bin/fits/ui.h
index 4249fb4..4ee5333 100644
--- a/bin/fits/ui.h
+++ b/bin/fits/ui.h
@@ -5,7 +5,7 @@ Fits is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -33,7 +33,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Option groups particular to this program. */
 enum program_args_groups
 {
-  UI_GROUP_EXTENSION = GAL_OPTIONS_GROUP_AFTER_COMMON,
+  UI_GROUP_EXTENSION_INFORMATION = GAL_OPTIONS_GROUP_AFTER_COMMON,
+  UI_GROUP_EXTENSION_MANIPULATION,
   UI_GROUP_KEYWORD,
 };
 
@@ -43,36 +44,41 @@ enum program_args_groups
 
 /* Available letters for short options:
 
-   b e f g i j l m x y z
-   A B E G J L O W X Y
+   b e f g i j m x y z
+   A B E G J L W X Y
  */
 enum option_keys_enum
 {
   /* With short-option version. */
-  UI_KEY_REMOVE       = 'R',
-  UI_KEY_COPY         = 'C',
-  UI_KEY_CUT          = 'k',
-  UI_KEY_NUMHDUS      = 'n',
-  UI_KEY_PRINTALLKEYS = 'p',
-  UI_KEY_ASIS         = 'a',
-  UI_KEY_DELETE       = 'd',
-  UI_KEY_RENAME       = 'r',
-  UI_KEY_UPDATE       = 'u',
-  UI_KEY_WRITE        = 'w',
-  UI_KEY_COMMENT      = 'c',
-  UI_KEY_HISTORY      = 'H',
-  UI_KEY_DATE         = 't',
-  UI_KEY_VERIFY       = 'v',
-  UI_KEY_QUITONERROR  = 'Q',
-  UI_KEY_DATETOSEC    = 's',
-
+  UI_KEY_REMOVE          = 'R',
+  UI_KEY_COPY            = 'C',
+  UI_KEY_CUT             = 'k',
+  UI_KEY_NUMHDUS         = 'n',
+  UI_KEY_PRINTALLKEYS    = 'p',
+  UI_KEY_ASIS            = 'a',
+  UI_KEY_KEYVALUE        = 'l',
+  UI_KEY_DELETE          = 'd',
+  UI_KEY_RENAME          = 'r',
+  UI_KEY_UPDATE          = 'u',
+  UI_KEY_WRITE           = 'w',
+  UI_KEY_COMMENT         = 'c',
+  UI_KEY_HISTORY         = 'H',
+  UI_KEY_DATE            = 't',
+  UI_KEY_VERIFY          = 'v',
+  UI_KEY_QUITONERROR     = 'Q',
+  UI_KEY_DATETOSEC       = 's',
+  UI_KEY_COLINFOINSTDOUT = 'O',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
   UI_KEY_TITLE        = 1000,
+  UI_KEY_DATASUM,
+  UI_KEY_PIXELSCALE,
+  UI_KEY_SKYCOVERAGE,
   UI_KEY_OUTHDU,
   UI_KEY_COPYKEYS,
   UI_KEY_PRIMARYIMGHDU,
+  UI_KEY_WCSDISTORTION,
 };
 
 
diff --git a/bin/gnuastro.conf b/bin/gnuastro.conf
index 115b78e..6c180c0 100644
--- a/bin/gnuastro.conf
+++ b/bin/gnuastro.conf
@@ -4,15 +4,15 @@
 # Use the long option name of each paramter followed by
 # a value. The name and value should be separated by
 # atleast one of the following charaacters:
-# space, `,`, `=` or `:`
+# space, ',', '=' or ':'
 #
-# Run with `--help` option or read the manual for a full
+# Run with '--help' option or read the manual for a full
 # explanation of what each option means.
 #
 # NOTE I:  All counting is from zero, not one.
-# NOTE II: Lines starting with `#` are ignored.
+# NOTE II: Lines starting with '#' are ignored.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -31,7 +31,7 @@
  remainderfrac    0.1
  workoverch       0
  interpmetric     radial
- interpnumngb     9
+ interpnumngb     15
  interponlyblank  0
 
 # Output:
@@ -39,4 +39,13 @@
 
 # Operating mode
  quietmmap        0
- minmapsize       2000000000
+
+ # The default 'minmapsize' is set to the maximum possible value for signed
+ # 64-bit integers (half the full logical size of a 64-bit system, which is
+ # larger than 9 x 10^18 bytes). Effectively, this means that forced
+ # memory-mapping will be disabled. Therefore memory-mapping will only
+ # happen when memory cannot be allocated with the RAM (for any reason,
+ # mainly not having enough space). On 32-bit systems, this will
+ # automatically be read by the C library as the largest possible memory in
+ # those systems (~4.3 x 10^9 bytes).
+ minmapsize       9223372036854775807
diff --git a/bin/match/Makefile.am b/bin/match/Makefile.am
index 9e9fe33..75f3413 100644
--- a/bin/match/Makefile.am
+++ b/bin/match/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2017-2019, Free Software Foundation, Inc.
+## Copyright (C) 2017-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astmatch
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astmatch_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                 $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astmatch_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                 -lgnuastro $(CONFIG_LDADD)
 
 astmatch_SOURCES = main.c ui.c match.c
 
diff --git a/bin/match/args.h b/bin/match/args.h
index 5dd2424..67515d1 100644
--- a/bin/match/args.h
+++ b/bin/match/args.h
@@ -5,7 +5,7 @@ Match is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -81,7 +81,7 @@ struct argp_option program_options[] =
       UI_KEY_OUTCOLS,
       "STR",
       0,
-      "Out cols in CSV, `a': first, `b': second input.",
+      "Out cols in CSV, 'a': first, 'b': second input.",
       GAL_OPTIONS_GROUP_OUTPUT,
       &p->outcols,
       GAL_OPTIONS_NO_ARG_TYPE,
diff --git a/bin/match/astmatch.conf b/bin/match/astmatch.conf
index 5e269f0..6e64203 100644
--- a/bin/match/astmatch.conf
+++ b/bin/match/astmatch.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astmatch                       # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
@@ -22,4 +22,4 @@
 # Input
  hdu2            1
 
-# Catalog matching
\ No newline at end of file
+# Catalog matching
diff --git a/bin/match/authors-cite.h b/bin/match/authors-cite.h
index 7625dbf..9f7ffd8 100644
--- a/bin/match/authors-cite.h
+++ b/bin/match/authors-cite.h
@@ -5,7 +5,7 @@ Match is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/match/main.c b/bin/match/main.c
index f89132b..3fa4836 100644
--- a/bin/match/main.c
+++ b/bin/match/main.c
@@ -5,7 +5,7 @@ Match is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/match/main.h b/bin/match/main.h
index 1e776a6..d9d0c5b 100644
--- a/bin/match/main.h
+++ b/bin/match/main.h
@@ -5,7 +5,7 @@ Match is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/match/match.c b/bin/match/match.c
index a1f6eac..ec23d1c 100644
--- a/bin/match/match.c
+++ b/bin/match/match.c
@@ -5,7 +5,7 @@ Match is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -79,7 +79,7 @@ match_add_all_cols(char *filename, char *extname, 
gal_list_str_t *stdinlines,
     }
 
   /* If a new list of columns is ready, re-order tham and write
-     them in. Note that there may be multiple `_all' terms, so we
+     them in. Note that there may be multiple '_all' terms, so we
      need to do this after parsing all the requested columns. */
   gal_list_str_reverse(&finalcols);
 
@@ -111,12 +111,12 @@ match_cat_from_coord(struct matchparams *p, 
gal_list_str_t *cols,
   colcounter=0;
   for(col=cols;col!=NULL;col=col->next)
     {
-      /* In `ui_preparations_out_cols', we have done the necessary sanity
+      /* In 'ui_preparations_out_cols', we have done the necessary sanity
          checks, so we can safely use the values. */
       rptr=gal_type_string_to_number(col->v, &readtype);
       if(readtype!=GAL_TYPE_UINT8)
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-              "the problem. The given string didn't have a `uint8' type",
+              "the problem. The given string didn't have a 'uint8' type",
               __func__, PACKAGE_BUGREPORT);
       read=*((uint8_t *)rptr);
 
@@ -172,13 +172,13 @@ match_catalog_read_write_all(struct matchparams *p, 
size_t *permutation,
   if(p->outcols)
     {
       /* As a special situation, the user can ask to incude all of the
-         columns from one of the inputs with the special `_all' name. So,
+         columns from one of the inputs with the special '_all' name. So,
          we'll check if that is the case and write in all the columns where
          they are requested.*/
       for(tcol=incols; tcol!=NULL; tcol=tcol->next)
         if(!strcmp(tcol->v,"_all")) { hasall=1; break; }
 
-      /* If atleast one instance of `_all' is present, then reset the list
+      /* If atleast one instance of '_all' is present, then reset the list
          of columns to include in output. */
       if(hasall)
         {
@@ -200,8 +200,8 @@ match_catalog_read_write_all(struct matchparams *p, size_t 
*permutation,
   else cols=incols;
 
 
-  /* Read the full table. NOTE that with `--coord', for the second input,
-     both `filename' and `p->stdinlines' will be NULL. */
+  /* Read the full table. NOTE that with '--coord', for the second input,
+     both 'filename' and 'p->stdinlines' will be NULL. */
   if(filename || p->stdinlines)
     cat=gal_table_read(filename, hdu, filename ? NULL : p->stdinlines, cols,
                        p->cp.searchin, p->cp.ignorecase, p->cp.minmapsize,
@@ -238,7 +238,7 @@ match_catalog_read_write_all(struct matchparams *p, size_t 
*permutation,
         else
           tmp->size=tmp->dsize[0]=nummatched;
       }
-  /* If no match was found (`permutation==NULL'), and the matched columns
+  /* If no match was found ('permutation==NULL'), and the matched columns
      are requested, empty all the columns that are to be written (only
      keeping the meta-data). */
   else
@@ -259,12 +259,13 @@ match_catalog_read_write_all(struct matchparams *p, 
size_t *permutation,
   else if(cat)
     {
       /* Write the catalog to a file. */
-      gal_table_write(cat, NULL, p->cp.tableformat, outname, extname, 0);
+      gal_table_write(cat, NULL, NULL, p->cp.tableformat, outname,
+                      extname, 0);
 
-      /* Correct arrays and sizes (when `notmatched' was called). The
-         `array' element has to be corrected for later freeing.
+      /* Correct arrays and sizes (when 'notmatched' was called). The
+         'array' element has to be corrected for later freeing.
 
-         IMPORTANT: `--notmatched' cannot be called with `--outcols'. So
+         IMPORTANT: '--notmatched' cannot be called with '--outcols'. So
          you don't have to worry about the checks here being done later. */
       if(p->notmatched)
         {
@@ -334,13 +335,14 @@ match_catalog_write_one(struct matchparams *p, gal_data_t 
*a, gal_data_t *b,
   /* A small sanity check. */
   if(a || b)
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us to fix the problem. "
-          "The two `a' and `b' arrays must be NULL by this point: "
-          "`a' %s NULL, `b' %s NULL", __func__, a?"is not":"is",
+          "The two 'a' and 'b' arrays must be NULL by this point: "
+          "'a' %s NULL, 'b' %s NULL", __func__, a?"is not":"is",
           b?"is not":"is");
 
   /* Reverse the table and write it out. */
   gal_list_data_reverse(&cat);
-  gal_table_write(cat, NULL, p->cp.tableformat, p->out1name, "MATCHED", 0);
+  gal_table_write(cat, NULL, NULL, p->cp.tableformat, p->out1name,
+                  "MATCHED", 0);
 }
 
 
@@ -366,7 +368,7 @@ match_catalog(struct matchparams *p)
     {
       /* Read (and possibly write) the outputs. Note that we only need to
          read the table when it is necessary for the output (the user might
-         have asked for `--outcols', only with columns of one of the two
+         have asked for '--outcols', only with columns of one of the two
          inputs). */
       if(p->outcols==NULL || p->acols)
         a=match_catalog_read_write_all(p, mcols?mcols->array:NULL,
@@ -401,7 +403,7 @@ match_catalog(struct matchparams *p)
       mcols=tmp;
 
       /* We also want everything to be incremented by one. In a C
-         program, counting starts with zero, so `gal_match_coordinates'
+         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). */
@@ -423,7 +425,7 @@ match_catalog(struct matchparams *p)
         "from 1).";
 
       /* Write them into the table. */
-      gal_table_write(mcols, NULL, p->cp.tableformat, p->logname,
+      gal_table_write(mcols, NULL, NULL, p->cp.tableformat, p->logname,
                       "LOG_INFO", 0);
 
       /* Set the comment pointer to NULL: they weren't allocated. */
diff --git a/bin/match/match.h b/bin/match/match.h
index 6e399e2..d22f615 100644
--- a/bin/match/match.h
+++ b/bin/match/match.h
@@ -5,7 +5,7 @@ Match is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/match/ui.c b/bin/match/ui.c
index dacb0d3..31d1ee2 100644
--- a/bin/match/ui.c
+++ b/bin/match/ui.c
@@ -5,7 +5,7 @@ Match is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -145,18 +145,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct matchparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -210,12 +210,12 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct matchparams *p)
 {
   if(p->outcols && p->notmatched)
-    error(EXIT_FAILURE, 0, "`--outcols' and `--notmatched' cannot be called "
+    error(EXIT_FAILURE, 0, "'--outcols' and '--notmatched' cannot be called "
           "at the same time. The former is only for cases when the matches "
           "are required");
 }
@@ -225,38 +225,38 @@ ui_read_check_only_options(struct matchparams *p)
 
 
 /* Two necessary catalogs: First:  Standard input, or a file.
-                           Second: `--coord',      or a file.
+                           Second: '--coord',      or a file.
  */
 static void
 ui_check_options_and_arguments(struct matchparams *p)
 {
-  /* When `--coord' is given, there should be no second catalog
+  /* When '--coord' is given, there should be no second catalog
      (argument). */
   if(p->coord)
     {
       /* Make sure no second argument is given. */
       if(p->input2name)
         error(EXIT_FAILURE, 0, "only one argument can be given with the "
-              "`--coord' option");
+              "'--coord' option");
 
-      /* No need for `p->input2name' or `p->ccol2'. */
+      /* No need for 'p->input2name' or 'p->ccol2'. */
       gal_data_free(p->ccol2);
       p->ccol2=NULL;
     }
 
-  /* `--coord' is not given. */
+  /* '--coord' is not given. */
   else
     {
-      /* Without `coord' atleast one input is necessary. */
+      /* Without 'coord' atleast one input is necessary. */
       if(p->input1name==NULL)
         error(EXIT_FAILURE, 0, "no inputs!\n\n"
               "Two inputs are necessary. The first can be a file, or from "
               "the standard input (e.g., a pipe). The second can be a "
               "file, or its coordinates can be directly specified on the "
-              "command-line with `--coord'");
+              "command-line with '--coord'");
 
       /* If the first input should be read from the standard input, the
-         contents of `input1name' actually belong to `input2name'. */
+         contents of 'input1name' actually belong to 'input2name'. */
       if(p->input2name==NULL)
         {
           p->input2name=p->input1name;
@@ -271,14 +271,14 @@ ui_check_options_and_arguments(struct matchparams *p)
       && 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 "
+          "'--hdu' ('-h') option and give it the HDU number (starting "
           "from zero), extension name, or anything acceptable by "
           "CFITSIO");
   if( p->input2name
       && 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 "
+          "the '--hdu2' ('-H') option and give it the HDU number "
           "(starting from zero), extension name, or anything "
           "acceptable by CFITSIO");
 }
@@ -313,7 +313,7 @@ ui_set_mode(struct matchparams *p)
 
   /* We will base the mode on the first input, then check with the
      second. Note that when the first is from standard input (it is
-     `NULL'), then we go into catalog mode because currently we assume
+     'NULL'), then we go into catalog mode because currently we assume
      standard input is only for plain text and WCS matching is not defined
      on plain text. */
   if( p->input1name && gal_fits_name_is_fits(p->input1name) )
@@ -330,13 +330,13 @@ ui_set_mode(struct matchparams *p)
 
   /* Necessary sanity checks. */
   if(p->mode==MATCH_MODE_CATALOG && p->cp.searchin==0)
-    error(EXIT_FAILURE, 0, "no `--searchin' option specified. Please run "
+    error(EXIT_FAILURE, 0, "no '--searchin' option specified. Please run "
           "the following command for more information:\n\n"
           "    $ info gnuastro \"selecting table columns\"\n");
 
 
   /* Now that the mode is set, do some sanity checks on the second
-     catalog. Recall that when `--coord' is given, there is no second input
+     catalog. Recall that when '--coord' is given, there is no second input
      file.*/
   if(p->input2name)
     {
@@ -365,10 +365,10 @@ ui_set_mode(struct matchparams *p)
     }
   else
     {
-      /* When there is no second-input file name (`--coord' is given), we
+      /* When there is no second-input file name ('--coord' is given), we
          cannot be in WCS mode (requiring a FITS file). */
       if(p->mode==MATCH_MODE_WCS)
-        error(EXIT_FAILURE, 0, "%s is an image, while `--coord' is only "
+        error(EXIT_FAILURE, 0, "%s is an image, while '--coord' is only "
               "meaningful for catalogs",
               gal_checkset_dataset_name(p->input1name, p->cp.hdu));
     }
@@ -393,10 +393,10 @@ ui_read_columns_aperture_2d(struct matchparams *p)
   /* 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 "
+    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 "
+    error(EXIT_FAILURE, 0, "the second value of '--aperture' cannot be "
           "zero or negative");
 
   /* Will be needed in more than one case. */
@@ -424,13 +424,13 @@ ui_read_columns_aperture_2d(struct matchparams *p)
 
     case 3:
       if(oaper[1]>1)
-        error(EXIT_FAILURE, 0, "second value to `--aperture' is larger "
+        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'. In 2D, this "
+      error(EXIT_FAILURE, 0, "%zu values given to '--aperture'. In 2D, this "
             "option can only take 1, 2, or 3 values", p->aperture->size);
     }
 
@@ -465,10 +465,10 @@ ui_read_columns_aperture_3d(struct matchparams *p)
   /* 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 "
+    error(EXIT_FAILURE, 0, "the first value of '--aperture' cannot be "
           "zero or negative");
   if( p->aperture->size>2 && (oaper[1]<=0 || oaper[2]<=0) )
-    error(EXIT_FAILURE, 0, "the second and third values of `--aperture' "
+    error(EXIT_FAILURE, 0, "the second and third values of '--aperture' "
           "cannot be zero or negative");
 
   /* Will be needed in more than one case. */
@@ -498,8 +498,8 @@ ui_read_columns_aperture_3d(struct matchparams *p)
           naper[3] = naper[4] = naper[5] = 0;
         }
 
-      /* Major axis is along the second dimension. So we want `X' to be in
-         the direction of `y'. Therefore, just the first Eurler ZXZ
+      /* Major axis is along the second dimension. So we want 'X' to be in
+         the direction of 'y'. Therefore, just the first Eurler ZXZ
          rotation is necessary by 90 degrees. Here is how the rotated
          coordinates (X,Y,Z) look like (after one rotation about Z).
 
@@ -511,7 +511,7 @@ ui_read_columns_aperture_3d(struct matchparams *p)
               x /
 
          You see how the major axis (X) now lies in the second original
-         direction (y). The length along `x' is now along `Y' and the third
+         direction (y). The length along 'x' is now along 'Y' and the third
          hasn't changed. Note that we are talking about the semi-axis
          lengths (a scalar, not a vector), so +- is irrelevant. */
       else if(oaper[1]>=oaper[0] && oaper[1]>=oaper[2])
@@ -523,8 +523,8 @@ ui_read_columns_aperture_3d(struct matchparams *p)
           naper[4] = naper[5] = 0;
         }
 
-      /* The major axis is along the third dimension. So we want `X' to
-         point in the direction of `z'. To get to that point, we need 90
+      /* The major axis is along the third dimension. So we want 'X' to
+         point in the direction of 'z'. To get to that point, we need 90
          degree rotations in all three Euler ZXZ rotations.
 
               |z (before)     z'|  /y'            |y''                  |X
@@ -552,13 +552,13 @@ ui_read_columns_aperture_3d(struct matchparams *p)
     case 6:
       if(oaper[1]>1 || oaper[2]>1)
         error(EXIT_FAILURE, 0, "atleast one of the second or third values "
-              "to `--aperture' is larger than one. When size numbers are "
+              "to '--aperture' is larger than one. When size numbers are "
               "given to this option (in 3D), the second and third are the "
               "axis ratios (which must always be less than 1).");
       break;
 
     default:
-      error(EXIT_FAILURE, 0, "%zu values given to `--aperture'. In 3D, this "
+      error(EXIT_FAILURE, 0, "%zu values given to '--aperture'. In 3D, this "
             "option can only take 1, 3, or 6 values. See the description of "
             "this option in the book for more with this command:\n\n"
             "    $ info astmatch\n", p->aperture->size);
@@ -586,13 +586,13 @@ ui_set_columns_sanity_check_read_aperture(struct 
matchparams *p)
   if(p->coord)
     {
       if(p->ccol1==NULL)
-        error(EXIT_FAILURE, 0, "no value given to `--ccol1' (necessary with "
-              "`--coord')");
+        error(EXIT_FAILURE, 0, "no value given to '--ccol1' (necessary with "
+              "'--coord')");
     }
   else
     {
       if(p->ccol1==NULL || p->ccol2==NULL)
-        error(EXIT_FAILURE, 0, "both `--ccol1' and `--ccol2' must be given. "
+        error(EXIT_FAILURE, 0, "both '--ccol1' and '--ccol2' must be given. "
               "They specify the columns containing the coordinates to match");
     }
 
@@ -600,9 +600,9 @@ ui_set_columns_sanity_check_read_aperture(struct 
matchparams *p)
   ccol1n = p->ccol1->size;
   ccol2n = p->coord ? p->coord->size : p->ccol2->size;
   if(ccol1n!=ccol2n)
-    error(EXIT_FAILURE, 0, "number of coordinates given to `--ccol1' "
-          "(%zu) and `--%s' (%zu) must be equal.\n\n"
-          "If you didn't call these options, run with `--checkconfig' to "
+    error(EXIT_FAILURE, 0, "number of coordinates given to '--ccol1' "
+          "(%zu) and '--%s' (%zu) must be equal.\n\n"
+          "If you didn't call these options, run with '--checkconfig' to "
           "see which configuration file is responsible. You can always "
           "override the configuration file values by calling the option "
           "manually on the command-line",
@@ -614,7 +614,7 @@ ui_set_columns_sanity_check_read_aperture(struct 
matchparams *p)
       {
       case 1:
         if(p->aperture->size>1)
-          error(EXIT_FAILURE, 0, "%zu values given to `--aperture'. In a 1D "
+          error(EXIT_FAILURE, 0, "%zu values given to '--aperture'. In a 1D "
                 "match, this option can only take one value",
                 p->aperture->size);
         break;
@@ -625,11 +625,11 @@ ui_set_columns_sanity_check_read_aperture(struct 
matchparams *p)
         error(EXIT_FAILURE, 0, "%zu dimensional matches are not currently "
               "supported (maximum is 2 dimensions). The number of "
               "dimensions is deduced from the number of values given to "
-              "`--ccol1' (or `--coord') and `--ccol2'", ccol1n);
+              "'--ccol1' (or '--coord') and '--ccol2'", ccol1n);
       }
   else
     error(EXIT_FAILURE, 0, "no matching aperture specified. Please use "
-          "the `--aperture' option to define the acceptable aperture for "
+          "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);
@@ -642,7 +642,7 @@ ui_set_columns_sanity_check_read_aperture(struct 
matchparams *p)
 
 
 
-/* Save the manually given coordinates (with `--coord') in column format (a
+/* Save the manually given coordinates (with '--coord') in column format (a
    list of datasets). */
 static gal_data_t *
 ui_set_columns_from_coord(struct matchparams *p)
@@ -678,8 +678,8 @@ ui_read_columns_to_double(struct matchparams *p, char 
*filename, char *hdu,
   gal_data_t *tmp, *ttmp, *tout, *out=NULL;
   struct gal_options_common_params *cp=&p->cp;
   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 "
+    "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).";
 
   /* Read the columns. Note that the first input's name can be NULL (if the
@@ -705,7 +705,7 @@ ui_read_columns_to_double(struct matchparams *p, char 
*filename, char *hdu,
   tmp=tout;
   while(tmp!=NULL)
     {
-      /* We need ot set the `next' pointer  */
+      /* We need ot set the 'next' pointer  */
       ttmp=tmp->next;
       tmp->next=NULL;
 
@@ -717,11 +717,11 @@ ui_read_columns_to_double(struct matchparams *p, char 
*filename, char *hdu,
                           gal_data_copy_to_new_type_free(tmp,
                                                          GAL_TYPE_FLOAT64) );
 
-      /* Set `tmp' to the initial `next pointer. */
+      /* Set 'tmp' to the initial 'next pointer. */
       tmp=ttmp;
     }
 
-  /* The `out' above is in reverse, so correct it and return */
+  /* The 'out' above is in reverse, so correct it and return */
   gal_list_data_reverse(&out);
   return out;
 }
@@ -789,7 +789,7 @@ ui_preparations_out_cols(struct matchparams *p)
         {
         case 'a': gal_list_str_add(&p->acols, col+1, 0); break;
         case 'b':
-          /* With `--coord', only numbers that are smaller than the number
+          /* With '--coord', only numbers that are smaller than the number
              of the dimensions are acceptable. */
           if(p->coord)
             {
@@ -809,10 +809,10 @@ ui_preparations_out_cols(struct matchparams *p)
                 }
               if(goodvalue==0)
                 error(EXIT_FAILURE, 0, "bad value to second catalog "
-                      "column (%s) of `--outcols'.\n\n"
-                      "With the `--coord' option, the second catalog is "
+                      "column (%s) of '--outcols'.\n\n"
+                      "With the '--coord' option, the second catalog is "
                       "assumed to have a single row and the given number "
-                      "of columns. Therefore when using `--outcols', only "
+                      "of columns. Therefore when using '--outcols', only "
                       "integers that are less than the number of "
                       "dimensions (%zu in this case) are acceptable", col+1,
                       ndim);
@@ -820,10 +820,10 @@ ui_preparations_out_cols(struct matchparams *p)
           gal_list_str_add(&p->bcols, col+1, 0);
           break;
         default:
-          error(EXIT_FAILURE, 0, "`%s' is not a valid value for "
-                "`--outcols'.\n\n"
+          error(EXIT_FAILURE, 0, "'%s' is not a valid value for "
+                "'--outcols'.\n\n"
                 "The first character of each value to this option must be "
-                "either `a' or `b'. The former specifies a column from the "
+                "either 'a' or 'b'. The former specifies a column from the "
                 "first input and the latter a column from the second. The "
                 "characters after them can be any column identifier (number, "
                 "name, or regular expression). For more on column selection, "
@@ -886,7 +886,7 @@ ui_preparations_out_name(struct matchparams *p)
         }
       else
         {
-          /* Set `p->out1name' and `p->out2name'. */
+          /* Set 'p->out1name' and 'p->out2name'. */
           if(p->cp.output)
             {
               if( gal_fits_name_is_fits(p->cp.output) )
@@ -899,7 +899,7 @@ ui_preparations_out_name(struct matchparams *p)
                   /* Here, we are be using the output name as input to the
                      automatic output generating function (usually it is
                      the input name, not the output name). Therefore, the
-                     `keepinputdir' variable should be 1. So we will
+                     'keepinputdir' variable should be 1. So we will
                      temporarily change it here, then set it back to what
                      it was. */
                   keepinputdir_orig=p->cp.keepinputdir;
@@ -998,9 +998,9 @@ 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
+  /* 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
diff --git a/bin/match/ui.h b/bin/match/ui.h
index 6d66082..2c172fd 100644
--- a/bin/match/ui.h
+++ b/bin/match/ui.h
@@ -5,7 +5,7 @@ Match is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/bin/mkcatalog/Makefile.am b/bin/mkcatalog/Makefile.am
index 19b30f3..be06118 100644
--- a/bin/mkcatalog/Makefile.am
+++ b/bin/mkcatalog/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astmkcatalog
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astmkcatalog_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                     $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astmkcatalog_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                     -lgnuastro $(CONFIG_LDADD)
 
 astmkcatalog_SOURCES = main.c ui.c mkcatalog.c columns.c upperlimit.c parse.c
 
diff --git a/bin/mkcatalog/args.h b/bin/mkcatalog/args.h
index 1a2ad60..6582d38 100644
--- a/bin/mkcatalog/args.h
+++ b/bin/mkcatalog/args.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -35,7 +35,7 @@ struct argp_option program_options[] =
     {
       "clumpsfile",
       UI_KEY_CLUMPSFILE,
-      "STR",
+      "FITS",
       0,
       "Dataset containing clump labels.",
       GAL_OPTIONS_GROUP_INPUT,
@@ -61,7 +61,7 @@ struct argp_option program_options[] =
     {
       "valuesfile",
       UI_KEY_VALUESFILE,
-      "STR",
+      "FITS",
       0,
       "Values/brightness dataset.",
       GAL_OPTIONS_GROUP_INPUT,
@@ -87,9 +87,9 @@ struct argp_option program_options[] =
     {
       "insky",
       UI_KEY_INSKY,
-      "STR/FLT",
+      "FITS/FLT",
       0,
-      "Input Sky value or dataset.",
+      "Input Sky value or file.",
       GAL_OPTIONS_GROUP_INPUT,
       &p->skyfile,
       GAL_TYPE_STRING,
@@ -163,6 +163,19 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET
     },
     {
+      "forcereadstd",
+      UI_KEY_FORCEREADSTD,
+      0,
+      0,
+      "Read STD even if no columns need it.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->forcereadstd,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
       "zeropoint",
       UI_KEY_ZEROPOINT,
       "FLT",
@@ -175,6 +188,20 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "sigmaclip",
+      UI_KEY_SIGMACLIP,
+      "FLT,FLT",
+      0,
+      "Sigma-clip column multiple and tolerance.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->sigmaclip,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_read_sigma_clip
+    },
 
 
 
@@ -244,6 +271,21 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "inbetweenints",
+      UI_KEY_INBETWEENINTS,
+      0,
+      0,
+      "Keep rows (integer ids) with no labels.",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->inbetweenints,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+
+
 
 
 
@@ -256,7 +298,7 @@ struct argp_option program_options[] =
     {
       "upmaskfile",
       UI_KEY_UPMASKFILE,
-      "STR",
+      "FITS",
       0,
       "Mask image file name only for upper limit.",
       UI_GROUP_UPPERLIMIT,
@@ -365,13 +407,38 @@ struct argp_option program_options[] =
 
 
 
+    /* Other column configurations. */
+    {
+      0, 0, 0, 0,
+      "Settings for other columns:",
+      UI_GROUP_OTHERSETTINGS
+    },
+    {
+      "fracmax",
+      UI_KEY_FRACMAX,
+      "FLT[,FLT]",
+      0,
+      "Fraction(s) in --fracmaxarea1 or --fracmaxarea2.",
+      UI_GROUP_OTHERSETTINGS,
+      &p->fracmax,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_float64
+    },
+
+
+
+
+
     /* ID related columns. */
     {
       0, 0, 0, 0,
       "Identifier columns",
       UI_GROUP_COLUMNS_IDS
     },
-    {  /* `ids' is not a unique column, it is a combination of several
+    {  /* 'ids' is not a unique column, it is a combination of several
           columns. */
       "ids",
       UI_KEY_IDS,
@@ -524,11 +591,95 @@ struct argp_option program_options[] =
       ui_column_codes_ll
     },
     {
+      "minvx",
+      UI_KEY_MINVX,
+      0,
+      0,
+      "Minimum value's X axis position",
+      UI_GROUP_COLUMNS_POSITION_PIXEL,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "maxvx",
+      UI_KEY_MAXVX,
+      0,
+      0,
+      "Maximum value's X axis position",
+      UI_GROUP_COLUMNS_POSITION_PIXEL,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "minvy",
+      UI_KEY_MINVY,
+      0,
+      0,
+      "Minimum value's Y axis position",
+      UI_GROUP_COLUMNS_POSITION_PIXEL,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "maxvy",
+      UI_KEY_MAXVY,
+      0,
+      0,
+      "Maximum value's Y axis position",
+      UI_GROUP_COLUMNS_POSITION_PIXEL,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "minvz",
+      UI_KEY_MINVZ,
+      0,
+      0,
+      "Minimum value's Z axis position",
+      UI_GROUP_COLUMNS_POSITION_PIXEL,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "maxvz",
+      UI_KEY_MAXVZ,
+      0,
+      0,
+      "Maximum value's Z axis position",
+      UI_GROUP_COLUMNS_POSITION_PIXEL,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
       "minx",
       UI_KEY_MINX,
       0,
       0,
-      "Minimum first FITS axis position.",
+      "Minimum X axis position.",
       UI_GROUP_COLUMNS_POSITION_PIXEL,
       0,
       GAL_TYPE_INVALID,
@@ -542,7 +693,7 @@ struct argp_option program_options[] =
       UI_KEY_MAXX,
       0,
       0,
-      "Maximum first FITS axis position.",
+      "Maximum X axis position.",
       UI_GROUP_COLUMNS_POSITION_PIXEL,
       0,
       GAL_TYPE_INVALID,
@@ -556,7 +707,7 @@ struct argp_option program_options[] =
       UI_KEY_MINY,
       0,
       0,
-      "Minimum second FITS axis position.",
+      "Minimum Y axis position.",
       UI_GROUP_COLUMNS_POSITION_PIXEL,
       0,
       GAL_TYPE_INVALID,
@@ -570,7 +721,7 @@ struct argp_option program_options[] =
       UI_KEY_MAXY,
       0,
       0,
-      "Maximum second FITS axis position.",
+      "Maximum Y axis position.",
       UI_GROUP_COLUMNS_POSITION_PIXEL,
       0,
       GAL_TYPE_INVALID,
@@ -584,7 +735,7 @@ struct argp_option program_options[] =
       UI_KEY_MINZ,
       0,
       0,
-      "Minimum third FITS axis position.",
+      "Minimum Z axis position",
       UI_GROUP_COLUMNS_POSITION_PIXEL,
       0,
       GAL_TYPE_INVALID,
@@ -598,7 +749,7 @@ struct argp_option program_options[] =
       UI_KEY_MAXZ,
       0,
       0,
-      "Maximum third FITS axis position.",
+      "Maximum Z axis position.",
       UI_GROUP_COLUMNS_POSITION_PIXEL,
       0,
       GAL_TYPE_INVALID,
@@ -994,6 +1145,20 @@ struct argp_option program_options[] =
       ui_column_codes_ll
     },
     {
+      "maximum",
+      UI_KEY_MAXIMUM,
+      0,
+      0,
+      "Maximum value (mean of top three pixels)",
+      UI_GROUP_COLUMNS_BRIGHTNESS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
       "magnitude",
       UI_KEY_MAGNITUDE,
       0,
@@ -1189,6 +1354,62 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET,
       ui_column_codes_ll
     },
+    {
+      "sigclip-number",
+      UI_KEY_SIGCLIPNUMBER,
+      0,
+      0,
+      "Number of pixels in Sigma-clipped measurement.",
+      UI_GROUP_COLUMNS_BRIGHTNESS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "sigclip-median",
+      UI_KEY_SIGCLIPMEDIAN,
+      0,
+      0,
+      "Median after Sigma-clipping",
+      UI_GROUP_COLUMNS_BRIGHTNESS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "sigclip-mean",
+      UI_KEY_SIGCLIPMEAN,
+      0,
+      0,
+      "Mean after Sigma-clipping",
+      UI_GROUP_COLUMNS_BRIGHTNESS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "sigclip-std",
+      UI_KEY_SIGCLIPSTD,
+      0,
+      0,
+      "Standard deviation after Sigma-clipping",
+      UI_GROUP_COLUMNS_BRIGHTNESS,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
 
 
 
@@ -1228,11 +1449,67 @@ struct argp_option program_options[] =
       ui_column_codes_ll
     },
     {
+      "areaarcsec2",
+      UI_KEY_AREAARCSEC2,
+      0,
+      0,
+      "Area of labeled region in arcsec^2.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "areaminv",
+      UI_KEY_MINVNUM,
+      0,
+      0,
+      "Number of pixels with minimum value.",
+      UI_GROUP_COLUMNS_POSITION_PIXEL,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "areamaxv",
+      UI_KEY_MAXVNUM,
+      0,
+      0,
+      "Number of pixels with maximum value.",
+      UI_GROUP_COLUMNS_POSITION_PIXEL,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "surfacebrightness",
+      UI_KEY_SURFACEBRIGHTNESS,
+      0,
+      0,
+      "Surface brightness (mag/arcsec^2).",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
       "areaxy",
       UI_KEY_AREAXY,
       0,
       0,
-      "Projected area in first two dimentions.",
+      "Projected area in first two dimensions.",
       UI_GROUP_COLUMNS_MORPHOLOGY,
       0,
       GAL_TYPE_INVALID,
@@ -1409,6 +1686,205 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET,
       ui_column_codes_ll
     },
+    {
+      "fwhm",
+      UI_KEY_FWHM,
+      0,
+      0,
+      "Full width at half max (non-parametric).",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "halfmaxarea",
+      UI_KEY_HALFMAXAREA,
+      0,
+      0,
+      "No. pixels valued above half the max.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "halfmaxradius",
+      UI_KEY_HALFMAXRADIUS,
+      0,
+      0,
+      "Radius at half the maximum (non-parametric).",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "halfmaxsum",
+      UI_KEY_HALFMAXSUM,
+      0,
+      0,
+      "Sum of pixels above half the maximum.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "halfmaxsb",
+      UI_KEY_HALFMAXSB,
+      0,
+      0,
+      "Surface brightness within half the maximum.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "halfsumarea",
+      UI_KEY_HALFSUMAREA,
+      0,
+      0,
+      "Area containing half of --brightness.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "halfsumsb",
+      UI_KEY_HALFSUMSB,
+      0,
+      0,
+      "Surface brightness within --halfsumarea.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "halfsumradius",
+      UI_KEY_HALFSUMRADIUS,
+      0,
+      0,
+      "Radius calculated from --halfsumarea.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "fracmaxsum1",
+      UI_KEY_FRACMAXSUM1,
+      0,
+      0,
+      "Sum of pixels brighter than 1st frac. of max.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "fracmaxsum2",
+      UI_KEY_FRACMAXSUM2,
+      0,
+      0,
+      "Sum of pixels brighter than 2nd frac. of max.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "fracmaxarea1",
+      UI_KEY_FRACMAXAREA1,
+      0,
+      0,
+      "Area containing 1st fraction of maximum.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "fracmaxarea2",
+      UI_KEY_FRACMAXAREA2,
+      0,
+      0,
+      "Area containing 2nd fraction of maximum.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "fracmaxradius1",
+      UI_KEY_FRACMAXRADIUS1,
+      0,
+      0,
+      "Radius calculated from --fracmaxarea1.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+    {
+      "fracmaxradius2",
+      UI_KEY_FRACMAXRADIUS2,
+      0,
+      0,
+      "Radius calculated from --fracmaxarea2.",
+      UI_GROUP_COLUMNS_MORPHOLOGY,
+      0,
+      GAL_TYPE_INVALID,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_column_codes_ll
+    },
+
+
+
 
 
     {0}
diff --git a/bin/mkcatalog/astmkcatalog.conf b/bin/mkcatalog/astmkcatalog.conf
index 19c417a..6815d2e 100644
--- a/bin/mkcatalog/astmkcatalog.conf
+++ b/bin/mkcatalog/astmkcatalog.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astmkcatalog                   # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
@@ -26,6 +26,7 @@
  skyhdu           SKY
  stdhdu       SKY_STD
  zeropoint        0.0
+ sigmaclip      3,0.2
 
 # Output:
  sfmagnsigma        1
diff --git a/bin/mkcatalog/authors-cite.h b/bin/mkcatalog/authors-cite.h
index 6d08bca..cdb8f44 100644
--- a/bin/mkcatalog/authors-cite.h
+++ b/bin/mkcatalog/authors-cite.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,28 +26,28 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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                                                 \
-  "Description of MakeCatalog\n"                                       \
-  "--------------------------\n"                                       \
-  "@ARTICLE{2016arXiv161106387A,\n"                                    \
-  "   author = {{Akhlaghi}, M.},\n"                                    \
-  "    title = \"{Separating detection and catalog production}\",\n"   \
-  "  journal = {ASP Conf. Ser. (in press)},\n"                         \
-  "archivePrefix = \"arXiv\",\n"                                       \
-  "   eprint = {1611.06387},\n"                                        \
-  "primaryClass = \"astro-ph.IM\",\n"                                  \
-  " keywords = {Astrophysics - Instrumentation and Methods for "       \
-               "Astrophysics},\n"                                      \
-  "     year = 2016,\n"                                                \
-  "    month = nov,\n"                                                 \
-  "   adsurl = {http://adsabs.harvard.edu/abs/2016arXiv161106387A},\n"; \
-  "  adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n"  \
-  "};\n"
+   put a row of '-' with the same length and then put the BibTeX.
+
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
+
+#define PROGRAM_BIBTEX                                                  \
+  "Description of MakeCatalog\n"                                        \
+  "--------------------------\n"                                        \
+  "@ARTICLE{makecatalog,\n"                                             \
+  "       author = {{Akhlaghi}, Mohammad},\n"                           \
+  "        title = \"{Separating Detection and Catalog Production}\",\n" \
+  "      journal = {ASPC},\n"                                           \
+  "         year = \"2019\",\n"                                         \
+  "        month = \"Oct\",\n"                                          \
+  "       volume = {521},\n"                                            \
+  "        pages = {299},\n"                                            \
+  "archivePrefix = {arXiv},\n"                                          \
+  "       eprint = {1611.06387},\n"                                     \
+  " primaryClass = {astro-ph.IM},\n"                                    \
+  "       adsurl = {https://ui.adsabs.harvard.edu/abs/2019ASPC..521..299A},\n"; 
\
+  "      adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n" \
+  "}\n"
 
 #define PROGRAM_AUTHORS "Mohammad Akhlaghi"
 
diff --git a/bin/mkcatalog/columns.c b/bin/mkcatalog/columns.c
index 6fac3a7..e94bb79 100644
--- a/bin/mkcatalog/columns.c
+++ b/bin/mkcatalog/columns.c
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -30,6 +30,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <string.h>
 #include <pthread.h>
 
+#include <gnuastro/wcs.h>
 #include <gnuastro/pointer.h>
 
 #include <gnuastro-internal/checkset.h>
@@ -52,7 +53,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    requested first or Dec or if they are requested multiple times. So
    before the allocation, we'll check the first one.
 
-   The space that is allocated in `columns_define_alloc' is for the final
+   The space that is allocated in 'columns_define_alloc' is for the final
    values that are written in the output file. */
 static void
 columns_alloc_radec(struct mkcatalogparams *p)
@@ -78,7 +79,7 @@ columns_alloc_radec(struct mkcatalogparams *p)
 
 
 
-/* Similar to `columns_alloc_radec'. */
+/* Similar to 'columns_alloc_radec'. */
 static void
 columns_alloc_georadec(struct mkcatalogparams *p)
 {
@@ -103,7 +104,7 @@ columns_alloc_georadec(struct mkcatalogparams *p)
 
 
 
-/* Similar to `columns_alloc_radec'. */
+/* Similar to 'columns_alloc_radec'. */
 static void
 columns_alloc_clumpsradec(struct mkcatalogparams *p)
 {
@@ -120,7 +121,7 @@ columns_alloc_clumpsradec(struct mkcatalogparams *p)
 
 
 
-/* Similar to `columns_alloc_radec'. */
+/* Similar to 'columns_alloc_radec'. */
 static void
 columns_alloc_clumpsgeoradec(struct mkcatalogparams *p)
 {
@@ -189,6 +190,7 @@ static void
 columns_wcs_preparation(struct mkcatalogparams *p)
 {
   size_t i;
+  double *pixscale;
   gal_list_i32_t *colcode;
   int continue_wcs_check=1;
 
@@ -202,6 +204,10 @@ columns_wcs_preparation(struct mkcatalogparams *p)
             /* High-level. */
             case UI_KEY_RA:
             case UI_KEY_DEC:
+            case UI_KEY_HALFMAXSB:
+            case UI_KEY_HALFSUMSB:
+            case UI_KEY_AREAARCSEC2:
+            case UI_KEY_SURFACEBRIGHTNESS:
 
             /* Low-level. */
             case UI_KEY_W1:
@@ -246,6 +252,16 @@ columns_wcs_preparation(struct mkcatalogparams *p)
                 "the WCS axis types (CTYPE)", p->objectsfile, p->cp.hdu,
                 colcode->v==UI_KEY_RA ? "RA" : "DEC");
         break;
+
+      /* Calculate the pixel area if necessary. */
+      case UI_KEY_HALFMAXSB:
+      case UI_KEY_HALFSUMSB:
+      case UI_KEY_AREAARCSEC2:
+      case UI_KEY_SURFACEBRIGHTNESS:
+        pixscale=gal_wcs_pixel_scale(p->objects->wcs);
+        p->pixelarcsecsq=pixscale[0]*pixscale[1]*3600.0f*3600.0f;
+        free(pixscale);
+        break;
       }
 }
 
@@ -265,6 +281,23 @@ columns_sanity_check(struct mkcatalogparams *p)
      based on it (for units and names). */
   columns_wcs_preparation(p);
 
+  /* If sigma-clipping measurements are requested, make sure the necessary
+     parameters are provided. */
+  for(colcode=p->columnids; colcode!=NULL; colcode=colcode->next)
+    switch(colcode->v)
+      {
+      case UI_KEY_SIGCLIPSTD:
+      case UI_KEY_SIGCLIPMEAN:
+      case UI_KEY_SIGCLIPNUMBER:
+      case UI_KEY_SIGCLIPMEDIAN:
+        if(isnan(p->sigmaclip[0]) || isnan(p->sigmaclip[1]))
+          error(EXIT_FAILURE, 0, "no sigma-clip defined! When any of the "
+                "sigma-clipping columns are requested, it is necessary to "
+                "specify the necessary sigma-clipping parameters with the "
+                "'--sigmaclip' option");
+        break;
+      }
+
   /* Check for dimension-specific columns. */
   switch(p->objects->ndim)
     {
@@ -301,6 +334,9 @@ columns_sanity_check(struct mkcatalogparams *p)
           case UI_KEY_GEOSEMIMAJOR:
           case UI_KEY_GEOSEMIMINOR:
           case UI_KEY_GEOAXISRATIO:
+          case UI_KEY_HALFSUMRADIUS:
+          case UI_KEY_FRACMAXRADIUS1:
+          case UI_KEY_FRACMAXRADIUS2:
           case UI_KEY_GEOPOSITIONANGLE:
             error(EXIT_FAILURE, 0, "columns requiring second moment "
                   "calculations (like semi-major, semi-minor, axis ratio "
@@ -350,8 +386,8 @@ columns_define_alloc(struct mkcatalogparams *p)
   for(colcode=p->columnids; colcode!=NULL; colcode=colcode->next)
     {
       /* Set the column-specific parameters, please follow the same order
-         as `args.h'. IMPORTANT: we want the names to be the same as the
-         option names. Note that zero `disp_' variables will be
+         as 'args.h'. IMPORTANT: we want the names to be the same as the
+         option names. Note that zero 'disp_' variables will be
          automatically determined.*/
       switch(colcode->v)
         {
@@ -420,6 +456,33 @@ columns_define_alloc(struct mkcatalogparams *p)
           oiflag[ OCOL_NUM ] = ciflag[ CCOL_NUM ] = 1;
           break;
 
+        case UI_KEY_AREAARCSEC2:
+          name           = "AREA_ARCSEC2";
+          unit           = "arcsec2";
+          ocomment       = "Number of non-blank pixels in arcsec^2";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM ] = ciflag[ CCOL_NUM ] = 1;
+          break;
+
+        case UI_KEY_SURFACEBRIGHTNESS:
+          name           = "SURFACE_BRIGHTNESS";
+          unit           = "mag/arcsec^2";
+          ocomment       = "Surface brightness (magnitude of 
brightness/area).";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM ] = ciflag[ CCOL_NUM ] = 1;
+          oiflag[ OCOL_SUM ] = ciflag[ CCOL_SUM ] = 1;
+          break;
+
         case UI_KEY_AREAXY:
           name           = "AREAXY";
           unit           = "counter";
@@ -658,6 +721,116 @@ columns_define_alloc(struct mkcatalogparams *p)
           disp_precision = 3;
           oiflag[ OCOL_C_GZ ] = 1;
 
+        case UI_KEY_MINVX:
+          name           = "MIN_V_X";
+          unit           = "pixel";
+          ocomment       = "Minimum value X pixel position.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 10;
+          disp_precision = 0;
+          oiflag[ OCOL_MINVX   ] = ciflag[ CCOL_MINVX   ] = 1;
+          oiflag[ OCOL_MINVNUM ] = ciflag[ CCOL_MINVNUM ] = 1;
+          break;
+
+        case UI_KEY_MAXVX:
+          name           = "MAX_V_X";
+          unit           = "pixel";
+          ocomment       = "Maximum value X pixel position.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 10;
+          disp_precision = 0;
+          oiflag[ OCOL_MAXVX   ] = ciflag[ CCOL_MAXVX   ] = 1;
+          oiflag[ OCOL_MAXVNUM ] = ciflag[ CCOL_MAXVNUM ] = 1;
+          break;
+
+        case UI_KEY_MINVY:
+          name           = "MIN_V_Y";
+          unit           = "pixel";
+          ocomment       = "Minimum value Y pixel position.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 10;
+          disp_precision = 0;
+          oiflag[ OCOL_MINVY   ] = ciflag[ CCOL_MINVY   ] = 1;
+          oiflag[ OCOL_MINVNUM ] = ciflag[ CCOL_MINVNUM ] = 1;
+          break;
+
+        case UI_KEY_MAXVY:
+          name           = "MAX_V_Y";
+          unit           = "pixel";
+          ocomment       = "Maximum value Y pixel position.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 10;
+          disp_precision = 0;
+          oiflag[ OCOL_MAXVY   ] = ciflag[ CCOL_MAXVY   ] = 1;
+          oiflag[ OCOL_MAXVNUM ] = ciflag[ CCOL_MAXVNUM ] = 1;
+          break;
+
+        case UI_KEY_MINVZ:
+          name           = "MIN_V_Z";
+          unit           = "pixel";
+          ocomment       = "Minimum value Z pixel position.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 10;
+          disp_precision = 0;
+          oiflag[ OCOL_MINVZ   ] = ciflag[ CCOL_MINVZ   ] = 1;
+          oiflag[ OCOL_MINVNUM ] = ciflag[ CCOL_MINVNUM ] = 1;
+          break;
+
+        case UI_KEY_MAXVZ:
+          name           = "MAX_V_Z";
+          unit           = "pixel";
+          ocomment       = "Maximum value Z pixel position.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 10;
+          disp_precision = 0;
+          oiflag[ OCOL_MAXVZ   ] = ciflag[ CCOL_MAXVZ   ] = 1;
+          oiflag[ OCOL_MAXVNUM ] = ciflag[ CCOL_MAXVNUM ] = 1;
+          break;
+
+        case UI_KEY_MINVNUM:
+          name           = "MIN_V_NUM";
+          unit           = "counter";
+          ocomment       = "Number of pixels with the minimum value.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_UINT32;
+          ctype          = GAL_TYPE_UINT32;
+          disp_fmt       = 0;
+          disp_width     = 10;
+          disp_precision = 0;
+          oiflag[ OCOL_MINVNUM ] = ciflag[ CCOL_MINVNUM ] = 1;
+          break;
+
+        case UI_KEY_MAXVNUM:
+          name           = "MAX_V_NUM";
+          unit           = "counter";
+          ocomment       = "Number of pixels with the maximum value..";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_UINT32;
+          ctype          = GAL_TYPE_UINT32;
+          disp_fmt       = 0;
+          disp_width     = 10;
+          disp_precision = 0;
+          oiflag[ OCOL_MAXVNUM ] = ciflag[ CCOL_MAXVNUM ] = 1;
+          break;
+
         case UI_KEY_MINX:
           name           = "MIN_X";
           unit           = "pixel";
@@ -669,6 +842,7 @@ columns_define_alloc(struct mkcatalogparams *p)
           disp_width     = 10;
           disp_precision = 0;
           ciflag[ CCOL_MINX ] = 1;
+          oiflag[ OCOL_NUMALL ] = ciflag[ CCOL_NUMALL ] = 1;
           break;
 
         case UI_KEY_MAXX:
@@ -682,6 +856,7 @@ columns_define_alloc(struct mkcatalogparams *p)
           disp_width     = 10;
           disp_precision = 0;
           ciflag[ CCOL_MAXX ] = 1;
+          oiflag[ OCOL_NUMALL ] = ciflag[ CCOL_NUMALL ] = 1;
           break;
 
         case UI_KEY_MINY:
@@ -695,6 +870,7 @@ columns_define_alloc(struct mkcatalogparams *p)
           disp_width     = 10;
           disp_precision = 0;
           ciflag[ CCOL_MINY ] = 1;
+          oiflag[ OCOL_NUMALL ] = ciflag[ CCOL_NUMALL ] = 1;
           break;
 
         case UI_KEY_MAXY:
@@ -708,6 +884,7 @@ columns_define_alloc(struct mkcatalogparams *p)
           disp_width     = 10;
           disp_precision = 0;
           ciflag[ CCOL_MAXY ] = 1;
+          oiflag[ OCOL_NUMALL ] = ciflag[ CCOL_NUMALL ] = 1;
           break;
 
         case UI_KEY_MINZ:
@@ -721,6 +898,7 @@ columns_define_alloc(struct mkcatalogparams *p)
           disp_width     = 10;
           disp_precision = 0;
           ciflag[ CCOL_MINZ ] = 1;
+          oiflag[ OCOL_NUMALL ] = ciflag[ CCOL_NUMALL ] = 1;
           break;
 
         case UI_KEY_MAXZ:
@@ -734,6 +912,7 @@ columns_define_alloc(struct mkcatalogparams *p)
           disp_width     = 10;
           disp_precision = 0;
           ciflag[ CCOL_MAXZ ] = 1;
+          oiflag[ OCOL_NUMALL ] = ciflag[ CCOL_NUMALL ] = 1;
           break;
 
         case UI_KEY_W1:
@@ -1075,6 +1254,84 @@ columns_define_alloc(struct mkcatalogparams *p)
                                    ciflag[ CCOL_RIV_SUM ] = 1;
           break;
 
+        case UI_KEY_MAXIMUM:
+          name           = "MAXIMUM";
+          unit           = MKCATALOG_NO_UNIT;
+          ocomment       = "Maximum of sky subtracted values.";
+          ccomment       = "Maximum of pixels subtracted by rivers.";
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 5;
+          oiflag[ OCOL_NUM     ] = ciflag[ CCOL_NUM     ] = 1;
+          oiflag[ OCOL_MAXIMUM ] = ciflag[ CCOL_MAXIMUM ] = 1;
+          break;
+
+        case UI_KEY_SIGCLIPNUMBER:
+          name           = "SIGCLIP-NUMBER";
+          unit           = "counter";
+          ocomment       = "Number of pixels in Sigma-clipped object";
+          ccomment       = "Number of pixels in Sigma-clipped clump.";
+          otype          = GAL_TYPE_INT32;
+          ctype          = GAL_TYPE_INT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM        ] = ciflag[ CCOL_NUM        ] = 1;
+          oiflag[ OCOL_SIGCLIPNUM ] = ciflag[ CCOL_SIGCLIPNUM ] = 1;
+                                      ciflag[ CCOL_RIV_NUM    ] = 1;
+                                      ciflag[ CCOL_RIV_SUM    ] = 1;
+          break;
+
+        case UI_KEY_SIGCLIPMEDIAN:
+          name           = "SIGCLIP-MEDIAN";
+          unit           = MKCATALOG_NO_UNIT;
+          ocomment       = "Sigma-clipped median of object pixels.";
+          ccomment       = "Sigma-clipped median of clump pixels.";
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 5;
+          oiflag[ OCOL_NUM           ] = ciflag[ CCOL_NUM           ] = 1;
+          oiflag[ OCOL_SIGCLIPMEDIAN ] = ciflag[ CCOL_SIGCLIPMEDIAN ] = 1;
+                                         ciflag[ CCOL_RIV_NUM       ] = 1;
+                                         ciflag[ CCOL_RIV_SUM       ] = 1;
+          break;
+
+        case UI_KEY_SIGCLIPMEAN:
+          name           = "SIGCLIP-MEAN";
+          unit           = MKCATALOG_NO_UNIT;
+          ocomment       = "Sigma-clipped mean of object pixels.";
+          ccomment       = "Sigma-clipped mean of clump pixels.";
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 5;
+          oiflag[ OCOL_NUM         ] = ciflag[ CCOL_NUM         ] = 1;
+          oiflag[ OCOL_SIGCLIPMEAN ] = ciflag[ CCOL_SIGCLIPMEAN ] = 1;
+                                       ciflag[ CCOL_RIV_NUM     ] = 1;
+                                       ciflag[ CCOL_RIV_SUM     ] = 1;
+          break;
+
+        case UI_KEY_SIGCLIPSTD:
+          name           = "SIGCLIP-STD";
+          unit           = MKCATALOG_NO_UNIT;
+          ocomment       = "Sigma-clipped standard deviation of object 
pixels.";
+          ccomment       = "Sigma-clipped standard deviation of clump pixels.";
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 5;
+          oiflag[ OCOL_NUM        ] = ciflag[ CCOL_NUM        ] = 1;
+          oiflag[ OCOL_SIGCLIPSTD ] = ciflag[ CCOL_SIGCLIPSTD ] = 1;
+                                      ciflag[ CCOL_RIV_NUM   ] = 1;
+                                      ciflag[ CCOL_RIV_SUM   ] = 1;
+          break;
+
         case UI_KEY_MAGNITUDE:
           name           = "MAGNITUDE";
           unit           = "log";
@@ -1257,6 +1514,7 @@ columns_define_alloc(struct mkcatalogparams *p)
           disp_precision = 3;
           oiflag[ OCOL_SUM         ] = ciflag[ CCOL_SUM         ] = 1;
           oiflag[ OCOL_SUM_VAR     ] = ciflag[ CCOL_SUM_VAR     ] = 1;
+                                       ciflag[ CCOL_NUM         ] = 1;
                                        ciflag[ CCOL_RIV_NUM     ] = 1;
                                        ciflag[ CCOL_RIV_SUM     ] = 1;
                                        ciflag[ CCOL_RIV_SUM_VAR ] = 1;
@@ -1458,6 +1716,180 @@ columns_define_alloc(struct mkcatalogparams *p)
           oiflag[ OCOL_GXY    ] = ciflag[ CCOL_GXY    ] = 1;
           break;
 
+        case UI_KEY_HALFSUMAREA:
+          name           = "HALF_SUM_AREA";
+          unit           = "counter";
+          ocomment       = "Number of brightest pixels containing half of 
total sum.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_INT32;
+          ctype          = GAL_TYPE_INT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM        ] = ciflag[ CCOL_NUM        ] = 1;
+          oiflag[ OCOL_SUM        ] = ciflag[ CCOL_SUM        ] = 1;
+          oiflag[ OCOL_HALFSUMNUM ] = ciflag[ CCOL_HALFSUMNUM ] = 1;
+          break;
+
+        case UI_KEY_HALFMAXAREA:
+          name           = "HALF_MAX_AREA";
+          unit           = "counter";
+          ocomment       = "Number of pixels with a value larger than half the 
maximum.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_INT32;
+          ctype          = GAL_TYPE_INT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM         ] = ciflag[ CCOL_NUM         ] = 1;
+          oiflag[ OCOL_HALFMAXNUM  ] = ciflag[ CCOL_HALFMAXNUM  ] = 1;
+          break;
+
+        case UI_KEY_HALFMAXSUM:
+          name           = "HALF_MAX_SUM";
+          unit           = MKCATALOG_NO_UNIT;
+          ocomment       = "Sum of pixels with a value larger than half the 
maximum.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM        ] = ciflag[ CCOL_NUM        ] = 1;
+          oiflag[ OCOL_HALFMAXSUM ] = ciflag[ CCOL_HALFMAXSUM ] = 1;
+          break;
+
+        case UI_KEY_HALFMAXSB:
+          name           = "HALF_MAX_SB";
+          unit           = "mag/arcsec^2";
+          ocomment       = "Brightness within half the maximum, divided by its 
area.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM        ] = ciflag[ CCOL_NUM        ] = 1;
+          oiflag[ OCOL_HALFMAXNUM ] = ciflag[ CCOL_HALFMAXNUM ] = 1;
+          oiflag[ OCOL_HALFMAXSUM ] = ciflag[ CCOL_HALFMAXSUM ] = 1;
+          break;
+
+        case UI_KEY_HALFSUMSB:
+          name           = "HALF_SUM_SB";
+          unit           = "mag/arcsec^2";
+          ocomment       = "Half the brightness, divided by its area.";
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 5;
+          oiflag[ OCOL_NUM        ] = ciflag[ CCOL_NUM        ] = 1;
+          oiflag[ OCOL_SUM        ] = ciflag[ CCOL_SUM        ] = 1;
+          oiflag[ OCOL_HALFSUMNUM ] = ciflag[ CCOL_HALFSUMNUM ] = 1;
+          break;
+
+        case UI_KEY_FRACMAXSUM1:
+        case UI_KEY_FRACMAXSUM2:
+          name           = ( colcode->v==UI_KEY_FRACMAXSUM1
+                             ? "FRAC_MAX_SUM_1"
+                             : "FRAC_MAX_SUM_2" );
+          unit           = MKCATALOG_NO_UNIT;
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM ] = ciflag[ CCOL_NUM ] = 1;
+          oiflag[ OCOL_SUM ] = ciflag[ CCOL_SUM ] = 1;
+          if(colcode->v==UI_KEY_FRACMAXSUM1)
+            {
+              ocomment = "Sum of pixels brighter than 1st fraction of 
maximum.";
+              oiflag[ OCOL_FRACMAX1SUM ] = ciflag[ CCOL_FRACMAX1SUM ] = 1;
+            }
+          else
+            {
+              ocomment = "Sum of pixels brighter than 2nd fraction of 
maximum.";
+              oiflag[ OCOL_FRACMAX2SUM ] = ciflag[ CCOL_FRACMAX2SUM ] = 1;
+            }
+          break;
+
+        case UI_KEY_FRACMAXAREA1:
+        case UI_KEY_FRACMAXAREA2:
+          name           = ( colcode->v==UI_KEY_FRACMAXAREA1
+                             ? "FRAC_MAX_AREA_1"
+                             : "FRAC_MAX_AREA_2" );
+          unit           = "counter";
+          ocomment       = "Number of pixels brighter than given fraction of 
maximum value.";
+          ccomment       = ocomment;
+          otype          = GAL_TYPE_INT32;
+          ctype          = GAL_TYPE_INT32;
+          disp_fmt       = 0;
+          disp_width     = 6;
+          disp_precision = 0;
+          oiflag[ OCOL_NUM ] = ciflag[ CCOL_NUM ] = 1;
+          oiflag[ OCOL_SUM ] = ciflag[ CCOL_SUM ] = 1;
+          if(colcode->v==UI_KEY_FRACMAXAREA1)
+            oiflag[ OCOL_FRACMAX1NUM ] = ciflag[ CCOL_FRACMAX1NUM ] = 1;
+          else
+            oiflag[ OCOL_FRACMAX2NUM ] = ciflag[ CCOL_FRACMAX2NUM ] = 1;
+          break;
+
+        case UI_KEY_FWHM:
+        case UI_KEY_HALFMAXRADIUS:
+        case UI_KEY_HALFSUMRADIUS:
+        case UI_KEY_FRACMAXRADIUS1:
+        case UI_KEY_FRACMAXRADIUS2:
+          unit           = "pixels";
+          otype          = GAL_TYPE_FLOAT32;
+          ctype          = GAL_TYPE_FLOAT32;
+          disp_fmt       = GAL_TABLE_DISPLAY_FMT_GENERAL;
+          disp_width     = 10;
+          disp_precision = 3;
+          oiflag[ OCOL_NUM        ] = ciflag[ CCOL_NUM        ] = 1; /* 
halfsumarea */
+          oiflag[ OCOL_SUM        ] = ciflag[ CCOL_SUM        ] = 1;
+          oiflag[ OCOL_SUMWHT     ] = ciflag[ CCOL_SUMWHT     ] = 1; /* 
axisratio. */
+          oiflag[ OCOL_VX         ] = ciflag[ CCOL_VX         ] = 1;
+          oiflag[ OCOL_VY         ] = ciflag[ CCOL_VY         ] = 1;
+          oiflag[ OCOL_VXX        ] = ciflag[ CCOL_VXX        ] = 1;
+          oiflag[ OCOL_VYY        ] = ciflag[ CCOL_VYY        ] = 1;
+          oiflag[ OCOL_VXY        ] = ciflag[ CCOL_VXY        ] = 1;
+          oiflag[ OCOL_NUMALL     ] = ciflag[ CCOL_NUMALL     ] = 1;
+          oiflag[ OCOL_GX         ] = ciflag[ CCOL_GX         ] = 1;
+          oiflag[ OCOL_GY         ] = ciflag[ CCOL_GY         ] = 1;
+          oiflag[ OCOL_GXX        ] = ciflag[ CCOL_GXX        ] = 1;
+          oiflag[ OCOL_GYY        ] = ciflag[ CCOL_GYY        ] = 1;
+          oiflag[ OCOL_GXY        ] = ciflag[ CCOL_GXY        ] = 1;
+          switch(colcode->v)
+            {
+            case UI_KEY_FWHM:
+              name="FWHM";
+              oiflag[ OCOL_HALFMAXNUM  ] = ciflag[ CCOL_HALFMAXNUM  ] = 1;
+              ocomment = "Full width at half maximum (accounting for 
ellipticity).";
+              break;
+            case UI_KEY_HALFMAXRADIUS:
+              name="HALF_MAX_RADIUS";
+              oiflag[ OCOL_HALFMAXNUM  ] = ciflag[ CCOL_HALFMAXNUM  ] = 1;
+              ocomment = "Radius at half of maximum (accounting for 
ellipticity).";
+              break;
+            case UI_KEY_HALFSUMRADIUS:
+              name="HALF_SUM_RADIUS";
+              oiflag[ OCOL_HALFSUMNUM  ] = ciflag[ CCOL_HALFSUMNUM  ] = 1;
+              ocomment = "Radius at half of total sum (accounting for 
ellipticity).";
+              break;
+            case UI_KEY_FRACMAXRADIUS1:
+              name="FRAC_MAX_RADIUS_1";
+              oiflag[ OCOL_FRACMAX1NUM ] = ciflag[ CCOL_FRACMAX1NUM ] = 1;
+              ocomment = "Radius derived from area of 1st fraction of 
maximum.";
+              break;
+            case UI_KEY_FRACMAXRADIUS2:
+              name="FRAC_MAX_RADIUS_2";
+              oiflag[ OCOL_FRACMAX2NUM ] = ciflag[ CCOL_FRACMAX2NUM ] = 1;
+              ocomment = "Radius derived from area of 2nd fraction of 
maximum.";
+              break;
+            }
+          break;
+
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to fix "
                 "the problem. The code %d is not an internally recognized "
@@ -1465,8 +1897,8 @@ columns_define_alloc(struct mkcatalogparams *p)
         }
 
 
-      /* If this is an objects column, add it to the list of columns. We
-         will be using the `status' element to keep the MakeCatalog code
+      /* If this is an object's column, add it to the list of columns. We
+         will be using the 'status' element to keep the MakeCatalog code
          for the columns. */
       if(otype!=GAL_TYPE_INVALID)
         {
@@ -1504,7 +1936,7 @@ columns_define_alloc(struct mkcatalogparams *p)
              the user.
 
              We'll just ignore the clump-specific ID-related columns,
-             because the `--ids' (generic for both objects and clumps) is a
+             because the '--ids' (generic for both objects and clumps) is a
              simple generic solution for identifiers. Also, ultimately,
              they aren't measurements. */
           else if( otype==GAL_TYPE_INVALID
@@ -1516,14 +1948,14 @@ columns_define_alloc(struct mkcatalogparams *p)
 
 
   /* If the user has asked for clump-only columns, but no clumps catalog is
-     to be created (the `--clumpscat' option was not given or there were no
+     to be created (the '--clumpscat' option was not given or there were no
      clumps in the specified image), then print an informative message that
      the columns in question will be ignored. */
   if(noclumpimg)
     {
       gal_list_str_reverse(&noclumpimg);
       fprintf(stderr, "WARNING: the following column(s) are unique to "
-              "clumps (not objects), but the `--clumpscat' option has not "
+              "clumps (not objects), but the '--clumpscat' option has not "
               "been called, or there were no clumps in the clumps labeled "
               "image. Hence, these columns will be ignored in the "
               "output.\n\n");
@@ -1563,7 +1995,10 @@ columns_define_alloc(struct mkcatalogparams *p)
 /**********            Column calculation           ***************/
 /******************************************************************/
 #define MKC_RATIO(TOP,BOT) ( (BOT)!=0.0f ? (TOP)/(BOT) : NAN )
-#define MKC_MAG(B)         ( ((B)>0) ? -2.5f * log10(B) + p->zeropoint : NAN )
+#define MKC_MAG(B) ( ((B)>0) ? -2.5f * log10(B) + p->zeropoint : NAN )
+#define MKC_SB(B, A) ( ((B)>0 && (A)>0)                                 \
+                       ? MKC_MAG(B) + 2.5f * log10((A) * p->pixelarcsecsq) \
+                       : NAN )
 
 
 
@@ -1591,8 +2026,10 @@ columns_sn(struct mkcatalogparams *p, double *row, int 
o0c1)
   /* When grown clumps are requested from NoiseChisel, some "clumps" will
      completely cover their objects and there will be no rivers. So if this
      is a clump, and the river area is 0, we should treat the S/N as a an
-     object. */
-  double O = (o0c1 && row[ CCOL_RIV_NUM ]) ? row[ CCOL_RIV_SUM ] : 0.0 ;
+     object (and set the outer flux to 0.0). */
+  double O = ( (o0c1 && row[ CCOL_RIV_NUM ])
+               ? (row[ CCOL_NUM ]*row[ CCOL_RIV_SUM ]/row[ CCOL_RIV_NUM ])
+               : 0.0 );
 
   /* Return the derived value. */
   return sqrt(1/p->cpscorr) * (I-O) / columns_brightness_error(p, row, o0c1);
@@ -1721,13 +2158,14 @@ columns_clump_brightness(double *ci)
 
 /* Measure the minimum and maximum positions. */
 static uint32_t
-columns_xy_extrema(struct mkcatalog_passparams *pp, size_t *coord, int key)
+columns_xy_extrema(struct mkcatalog_passparams *pp, double *oi,
+                   size_t *coord, int key)
 {
   size_t ndim=pp->tile->ndim;
   gal_data_t *tile=pp->tile, *block=tile->block;
 
-  /* We only want to do the coordinate estimation once: in `columns_fill',
-     we initialized the coordinates with `GAL_BLANK_SIZE_T'. When the
+  /* We only want to do the coordinate estimation once: in 'columns_fill',
+     we initialized the coordinates with 'GAL_BLANK_SIZE_T'. When the
      coordinate has already been measured already, it won't have this value
      any more. */
   if(coord[0]==GAL_BLANK_SIZE_T)
@@ -1736,21 +2174,25 @@ columns_xy_extrema(struct mkcatalog_passparams *pp, 
size_t *coord, int key)
                                                          block->type),
                                  block->ndim, block->dsize, coord);
 
-  /* Return the proper value: note that `coord' is in C standard: starting
+  /* Return the proper value: note that 'coord' is in C standard: starting
      from the slowest dimension and counting from zero. */
-  switch(key)
-    {
-    case UI_KEY_MINX: return coord[ndim-1] + 1;                   break;
-    case UI_KEY_MAXX: return coord[ndim-1] + tile->dsize[ndim-1]; break;
-    case UI_KEY_MINY: return coord[ndim-2] + 1;                   break;
-    case UI_KEY_MAXY: return coord[ndim-2] + tile->dsize[ndim-2]; break;
-    case UI_KEY_MINZ: return coord[ndim-3] + 1;                   break;
-    case UI_KEY_MAXZ: return coord[ndim-3] + tile->dsize[ndim-3]; break;
-    default:
-      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
-            "problem. The value %d is not a recognized value", __func__,
-            PACKAGE_BUGREPORT, key);
-    }
+  if(oi[OCOL_NUMALL])
+    switch(key)
+      {
+      case UI_KEY_MINX: return coord[ndim-1] + 1;                   break;
+      case UI_KEY_MAXX: return coord[ndim-1] + tile->dsize[ndim-1]; break;
+      case UI_KEY_MINY: return coord[ndim-2] + 1;                   break;
+      case UI_KEY_MAXY: return coord[ndim-2] + tile->dsize[ndim-2]; break;
+      case UI_KEY_MINZ: return coord[ndim-3] + 1;                   break;
+      case UI_KEY_MAXZ: return coord[ndim-3] + tile->dsize[ndim-3]; break;
+      default:
+        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+              "problem. The value %d is not a recognized value", __func__,
+              PACKAGE_BUGREPORT, key);
+      }
+  else
+    return 0;
+
 
   /* Control should not reach here. */
   error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to fix the "
@@ -1766,28 +2208,28 @@ columns_xy_extrema(struct mkcatalog_passparams *pp, 
size_t *coord, int key)
 /* The magnitude error is directly derivable from the S/N:
 
    To derive the error in measuring the magnitude from the S/N, let's take
-   `F' as the flux, `Z' is the zeropoint, `M' is the magnitude, `S' is the
-   S/N, and `D' to stand for capital delta (or error in a value) then from
+   'F' as the flux, 'Z' is the zeropoint, 'M' is the magnitude, 'S' is the
+   S/N, and 'D' to stand for capital delta (or error in a value) then from
 
-      `M = -2.5*log10(F) + Z'
+      'M = -2.5*log10(F) + Z'
 
    we get the following equation after calculating the derivative with
    respect to F.
 
-      `dM/df = -2.5 * ( 1 / ( F * ln(10) ) )'
+      'dM/df = -2.5 * ( 1 / ( F * ln(10) ) )'
 
-   From the Tailor series, `DM' can be written as:
+   From the Tailor series, 'DM' can be written as:
 
-      `DM = dM/dF * DF'
+      'DM = dM/dF * DF'
 
    So
 
-      `DM = |-2.5/ln(10)| * DF/F'
+      'DM = |-2.5/ln(10)| * DF/F'
 
-   But `DF/F' is just the inverse of the Signal to noise ratio, or
-  `1/S'. So
+   But 'DF/F' is just the inverse of the Signal to noise ratio, or
+  '1/S'. So
 
-      `DM = 2.5 / ( S * ln(10) )'               */
+      'DM = 2.5 / ( S * ln(10) )'               */
 #define MAG_ERROR(P,ROW,O0C1) ( 2.5f                                    \
                                 / ( ( columns_sn((P),(ROW),(O0C1)) > 0  \
                                       ? columns_sn((P),(ROW),(O0C1))    \
@@ -1801,7 +2243,7 @@ columns_xy_extrema(struct mkcatalog_passparams *pp, 
size_t *coord, int key)
 
 /* All the raw first and second pass information has been collected, now
    write them into the output columns. The list of columns here is in the
-   same order as `columns_alloc_set_out_cols', see there for the type of
+   same order as 'columns_alloc_set_out_cols', see there for the type of
    each column. */
 #define POS_V_G(ARRAY, SUMWHT_COL, NUMALL_COL, V_COL, G_COL)            \
   ( (ARRAY)[ SUMWHT_COL ]>0                                             \
@@ -1817,12 +2259,21 @@ columns_fill(struct mkcatalog_passparams *pp)
   void *colarr;
   gal_data_t *column;
   double *ci, *oi=pp->oi;
+  size_t tmpind=GAL_BLANK_SIZE_T;
   size_t coord[3]={GAL_BLANK_SIZE_T, GAL_BLANK_SIZE_T, GAL_BLANK_SIZE_T};
 
-  size_t sr=pp->clumpstartindex, cind, coind;
-  size_t oind=pp->object-1; /* IDs start from 1, indexs from 0. */
+  size_t i, cind, coind, sr=pp->clumpstartindex, oind=GAL_BLANK_SIZE_T;
   double **vo=NULL, **vc=NULL, **go=NULL, **gc=NULL, **vcc=NULL, **gcc=NULL;
 
+  /* Find the object's index in final catalog. */
+  if(p->outlabs)
+    {
+      for(i=0;i<p->numobjects;++i)
+        if(p->outlabs[i]==pp->object)
+          { oind=i; break; }
+    }
+  else oind=pp->object-1;
+
   /* If a WCS column is requested (check will be done inside the function),
      then set the pointers. */
   columns_set_wcs_pointers(p, &vo, &vc, &go, &gc, &vcc, &gcc);
@@ -1854,6 +2305,14 @@ columns_fill(struct mkcatalog_passparams *pp)
           ((int32_t *)colarr)[oind] = oi[OCOL_NUM];
           break;
 
+        case UI_KEY_AREAARCSEC2:
+          ((float *)colarr)[oind] = oi[OCOL_NUM]*p->pixelarcsecsq;
+          break;
+
+        case UI_KEY_SURFACEBRIGHTNESS:
+          ((float *)colarr)[oind] = MKC_SB(oi[OCOL_SUM], oi[OCOL_NUM]);
+          break;
+
         case UI_KEY_AREAXY:
           ((int32_t *)colarr)[oind] = oi[OCOL_NUMXY];
           break;
@@ -1930,13 +2389,45 @@ columns_fill(struct mkcatalog_passparams *pp)
           ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_C_GZ],
                                                oi[OCOL_C_NUMALL] );
 
+        case UI_KEY_MINVX:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_MINVX], 
oi[OCOL_MINVNUM] );
+          break;
+
+        case UI_KEY_MAXVX:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_MAXVX], 
oi[OCOL_MAXVNUM] );
+          break;
+
+        case UI_KEY_MINVY:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_MINVY], 
oi[OCOL_MINVNUM] );
+          break;
+
+        case UI_KEY_MAXVY:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_MAXVY], 
oi[OCOL_MAXVNUM] );
+          break;
+
+        case UI_KEY_MINVZ:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_MINVZ], 
oi[OCOL_MINVNUM] );
+          break;
+
+        case UI_KEY_MAXVZ:
+          ((float *)colarr)[oind] = MKC_RATIO( oi[OCOL_MAXVZ], 
oi[OCOL_MAXVNUM] );
+          break;
+
+        case UI_KEY_MINVNUM:
+          ((uint32_t *)colarr)[oind] = oi[OCOL_MINVNUM];
+          break;
+
+        case UI_KEY_MAXVNUM:
+          ((uint32_t *)colarr)[oind] = oi[OCOL_MAXVNUM];
+          break;
+
         case UI_KEY_MINX:
         case UI_KEY_MAXX:
         case UI_KEY_MINY:
         case UI_KEY_MAXY:
         case UI_KEY_MINZ:
         case UI_KEY_MAXZ:
-          ((uint32_t *)colarr)[oind]=columns_xy_extrema(pp, coord, key);
+          ((uint32_t *)colarr)[oind]=columns_xy_extrema(pp, oi, coord, key);
           break;
 
         case UI_KEY_W1:
@@ -2011,6 +2502,26 @@ columns_fill(struct mkcatalog_passparams *pp)
                                       : NAN );
           break;
 
+        case UI_KEY_MAXIMUM:
+          ((float *)colarr)[oind] = oi[ OCOL_MAXIMUM ];
+          break;
+
+        case UI_KEY_SIGCLIPNUMBER:
+          ((int32_t *)colarr)[oind] = oi[ OCOL_SIGCLIPNUM ];
+          break;
+
+        case UI_KEY_SIGCLIPMEDIAN:
+          ((float *)colarr)[oind] = oi[ OCOL_SIGCLIPMEDIAN ];
+          break;
+
+        case UI_KEY_SIGCLIPMEAN:
+          ((float *)colarr)[oind] = oi[ OCOL_SIGCLIPMEAN ];
+          break;
+
+        case UI_KEY_SIGCLIPSTD:
+          ((float *)colarr)[oind] = oi[ OCOL_SIGCLIPSTD ];
+          break;
+
         case UI_KEY_MAGNITUDE:
           ((float *)colarr)[oind] = ( oi[ OCOL_NUM ]>0.0f
                                       ? MKC_MAG(oi[ OCOL_SUM ])
@@ -2056,12 +2567,12 @@ columns_fill(struct mkcatalog_passparams *pp)
           break;
 
         case UI_KEY_SKY:
-          ((float *)colarr)[oind] = MKC_RATIO(oi[OCOL_SUMSKY], oi[OCOL_NUM]);
+          ((float *)colarr)[oind] = MKC_RATIO(oi[OCOL_SUMSKY], 
oi[OCOL_NUMSKY]);
           break;
 
         case UI_KEY_STD:
           ((float *)colarr)[oind] = sqrt( MKC_RATIO( oi[ OCOL_SUMVAR ],
-                                                     oi[ OCOL_NUM    ]) );
+                                                     oi[ OCOL_NUMVAR ]) );
           break;
 
         case UI_KEY_SEMIMAJOR:
@@ -2100,6 +2611,68 @@ columns_fill(struct mkcatalog_passparams *pp)
           ((float *)colarr)[oind] = columns_second_order(pp, oi, key, 0);
           break;
 
+        case UI_KEY_HALFSUMAREA:
+          ((int32_t *)colarr)[oind] = oi[OCOL_HALFSUMNUM];
+          break;
+
+        case UI_KEY_HALFMAXAREA:
+          ((int32_t *)colarr)[oind] = oi[OCOL_HALFMAXNUM];
+          break;
+
+        case UI_KEY_HALFMAXSUM:
+          ((float *)colarr)[oind] = oi[OCOL_HALFMAXNUM];
+          break;
+
+        case UI_KEY_HALFMAXSB:
+          ((float *)colarr)[oind] = MKC_SB( oi[OCOL_HALFMAXSUM],
+                                            oi[OCOL_HALFMAXNUM] );
+          break;
+
+        case UI_KEY_FRACMAXSUM1:
+          ((float *)colarr)[oind] = oi[OCOL_FRACMAX1SUM];
+          break;
+
+        case UI_KEY_FRACMAXSUM2:
+          ((float *)colarr)[oind] = oi[OCOL_FRACMAX2SUM];
+          break;
+
+        case UI_KEY_FRACMAXAREA1:
+          ((int32_t *)colarr)[oind] = oi[OCOL_FRACMAX1NUM];
+          break;
+
+        case UI_KEY_FRACMAXAREA2:
+          ((int32_t *)colarr)[oind] = oi[OCOL_FRACMAX2NUM];
+          break;
+
+        case UI_KEY_HALFSUMSB:
+          ((float *)colarr)[oind] = MKC_SB( oi[OCOL_SUM]/2.0f,
+                                            oi[OCOL_HALFSUMNUM] );
+          break;
+
+        case UI_KEY_FWHM:
+        case UI_KEY_HALFMAXRADIUS:
+        case UI_KEY_HALFSUMRADIUS:
+        case UI_KEY_FRACMAXRADIUS1:
+        case UI_KEY_FRACMAXRADIUS2:
+          /* First derive the axis ratio (as 'tmp'), then set the index to
+             use and calculate the radius from the area and axis ratio. */
+          tmp = ( columns_second_order(pp, oi, UI_KEY_SEMIMINOR, 0)
+                  / columns_second_order(pp, oi, UI_KEY_SEMIMAJOR, 0) );
+          switch(key)
+            {
+            case UI_KEY_FWHM:           tmpind=OCOL_HALFMAXNUM;  break;
+            case UI_KEY_HALFMAXRADIUS:  tmpind=OCOL_HALFMAXNUM;  break;
+            case UI_KEY_HALFSUMRADIUS:  tmpind=OCOL_HALFSUMNUM;  break;
+            case UI_KEY_FRACMAXRADIUS1: tmpind=OCOL_FRACMAX1NUM; break;
+            case UI_KEY_FRACMAXRADIUS2: tmpind=OCOL_FRACMAX2NUM; break;
+            }
+          tmp = sqrt( oi[tmpind]/(tmp*M_PI) );
+          if(key==UI_KEY_FWHM)
+            ((float *)colarr)[oind] = tmp<1e-6 ? NAN : (tmp*2);
+          else
+            ((float *)colarr)[oind] = tmp<1e-6 ? NAN : tmp;
+          break;
+
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
                 "solve the problem. the output column code %d not recognized "
@@ -2111,8 +2684,8 @@ columns_fill(struct mkcatalog_passparams *pp)
   for(column=p->clumpcols; column!=NULL; column=column->next)
     for(coind=0;coind<pp->clumpsinobj;++coind)
       {
-        /* `coind': clump-in-object-index.
-           `cind': clump-index (over all the catalog). */
+        /* 'coind': clump-in-object-index.
+           'cind': clump-index (over all the catalog). */
         cind   = sr + coind;
         colarr = column->array;
         key    = column->status;
@@ -2138,6 +2711,14 @@ columns_fill(struct mkcatalog_passparams *pp)
             ((int32_t *)colarr)[cind]=ci[CCOL_NUM];
             break;
 
+          case UI_KEY_AREAARCSEC2:
+            ((float *)colarr)[cind]=ci[CCOL_NUM]*p->pixelarcsecsq;
+            break;
+
+          case UI_KEY_SURFACEBRIGHTNESS:
+            ((float *)colarr)[cind]=MKC_SB(ci[CCOL_SUM], ci[CCOL_NUM]);
+            break;
+
           case UI_KEY_AREAXY:
             ((int32_t *)colarr)[cind]=ci[CCOL_NUMXY];
             break;
@@ -2183,30 +2764,45 @@ columns_fill(struct mkcatalog_passparams *pp)
             ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_GZ],
                                                  ci[CCOL_NUMALL] );
 
-          case UI_KEY_MINX:
-            ((uint32_t *)colarr)[cind] = ci[CCOL_MINX];
+          case UI_KEY_MINVX:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_MINVX], 
ci[CCOL_MINVNUM] );
             break;
 
-          case UI_KEY_MAXX:
-            ((uint32_t *)colarr)[cind] = ci[CCOL_MAXX];
+          case UI_KEY_MAXVX:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_MAXVX], 
ci[CCOL_MAXVNUM] );
             break;
 
-          case UI_KEY_MINY:
-            ((uint32_t *)colarr)[cind] = ci[CCOL_MINY];
+          case UI_KEY_MINVY:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_MINVY], 
ci[CCOL_MINVNUM] );
             break;
 
-          case UI_KEY_MAXY:
-            ((uint32_t *)colarr)[cind] = ci[CCOL_MAXY];
+          case UI_KEY_MAXVY:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_MAXVY], 
ci[CCOL_MAXVNUM] );
             break;
 
-          case UI_KEY_MINZ:
-            ((uint32_t *)colarr)[cind] = ci[CCOL_MINZ];
+          case UI_KEY_MINVZ:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_MINVZ], 
ci[CCOL_MINVNUM] );
             break;
 
-          case UI_KEY_MAXZ:
-            ((uint32_t *)colarr)[cind] = ci[CCOL_MAXZ];
+          case UI_KEY_MAXVZ:
+            ((float *)colarr)[cind] = MKC_RATIO( ci[CCOL_MAXVZ], 
ci[CCOL_MAXVNUM] );
             break;
 
+          case UI_KEY_MINVNUM:
+            ((uint32_t *)colarr)[cind] = ci[CCOL_MINVNUM];
+            break;
+
+          case UI_KEY_MAXVNUM:
+            ((uint32_t *)colarr)[cind] = ci[CCOL_MAXVNUM];
+            break;
+
+          case UI_KEY_MINX:  ((uint32_t *)colarr)[cind] = ci[CCOL_MINX];  
break;
+          case UI_KEY_MAXX:  ((uint32_t *)colarr)[cind] = ci[CCOL_MAXX];  
break;
+          case UI_KEY_MINY:  ((uint32_t *)colarr)[cind] = ci[CCOL_MINY];  
break;
+          case UI_KEY_MAXY:  ((uint32_t *)colarr)[cind] = ci[CCOL_MAXY];  
break;
+          case UI_KEY_MINZ:  ((uint32_t *)colarr)[cind] = ci[CCOL_MINZ];  
break;
+          case UI_KEY_MAXZ:  ((uint32_t *)colarr)[cind] = ci[CCOL_MAXZ];  
break;
+
           case UI_KEY_W1:
           case UI_KEY_W2:
           case UI_KEY_W3:
@@ -2253,6 +2849,26 @@ columns_fill(struct mkcatalog_passparams *pp)
                                         ? ci[ CCOL_MEDIAN ] : NAN );
             break;
 
+          case UI_KEY_MAXIMUM:
+            ((float *)colarr)[cind] = ci[ CCOL_MAXIMUM ];
+            break;
+
+          case UI_KEY_SIGCLIPNUMBER:
+            ((int32_t *)colarr)[cind] = ci[ CCOL_SIGCLIPNUM ];
+            break;
+
+          case UI_KEY_SIGCLIPMEDIAN:
+            ((float *)colarr)[cind] = ci[ CCOL_SIGCLIPMEDIAN ];
+            break;
+
+          case UI_KEY_SIGCLIPMEAN:
+            ((float *)colarr)[cind] = ci[ CCOL_SIGCLIPMEAN ];
+            break;
+
+          case UI_KEY_SIGCLIPSTD:
+            ((float *)colarr)[cind] = ci[ CCOL_SIGCLIPSTD ];
+            break;
+
           case UI_KEY_MAGNITUDE: /* Similar: brightness for clumps */
             tmp = ( ci[ CCOL_RIV_NUM ]
                     ? ci[ CCOL_RIV_SUM ]/ci[ CCOL_RIV_NUM ]*ci[ CCOL_NUM ]
@@ -2305,12 +2921,12 @@ columns_fill(struct mkcatalog_passparams *pp)
 
           case UI_KEY_SKY:
             ((float *)colarr)[cind] = MKC_RATIO( ci[ CCOL_SUMSKY],
-                                                 ci[ CCOL_NUM] );
+                                                 ci[ CCOL_NUMSKY] );
             break;
 
           case UI_KEY_STD:
             ((float *)colarr)[cind] = sqrt( MKC_RATIO( ci[ CCOL_SUMVAR ],
-                                                       ci[ CCOL_NUM    ] ));
+                                                       ci[ CCOL_NUMVAR ] ));
             break;
 
           case UI_KEY_SEMIMAJOR:
@@ -2349,6 +2965,66 @@ columns_fill(struct mkcatalog_passparams *pp)
             ((float *)colarr)[cind] = columns_second_order(pp, ci, key, 1);
             break;
 
+          case UI_KEY_HALFSUMAREA:
+            ((int32_t *)colarr)[cind] = ci[CCOL_HALFSUMNUM];
+            break;
+
+          case UI_KEY_HALFMAXAREA:
+            ((int32_t *)colarr)[cind] = ci[CCOL_HALFMAXNUM];
+            break;
+
+          case UI_KEY_HALFMAXSUM:
+            ((float *)colarr)[cind] = ci[CCOL_HALFMAXSUM];
+            break;
+
+          case UI_KEY_HALFMAXSB:
+            ((float *)colarr)[cind] = MKC_SB( ci[CCOL_HALFMAXSUM],
+                                              ci[CCOL_HALFMAXNUM] );
+            break;
+
+          case UI_KEY_FRACMAXSUM1:
+            ((int32_t *)colarr)[cind] = ci[CCOL_FRACMAX1SUM];
+            break;
+
+          case UI_KEY_FRACMAXSUM2:
+            ((int32_t *)colarr)[cind] = ci[CCOL_FRACMAX2SUM];
+            break;
+
+          case UI_KEY_FRACMAXAREA1:
+            ((int32_t *)colarr)[cind] = ci[CCOL_FRACMAX1NUM];
+            break;
+
+          case UI_KEY_FRACMAXAREA2:
+            ((int32_t *)colarr)[cind] = ci[CCOL_FRACMAX2NUM];
+            break;
+
+          case UI_KEY_HALFSUMSB:
+            ((float *)colarr)[cind] = MKC_SB( ci[CCOL_SUM]/2.0f,
+                                              ci[CCOL_HALFSUMNUM] );
+            break;
+
+          case UI_KEY_FWHM:
+          case UI_KEY_HALFMAXRADIUS:
+          case UI_KEY_HALFSUMRADIUS:
+          case UI_KEY_FRACMAXRADIUS1:
+          case UI_KEY_FRACMAXRADIUS2:
+            tmp = ( columns_second_order(  pp, ci, UI_KEY_SEMIMINOR, 1)
+                    / columns_second_order(pp, ci, UI_KEY_SEMIMAJOR, 1) );
+            switch(key)
+              {
+              case UI_KEY_FWHM:           tmpind=CCOL_HALFMAXNUM;  break;
+              case UI_KEY_HALFMAXRADIUS:  tmpind=CCOL_HALFMAXNUM;  break;
+              case UI_KEY_HALFSUMRADIUS:  tmpind=CCOL_HALFSUMNUM;  break;
+              case UI_KEY_FRACMAXRADIUS1: tmpind=CCOL_FRACMAX1NUM; break;
+              case UI_KEY_FRACMAXRADIUS2: tmpind=CCOL_FRACMAX2NUM; break;
+              }
+            tmp = sqrt( ci[tmpind]/(tmp*M_PI) );
+            if(key==UI_KEY_FWHM)
+              ((float *)colarr)[cind] = tmp<1e-6 ? NAN : (tmp*2);
+            else
+              ((float *)colarr)[cind] = tmp<1e-6 ? NAN : tmp;
+            break;
+
           default:
             error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
                   "solve the problem. The output column code %d not "
diff --git a/bin/mkcatalog/columns.h b/bin/mkcatalog/columns.h
index 723cd7c..6f65d8f 100644
--- a/bin/mkcatalog/columns.h
+++ b/bin/mkcatalog/columns.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/mkcatalog/main.c b/bin/mkcatalog/main.c
index c24fb2d..c244333 100644
--- a/bin/mkcatalog/main.c
+++ b/bin/mkcatalog/main.c
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/mkcatalog/main.h b/bin/mkcatalog/main.h
index 00eba86..11d7ff4 100644
--- a/bin/mkcatalog/main.h
+++ b/bin/mkcatalog/main.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -70,21 +70,36 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    the FITS standard (fastest dimension is first). */
 enum objectcols
   {
-    OCOL_NUMALL,         /* Number of all pixels with this label.     */
-    OCOL_NUMALLXY,       /* Number of all pixels in first two dims.   */
-    OCOL_NUM,            /* Number of values used in this object.     */
+    OCOL_NUMALL,         /* Area/number of all pixels with this label.*/
+    OCOL_NUMALLXY,       /* Area/Number in first two dimensions.      */
+    OCOL_NUM,            /* Area/Number of values used in this object.*/
     OCOL_NUMXY,          /* Number of values in the first two dims.   */
     OCOL_SUM,            /* Sum of (value-sky) in object.             */
-    OCOL_SUM_VAR,        /* Varience of sum (for brightness error).   */
-    OCOL_MEDIAN,         /* Median of value in object.                */
+    OCOL_SUM_VAR,        /* Variance including values (not just sky). */
+    OCOL_MEDIAN,         /* Median of values in object.               */
+    OCOL_MAXIMUM,        /* Maximum value in object.                  */
+    OCOL_SIGCLIPNUM,     /* Sigma-clipped mean of this object.        */
+    OCOL_SIGCLIPSTD,     /* Sigma-clipped mean of this object.        */
+    OCOL_SIGCLIPMEAN,    /* Sigma-clipped mean of this object.        */
+    OCOL_SIGCLIPMEDIAN,  /* Sigma-clipped mean of this object.        */
     OCOL_VX,             /* Sum of (value-sky) * x.                   */
     OCOL_VY,             /* Sum of (value-sky) * y.                   */
     OCOL_VZ,             /* Sum of (value-sky) * z.                   */
     OCOL_VXX,            /* Sum of (value-sky) * x * x.               */
     OCOL_VYY,            /* Sum of (value-sky) * y * y.               */
     OCOL_VXY,            /* Sum of (value-sky) * x * y.               */
+    OCOL_MINVX,          /* X of minimum pixel in values file.        */
+    OCOL_MAXVX,          /* X of maximum pixel in values file.        */
+    OCOL_MINVY,          /* Y of minimum pixel in values file.        */
+    OCOL_MAXVY,          /* Y of maximum pixel in values file.        */
+    OCOL_MINVZ,          /* Z of minimum pixel in values file.        */
+    OCOL_MAXVZ,          /* Z of maximum pixel in values file.        */
+    OCOL_MINVNUM,        /* Number of pixels with minimum value.      */
+    OCOL_MAXVNUM,        /* Number of pixels with maximum value.      */
     OCOL_SUMSKY,         /* Sum of sky value on this object.          */
+    OCOL_NUMSKY,         /* Number of sky value on this object.       */
     OCOL_SUMVAR,         /* Sum of sky variance value on this object. */
+    OCOL_NUMVAR,         /* Number of sky value on this object.       */
     OCOL_SUMWHT,         /* Sum of positive image pixels.             */
     OCOL_NUMWHT,         /* Number of positive pixels used for wht.   */
     OCOL_GX,             /* Geometric center of object in X.          */
@@ -97,6 +112,13 @@ enum objectcols
     OCOL_UPPERLIMIT_S,   /* Upper limit one-sigma value.              */
     OCOL_UPPERLIMIT_Q,   /* Quantile of object in random distribution.*/
     OCOL_UPPERLIMIT_SKEW,/* (Mean-Median)/STD of random distribution. */
+    OCOL_HALFMAXNUM,     /* Area/Number of pixels above half of max.  */
+    OCOL_HALFMAXSUM,     /* Sum of pixels above half of max.          */
+    OCOL_HALFSUMNUM,     /* Area/Number containing half of total sum. */
+    OCOL_FRACMAX1NUM,    /* Area/Number containing frac of maximum.   */
+    OCOL_FRACMAX1SUM,    /* Sum containing frac of maximum.           */
+    OCOL_FRACMAX2NUM,    /* Area/Number containing frac of maximum.   */
+    OCOL_FRACMAX2SUM,    /* Sum containing frac of maximum.           */
     OCOL_C_NUMALL,       /* Value independent no. of pixels in clumps.*/
     OCOL_C_NUM,          /* Area of clumps in this object.            */
     OCOL_C_SUM,          /* Brightness in object clumps.              */
@@ -119,8 +141,13 @@ enum clumpcols
     CCOL_NUM,            /* Number of values used in clump.           */
     CCOL_NUMXY,          /* Number of values only in first two dims.  */
     CCOL_SUM,            /* River subtracted brightness.              */
-    CCOL_SUM_VAR,        /* Variance of sum (for brightness error).   */
+    CCOL_SUM_VAR,        /* Variance including values (not just sky). */
     CCOL_MEDIAN,         /* Median of values in clump.                */
+    CCOL_MAXIMUM,        /* Maximum value in clump.                   */
+    CCOL_SIGCLIPNUM,     /* Sigma-clipped mean of this clump.         */
+    CCOL_SIGCLIPSTD,     /* Sigma-clipped mean of this clump.         */
+    CCOL_SIGCLIPMEAN,    /* Sigma-clipped mean of this clump.         */
+    CCOL_SIGCLIPMEDIAN,  /* Sigma-clipped mean of this clump.         */
     CCOL_RIV_NUM,        /* Num river pixels around this clump.       */
     CCOL_RIV_SUM,        /* Sum of rivers around clump.               */
     CCOL_RIV_SUM_VAR,    /* Variance of sum (for error measurements). */
@@ -130,8 +157,18 @@ enum clumpcols
     CCOL_VXX,            /* Sum of flux*x*x of this clump.            */
     CCOL_VYY,            /* Sum of flux*y*y of this clump.            */
     CCOL_VXY,            /* Sum of flux*x*y of this clump.            */
-    CCOL_SUMSKY,         /* Sum of sky value on this object.          */
-    CCOL_SUMVAR,         /* Sum of sky variance value on this object. */
+    CCOL_MINVX,          /* X of minimum pixel in values array.       */
+    CCOL_MAXVX,          /* X of maximum pixel in values array.       */
+    CCOL_MINVY,          /* Y of minimum pixel in values array.       */
+    CCOL_MAXVY,          /* Y of maximum pixel in values array.       */
+    CCOL_MINVZ,          /* Z of minimum pixel in values array.       */
+    CCOL_MAXVZ,          /* Z of maximum pixel in values array.       */
+    CCOL_MINVNUM,        /* Number of pixels with minimum value.      */
+    CCOL_MAXVNUM,        /* Number of pixels with maximum value.      */
+    CCOL_SUMSKY,         /* Sum of sky value on this clump.           */
+    CCOL_NUMSKY,         /* Number of sky value on this clump.        */
+    CCOL_SUMVAR,         /* Sum of sky variance value on this clump.  */
+    CCOL_NUMVAR,         /* Number of sky variance value on this clump.*/
     CCOL_SUMWHT,         /* Sum of positive image pixels for wht.     */
     CCOL_NUMWHT,         /* Num of positive image pixels for wht.     */
     CCOL_GX,             /* Geometric center of clump in X.           */
@@ -150,6 +187,13 @@ enum clumpcols
     CCOL_UPPERLIMIT_S,   /* Upper limit one-sigma value.              */
     CCOL_UPPERLIMIT_Q,   /* Quantile of object in random distribution.*/
     CCOL_UPPERLIMIT_SKEW,/* (Mean-Median)/STD of random distribution. */
+    CCOL_HALFMAXNUM,     /* Area/Number of pixels above half of max.  */
+    CCOL_HALFMAXSUM,     /* Sum of pixels above half of max.          */
+    CCOL_HALFSUMNUM,     /* Area/Number containing half of total sum. */
+    CCOL_FRACMAX1NUM,    /* Area/Number containing frac of maximum.   */
+    CCOL_FRACMAX1SUM,    /* Sum containing frac of maximum.           */
+    CCOL_FRACMAX2NUM,    /* Area/Number containing frac of maximum.   */
+    CCOL_FRACMAX2SUM,    /* Sum containing frac of maximum.           */
 
     CCOL_NUMCOLS,        /* SHOULD BE LAST: total number of columns.  */
   };
@@ -180,10 +224,13 @@ struct mkcatalogparams
   uint8_t         noclumpsort;  /* Don't sort the clumps catalog.       */
   float             zeropoint;  /* Zero-point magnitude of object.      */
   uint8_t            variance;  /* Input STD file is actually variance. */
+  uint8_t        forcereadstd;  /* Read STD even if not needed.         */
   uint8_t         subtractsky;  /* ==1: subtract the Sky from values.   */
   float           sfmagnsigma;  /* Surface brightness multiple of sigma.*/
   float             sfmagarea;  /* Surface brightness area (arcsec^2).  */
   uint8_t            spectrum;  /* Object spectrum for 3D datasets.     */
+  uint8_t       inbetweenints;  /* Keep rows (integer ids) with no labels. */
+  double         sigmaclip[2];  /* Sigma clip column settings.          */
 
   char            *upmaskfile;  /* Name of upper limit mask file.       */
   char             *upmaskhdu;  /* HDU of upper limit mask file.        */
@@ -194,6 +241,8 @@ struct mkcatalogparams
   float              upnsigma;  /* Multiple of sigma to define up-lim.  */
   int32_t       checkuplim[2];  /* Object & clump ID to check dist.     */
 
+  gal_data_t         *fracmax;  /* Fractions to use in --fracsumarea.   */
+
   /* Internal. */
   char           *relabclumps;  /* Name of new file for clump labels.   */
   time_t              rawtime;  /* Starting time of the program.        */
@@ -205,6 +254,8 @@ struct mkcatalogparams
   gal_data_t          *upmask;  /* Upper limit magnitude mask.          */
   float                medstd;  /* Median standard deviation value.     */
   float               cpscorr;  /* Counts-per-second correction.        */
+  int32_t            *outlabs;  /* Labels in output catalog (when necessary) */
+  int32_t         *outlabsinv;  /* Inverse of the 'outlabs' array.      */
   size_t           numobjects;  /* Number of object labels in image.    */
   float               clumpsn;  /* Clump S/N threshold.                 */
   size_t            numclumps;  /* Number of clumps in image.           */
@@ -228,6 +279,7 @@ struct mkcatalogparams
   size_t         *numclumps_c;  /* To sort the clumps table by Obj.ID.  */
   gal_data_t   *specsliceinfo;  /* Slice information for spectra.       */
   gal_data_t         *spectra;  /* Array of datasets containing spectra.*/
+  double        pixelarcsecsq;  /* Area of input's pixels in arcsec^2.  */
 
   char        *usedvaluesfile;  /* Ptr to final name used for values.   */
   char        *usedclumpsfile;  /* Ptr to final name used for clumps.   */
diff --git a/bin/mkcatalog/mkcatalog.c b/bin/mkcatalog/mkcatalog.c
index 5d12634..e3e75b7 100644
--- a/bin/mkcatalog/mkcatalog.c
+++ b/bin/mkcatalog/mkcatalog.c
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -134,22 +134,23 @@ mkcatalog_single_object(void *in_prm)
                                   NULL, NULL, NULL);
 
       /* Set the blank checked flag to 1. By definition, this dataset won't
-         have any blank values. Also `flag' is initialized to `0'. So we
-         just have to set the checked flag (`GAL_DATA_FLAG_BLANK_CH') to
+         have any blank values. Also 'flag' is initialized to '0'. So we
+         just have to set the checked flag ('GAL_DATA_FLAG_BLANK_CH') to
          one to inform later steps that there are no blank values. */
       pp.up_vals->flag |= GAL_DATA_FLAG_BLANK_CH;
     }
   else
     pp.up_vals=NULL;
 
-
   /* Fill the desired columns for all the objects given to this thread. */
   for(i=0; tprm->indexs[i]!=GAL_BLANK_SIZE_T; ++i)
     {
       /* For easy reading. Note that the object IDs start from one while
          the array positions start from 0. */
       pp.ci       = NULL;
-      pp.object   = tprm->indexs[i] + 1;
+      pp.object   = ( p->outlabs
+                      ? p->outlabs[ tprm->indexs[i] ]
+                      : tprm->indexs[i] + 1 );
       pp.tile     = &p->tiles[   tprm->indexs[i] ];
       pp.spectrum = &p->spectra[ tprm->indexs[i] ];
 
@@ -177,9 +178,20 @@ mkcatalog_single_object(void *in_prm)
           parse_clumps(&pp);
         }
 
-      /* If the median is requested, another pass is necessary. */
-      if( p->oiflag[ OCOL_MEDIAN ] )
-        parse_median(&pp);
+      /* If an order-based calculation is requested, another pass is
+         necessary. */
+      if( p->oiflag[ OCOL_MEDIAN ]
+          || p->oiflag[ OCOL_MAXIMUM ]
+          || p->oiflag[ OCOL_HALFMAXSUM ]
+          || p->oiflag[ OCOL_HALFMAXNUM ]
+          || p->oiflag[ OCOL_HALFSUMNUM ]
+          || p->oiflag[ OCOL_SIGCLIPNUM ]
+          || p->oiflag[ OCOL_SIGCLIPSTD ]
+          || p->oiflag[ OCOL_SIGCLIPMEAN ]
+          || p->oiflag[ OCOL_FRACMAX1NUM ]
+          || p->oiflag[ OCOL_FRACMAX2NUM ]
+          || p->oiflag[ OCOL_SIGCLIPMEDIAN ])
+        parse_order_based(&pp);
 
       /* Calculate the upper limit magnitude (if necessary). */
       if(p->upperlimit) upperlimit_calculate(&pp);
@@ -268,8 +280,8 @@ mkcatalog_wcs_conversion(struct mkcatalogparams *p)
       /* Definitions */
       c=NULL;
 
-      /* Set `c' for the columns that must be corrected. Note that this
-         `switch' statement doesn't need any `default', because there are
+      /* Set 'c' for the columns that must be corrected. Note that this
+         'switch' statement doesn't need any 'default', because there are
          probably columns that don't need any correction. */
       switch(column->status)
         {
@@ -300,8 +312,8 @@ mkcatalog_wcs_conversion(struct mkcatalogparams *p)
       /* Definitions */
       c=NULL;
 
-      /* Set `c' for the columns that must be corrected. Note that this
-         `switch' statement doesn't need any `default', because there are
+      /* Set 'c' for the columns that must be corrected. Note that this
+         'switch' statement doesn't need any 'default', because there are
          probably columns that don't need any correction. */
       switch(column->status)
         {
@@ -435,7 +447,7 @@ mkcatalog_outputs_same_start(struct mkcatalogparams *p, int 
o0c1,
       gal_list_str_add(&comments, str, 0);
     }
 
-  /* Write the date. However, `ctime' is going to put a new-line character
+  /* Write the date. However, 'ctime' is going to put a new-line character
      in the end of its string, so we are going to remove it manually. */
   if( asprintf(&str, "%s started on %s", PROGRAM_NAME, ctime(&p->rawtime))<0 )
     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
@@ -447,14 +459,15 @@ mkcatalog_outputs_same_start(struct mkcatalogparams *p, 
int o0c1,
   mkcatalog_write_inputs_in_comments(p, &comments, 1, 1);
 
 
-  /* Write other supplimentary information. */
+  /* Write other supplementary information. */
   if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT)
     {
-      if( asprintf(&str, "--------- Supplimentary information ---------")<0 )
+      if( asprintf(&str, "--------- Supplementary information ---------")<0 )
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
       gal_list_str_add(&comments, str, 0);
     }
 
+  /* Pixel area. */
   if(p->objects->wcs)
     {
       pixarea=gal_wcs_pixel_area_arcsec2(p->objects->wcs);
@@ -466,6 +479,7 @@ mkcatalog_outputs_same_start(struct mkcatalogparams *p, int 
o0c1,
         }
     }
 
+  /* Zeropoint magnitude */
   if(p->hasmag)
     {
       if( asprintf(&str, "Zeropoint magnitude: %.4f", p->zeropoint)<0 )
@@ -474,49 +488,53 @@ mkcatalog_outputs_same_start(struct mkcatalogparams *p, 
int o0c1,
     }
 
   /* Print surface brightness limits. */
-  if( !isnan(p->medstd) && !isnan(p->zeropoint) &&  !isnan(p->sfmagnsigma) )
+  if( !isnan(p->medstd) && !isnan(p->sfmagnsigma) )
     {
-      /* Per pixel. */
-      if( asprintf(&str, "%g sigma surface brightness (magnitude/pixel): "
-                   "%.3f", p->sfmagnsigma, ( -2.5f
-                                             *log10( p->sfmagnsigma
-                                                     * p->medstd )
-                                             + p->zeropoint ) )<0 )
-        error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
-      gal_list_str_add(&comments, str, 0);
-
-      /* Requested projected area: if a pixel area could be measured (a WCS
-         was given), then also estimate the surface brightness over one
-         arcsecond^2. From the pixel area, we know how many pixels are
-         necessary to fill the requested projected area (in
-         arcsecond^2). We also know that as the number of samples (pixels)
-         increases (to N), the noise increases by sqrt(N), see the full
-         discussion in the book. */
-      if(!isnan(pixarea) && !isnan(p->sfmagarea))
+      /* Only print magnitudes if a zeropoint is given. */
+      if( !isnan(p->zeropoint) )
         {
-          /* Prepare the comment/information. */
-          if(p->sfmagarea==1.0f)
-            tstr=NULL;
-          else
-            if( asprintf(&tstr, "%g-", p->sfmagarea)<0 )
-              error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
-          if( asprintf(&str, "%g sigma surface brightness "
-                       "(magnitude/%sarcsec^2): %.3f", p->sfmagnsigma,
-                       tstr ? tstr : "",
-                       ( -2.5f * log10( p->sfmagnsigma
-                                        * p->medstd
-                                        * sqrt( p->sfmagarea / pixarea) )
-                         + p->zeropoint ) )<0 )
+          /* Per pixel. */
+          if( asprintf(&str, "%g sigma surface brightness (magnitude/pixel): "
+                       "%.3f", p->sfmagnsigma, ( -2.5f
+                                                 *log10( p->sfmagnsigma
+                                                         * p->medstd )
+                                                 + p->zeropoint ) )<0 )
             error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
-
-          /* Add the final string/line to the catalog comments. */
           gal_list_str_add(&comments, str, 0);
 
-          /* Clean up (if necessary). */
-          if (tstr)
+          /* Requested projected area: if a pixel area could be measured (a
+             WCS was given), then also estimate the surface brightness over
+             one arcsecond^2. From the pixel area, we know how many pixels
+             are necessary to fill the requested projected area (in
+             arcsecond^2). We also know that as the number of samples
+             (pixels) increases (to N), the noise increases by sqrt(N), see
+             the full discussion in the book. */
+          if(!isnan(pixarea) && !isnan(p->sfmagarea))
             {
-              free(tstr);
-              tstr=NULL;
+              /* Prepare the comment/information. */
+              if(p->sfmagarea==1.0f)
+                tstr=NULL;
+              else
+                if( asprintf(&tstr, "%g-", p->sfmagarea)<0 )
+                  error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
+              if( asprintf(&str, "%g sigma surface brightness "
+                           "(magnitude/%sarcsec^2): %.3f", p->sfmagnsigma,
+                           tstr ? tstr : "",
+                           ( -2.5f * log10( p->sfmagnsigma
+                                            * p->medstd
+                                            * sqrt( p->sfmagarea / pixarea) )
+                             + p->zeropoint ) )<0 )
+                error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
+
+              /* Add the final string/line to the catalog comments. */
+              gal_list_str_add(&comments, str, 0);
+
+              /* Clean up (if necessary). */
+              if (tstr)
+                {
+                  free(tstr);
+                  tstr=NULL;
+                }
             }
         }
 
@@ -527,7 +545,17 @@ mkcatalog_outputs_same_start(struct mkcatalogparams *p, 
int o0c1,
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
       gal_list_str_add(&comments, str, 0);
     }
+  else
+    {
+      gal_checkset_allocate_copy("No surface brightness calcuations "
+                                 "because no STD image used.", &str);
+      gal_list_str_add(&comments, str, 0);
+      gal_checkset_allocate_copy("Ask for column that uses the STD image, "
+                                 "or '--forcereadstd'.", &str);
+      gal_list_str_add(&comments, str, 0);
+    }
 
+  /* The count-per-second correction. */
   if(p->cpscorr>1.0f)
     {
       if( asprintf(&str, "Counts-per-second correction: %.3f", p->cpscorr)<0 )
@@ -535,11 +563,11 @@ mkcatalog_outputs_same_start(struct mkcatalogparams *p, 
int o0c1,
       gal_list_str_add(&comments, str, 0);
     }
 
+  /* Print upper-limit parameters. */
   if(p->upperlimit)
     upperlimit_write_comments(p, &comments, 1);
 
-
-
+  /* Start column metadata. */
   if(p->cp.tableformat==GAL_TABLE_FORMAT_TXT)
     {
       if( asprintf(&str, "--------- Table columns ---------")<0 )
@@ -569,7 +597,7 @@ sort_clumps_by_objid(struct mkcatalogparams *p)
   /* Make sure everything is fine. */
   if(p->hostobjid_c==NULL || p->numclumps_c==NULL)
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
-          "problem. `p->hostobjid_c' and `p->numclumps_c' must not be "
+          "problem. 'p->hostobjid_c' and 'p->numclumps_c' must not be "
           "NULL.", __func__, PACKAGE_BUGREPORT);
 
 
@@ -592,7 +620,9 @@ sort_clumps_by_objid(struct mkcatalogparams *p)
   i=0;
   while(i<p->numclumps)
     {
-      o=p->hostobjid_c[i]-1;
+      o = ( p->outlabsinv
+            ? (p->outlabsinv[ p->hostobjid_c[i] ] + 1)
+            : p->hostobjid_c[i] ) - 1;
       for(j=0; j<p->numclumps_c[o]; ++j)
         permute[i++] = rowstart[o] + j;
     }
@@ -628,7 +658,7 @@ mkcatalog_write_outputs(struct mkcatalogparams *p)
       /* Reverse the comments list (so it is printed in the same order
          here), write the objects catalog and free the comments. */
       gal_list_str_reverse(&comments);
-      gal_table_write(p->objectcols, comments, p->cp.tableformat,
+      gal_table_write(p->objectcols, NULL, comments, p->cp.tableformat,
                       p->objectsout, "OBJECTS", 0);
       gal_list_str_free(comments, 1);
 
@@ -645,7 +675,7 @@ mkcatalog_write_outputs(struct mkcatalogparams *p)
              Reverse the comments list (so it is printed in the same order
              here), write the objects catalog and free the comments. */
           gal_list_str_reverse(&comments);
-          gal_table_write(p->clumpcols, comments, p->cp.tableformat,
+          gal_table_write(p->clumpcols, NULL, comments, p->cp.tableformat,
                           p->clumpsout, "CLUMPS", 0);
           gal_list_str_free(comments, 1);
         }
@@ -660,7 +690,7 @@ mkcatalog_write_outputs(struct mkcatalogparams *p)
         printf("  - Catalog(s) complete, writing spectra.\n");
 
       /* Start counting and writing the files. Note that due to some
-         conditions (for example in debugging), a `p->spectra[i]' may not
+         conditions (for example in debugging), a 'p->spectra[i]' may not
          actually contain any data. So we'll also count the number of
          spectra that are written. */
       scounter=0;
@@ -675,7 +705,8 @@ mkcatalog_write_outputs(struct mkcatalogparams *p)
               {
                 /* Write the table. */
                 sprintf(str, "SPECTRUM_%zu", i+1);
-                gal_table_write(&p->spectra[i], NULL, GAL_TABLE_FORMAT_BFITS,
+                gal_table_write(&p->spectra[i], NULL, NULL,
+                                GAL_TABLE_FORMAT_BFITS,
                                 p->objectsout, str, 0);
               }
             else
@@ -683,7 +714,7 @@ mkcatalog_write_outputs(struct mkcatalogparams *p)
                 sprintf(str, "-spec-%zu.txt", i+1);
                 fname=gal_checkset_automatic_output(&p->cp, p->objectsout,
                                                     str);
-                gal_table_write(&p->spectra[i], NULL, GAL_TABLE_FORMAT_TXT,
+                gal_table_write(&p->spectra[i], NULL, NULL, 
GAL_TABLE_FORMAT_TXT,
                                 fname, NULL, 0);
                 free(fname);
               }
@@ -720,14 +751,14 @@ mkcatalog_write_outputs(struct mkcatalogparams *p)
           if(outisfits)
             {
               if(p->objectcols)
-                printf("  - Spectra in %zu extensions named `SPECTRUM_NN'.\n",
+                printf("  - Spectra in %zu extensions named 'SPECTRUM_NN'.\n",
                        p->numobjects);
               else
                 printf("  - Output: %s (Spectra in %zu extensions named "
-                       "`SPECTRUM_NN').\n)", p->objectsout, p->numobjects);
+                       "'SPECTRUM_NN').\n)", p->objectsout, p->numobjects);
             }
           else
-            printf("  - Spectra in %zu files with `-spec-NN.txt' suffix.\n",
+            printf("  - Spectra in %zu files with '-spec-NN.txt' suffix.\n",
                    p->numobjects);
         }
     }
@@ -764,7 +795,8 @@ mkcatalog(struct mkcatalogparams *p)
 
   /* Do the processing on each thread. */
   gal_threads_spin_off(mkcatalog_single_object, p, p->numobjects,
-                       p->cp.numthreads);
+                       p->cp.numthreads, p->cp.minmapsize,
+                       p->cp.quietmmap);
 
   /* Post-thread processing, for example to convert image coordinates to RA
      and Dec. */
diff --git a/bin/mkcatalog/mkcatalog.h b/bin/mkcatalog/mkcatalog.h
index 730274b..9cbdbd8 100644
--- a/bin/mkcatalog/mkcatalog.h
+++ b/bin/mkcatalog/mkcatalog.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/mkcatalog/parse.c b/bin/mkcatalog/parse.c
index 1229de9..375d72c 100644
--- a/bin/mkcatalog/parse.c
+++ b/bin/mkcatalog/parse.c
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -169,7 +169,7 @@ parse_spectrum_pepare(struct mkcatalog_passparams *pp, 
size_t *start_end_inc,
                          p->values->unit, "Error in SUM_OTHER column.");
 
   /* Fill up the contents of the first element (note that the first
-     `gal_data_t' is actually in an array, so the skeleton is already
+     'gal_data_t' is actually in an array, so the skeleton is already
      allocated, we just have to allocate its contents. */
   gal_data_initialize(pp->spectrum, NULL, p->specsliceinfo->type, 1,
                       &numslices, NULL, 0, p->cp.minmapsize,
@@ -252,8 +252,8 @@ parse_spectrum_end(struct mkcatalog_passparams *pp, 
gal_data_t *xybin)
       osearr[i] = sqrt( osearr[i] );
     }
 
-  /* Convert the `double' type columns to `float'. The extra precision of
-     `double' was necessary when we were summing values in each slice. But
+  /* Convert the 'double' type columns to 'float'. The extra precision of
+     'double' was necessary when we were summing values in each slice. But
      afterwards, it is not necessary at all (the measurement error is much
      larger than a double-precision floating point number (15
      decimals). But the extra space gained (double) is very useful in not
@@ -271,7 +271,7 @@ parse_spectrum_end(struct mkcatalog_passparams *pp, 
gal_data_t *xybin)
   parse_spectrum_uint32_to_best_type(&oarea);
 
   /* List the datasets and write them into the pointer for this object
-     (exact copy of the statement in `parse_spectrum_pepare'). */
+     (exact copy of the statement in 'parse_spectrum_pepare'). */
   pp->spectrum->next->next                       = area;
   area->next                                     = sum;
   area->next->next                               = esum;
@@ -325,14 +325,14 @@ parse_spectrum(struct mkcatalog_passparams *pp, 
gal_data_t *xybin)
   osarr  = area->next->next->next->next->next->next->array;
   osearr = area->next->next->next->next->next->next->next->array;
 
-  /* If tile-id isn't necessary, set `tid' to a blank value. */
+  /* If tile-id isn't necessary, set 'tid' to a blank value. */
   tid = (p->std && p->std->size>1 && st_std == NULL) ? 0 : GAL_BLANK_SIZE_T;
 
   /* Parse each contiguous patch of memory covered by this object. */
   while( start_end_inc[0] + increment <= start_end_inc[1] )
     {
       /* Set the contiguous range to parse. The pixel-to-pixel counting
-         along the fastest dimension will be done over the `O' pointer. */
+         along the fastest dimension will be done over the 'O' pointer. */
       if( p->values        ) V  = st_v   + increment;
       if( p->std && st_std ) ST = st_std + increment;
       OO = ( O = st_o + increment ) + pp->tile->dsize[ndim-1];
@@ -357,7 +357,7 @@ parse_spectrum(struct mkcatalog_passparams *pp, gal_data_t 
*xybin)
 
                   /* Get the error associated with this voxel. Note that if
                      we are given a variance dataset already, there is no
-                     need to use `st*st', we can directly use `sval'. */
+                     need to use 'st*st', we can directly use 'sval'. */
                   sval = st_std ? *ST : (p->std->size>1?std[tid]:std[0]);
                   st = p->variance ? sqrt(sval) : sval;
                   var = (p->variance ? sval : st*st) + fabs(*V);
@@ -365,8 +365,8 @@ parse_spectrum(struct mkcatalog_passparams *pp, gal_data_t 
*xybin)
               else var = NAN;
 
 
-              /* Projected spectra: see if we have a value of `2' in the
-                 `xybin' array (showing that there is atleast one non-blank
+              /* Projected spectra: see if we have a value of '2' in the
+                 'xybin' array (showing that there is atleast one non-blank
                  element there over the whole spectrum.  */
               ++nproj;
               parr [ sind ] += *V;
@@ -402,8 +402,8 @@ parse_spectrum(struct mkcatalog_passparams *pp, gal_data_t 
*xybin)
       increment += ( gal_tile_block_increment(p->objects, tsize,
                                               num_increment++, NULL) );
 
-      /* Increment the slice number, `sind', and reset the projection (2D)
-         index `pind' if we have just finished parsing a slice. */
+      /* Increment the slice number, 'sind', and reset the projection (2D)
+         index 'pind' if we have just finished parsing a slice. */
       if( (num_increment-1)%pp->tile->dsize[1]==0 )
         {
           /* If there was no measurement, set NaN for the values and their
@@ -422,7 +422,7 @@ parse_spectrum(struct mkcatalog_passparams *pp, gal_data_t 
*xybin)
   free(tsize);
 
   /* For a check.
-  gal_table_write(pp->spectrum, NULL, GAL_TABLE_FORMAT_BFITS,
+  gal_table_write(pp->spectrum, NULL, NULL, GAL_TABLE_FORMAT_BFITS,
                   "spectrum.fits", "SPECTRUM", 0);
   */
 }
@@ -441,13 +441,14 @@ parse_objects(struct mkcatalog_passparams *pp)
   double *oi=pp->oi;
   gal_data_t *xybin=NULL;
   size_t *tsize=pp->tile->dsize;
-  uint8_t *xybinarr=NULL, *u, *uf;
-  float var, sval, *V=NULL, *SK=NULL, *ST=NULL;
+  uint8_t *u, *uf, goodvalue, *xybinarr=NULL;
+  double minima_v=FLT_MAX, maxima_v=-FLT_MAX;
   size_t d, pind=0, increment=0, num_increment=1;
   int32_t *O, *OO, *C=NULL, *objarr=p->objects->array;
+  float var, sval, varval, skyval, *V=NULL, *SK=NULL, *ST=NULL;
   float *std=p->std?p->std->array:NULL, *sky=p->sky?p->sky->array:NULL;
 
-  /* If tile processing isn't necessary, set `tid' to a blank value. */
+  /* If tile processing isn't necessary, set 'tid' to a blank value. */
   size_t tid = ( ( (p->sky     && p->sky->size>1 && pp->st_sky == NULL )
                    || ( p->std && p->std->size>1 && pp->st_std == NULL ) )
                  ? 0 : GAL_BLANK_SIZE_T );
@@ -461,15 +462,23 @@ parse_objects(struct mkcatalog_passparams *pp)
   /* If any coordinate columns are requested. */
   size_t *c = (
                /* Coordinate-related columns. */
-               ( oif[    OCOL_GX   ]
-                 || oif[ OCOL_GY   ]
-                 || oif[ OCOL_GZ   ]
-                 || oif[ OCOL_VX   ]
-                 || oif[ OCOL_VY   ]
-                 || oif[ OCOL_VZ   ]
-                 || oif[ OCOL_C_GX ]
-                 || oif[ OCOL_C_GY ]
-                 || oif[ OCOL_C_GZ ]
+               ( oif[    OCOL_GX    ]
+                 || oif[ OCOL_GY    ]
+                 || oif[ OCOL_GZ    ]
+                 || oif[ OCOL_VX    ]
+                 || oif[ OCOL_VY    ]
+                 || oif[ OCOL_VZ    ]
+                 || oif[ OCOL_C_GX  ]
+                 || oif[ OCOL_C_GY  ]
+                 || oif[ OCOL_C_GZ  ]
+                 || oif[ OCOL_MINVX ]
+                 || oif[ OCOL_MAXVX ]
+                 || oif[ OCOL_MINVY ]
+                 || oif[ OCOL_MAXVY ]
+                 || oif[ OCOL_MINVZ ]
+                 || oif[ OCOL_MAXVZ ]
+                 || oif[ OCOL_MINVNUM ]
+                 || oif[ OCOL_MAXVNUM ]
                  || sc
                  /* When the sky and its STD are tiles, we'll also need
                     the coordinate to find which tile a pixel belongs
@@ -478,7 +487,6 @@ parse_objects(struct mkcatalog_passparams *pp)
                ? gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__, "c")
                : NULL );
 
-
   /* If an XY projection area is necessary, we'll need to allocate an array
      to keep the projected space. */
   if( p->spectrum
@@ -491,12 +499,11 @@ parse_objects(struct mkcatalog_passparams *pp)
       xybinarr=xybin->array;
     }
 
-
   /* Parse each contiguous patch of memory covered by this object. */
   while( pp->start_end_inc[0] + increment <= pp->start_end_inc[1] )
     {
       /* Set the contiguous range to parse. The pixel-to-pixel counting
-         along the fastest dimension will be done over the `O' pointer. */
+         along the fastest dimension will be done over the 'O' pointer. */
       if( p->clumps            ) C  = pp->st_c   + increment;
       if( p->values            ) V  = pp->st_v   + increment;
       if( p->sky && pp->st_sky ) SK = pp->st_sky + increment;
@@ -563,12 +570,16 @@ parse_objects(struct mkcatalog_passparams *pp)
 
 
               /* Value related measurements. */
+              goodvalue=0;
               if( p->values && !( p->hasblank && isnan(*V) ) )
                 {
+                  /* For the standard-deviation measurements later. */
+                  goodvalue=1;
+
                   /* General flux summations. */
                   if(xybin) xybinarr[ pind ]=2;
-                  if(oif[ OCOL_NUM    ]) oi[ OCOL_NUM     ]++;
-                  if(oif[ OCOL_SUM    ]) oi[ OCOL_SUM     ] += *V;
+                  if(oif[ OCOL_NUM ]) oi[ OCOL_NUM ]++;
+                  if(oif[ OCOL_SUM ]) oi[ OCOL_SUM ] += *V;
 
                   /* Get the necessary clump information. */
                   if(p->clumps && *C>0)
@@ -577,6 +588,50 @@ parse_objects(struct mkcatalog_passparams *pp)
                       if(oif[ OCOL_C_SUM ]) oi[ OCOL_C_SUM ] += *V;
                     }
 
+                  /* Get the extrema of the values. Note that if the minima
+                     or maxima value's coordinates are requested in any
+                     dimension, then 'OCOL_MINVNUM' or 'OCOL_MAXVNUM' will
+                     be activated). */
+                  if( oif[ OCOL_MINVNUM ] && *V<=minima_v )
+                    {
+                      /* If the value is smaller than the smallest found so
+                         far, reset the counter to one, and reset the sum
+                         of positions this one's position. */
+                      if( *V<minima_v )
+                        {
+                          minima_v = *V;
+                          oi[ OCOL_MINVNUM ]=1;
+                          if(oif[OCOL_MINVX]) oi[ OCOL_MINVX ] = c[ ndim-1 ]+1;
+                          if(oif[OCOL_MINVY]) oi[ OCOL_MINVY ] = c[ ndim-2 ]+1;
+                          if(oif[OCOL_MINVZ]) oi[ OCOL_MINVZ ] = c[ ndim-3 ]+1;
+                        }
+                      else
+                        {
+                          oi[ OCOL_MINVNUM ]++;
+                          if(oif[OCOL_MINVX]) oi[ OCOL_MINVX ] += c[ ndim-1 
]+1;
+                          if(oif[OCOL_MINVY]) oi[ OCOL_MINVY ] += c[ ndim-2 
]+1;
+                          if(oif[OCOL_MINVZ]) oi[ OCOL_MINVZ ] += c[ ndim-3 
]+1;
+                        }
+                    }
+                  if( oif[ OCOL_MAXVNUM ] && *V>=maxima_v )
+                    {
+                      if( *V>maxima_v )
+                        {
+                          maxima_v = *V;
+                          oi[ OCOL_MAXVNUM ]=1;
+                          if(oif[OCOL_MAXVX]) oi[ OCOL_MAXVX ] = c[ ndim-1 ]+1;
+                          if(oif[OCOL_MAXVY]) oi[ OCOL_MAXVY ] = c[ ndim-2 ]+1;
+                          if(oif[OCOL_MAXVZ]) oi[ OCOL_MAXVZ ] = c[ ndim-3 ]+1;
+                        }
+                      else
+                        {
+                          oi[ OCOL_MAXVNUM ]++;
+                          if(oif[OCOL_MAXVX]) oi[ OCOL_MAXVX ] += c[ ndim-1 
]+1;
+                          if(oif[OCOL_MAXVY]) oi[ OCOL_MAXVY ] += c[ ndim-2 
]+1;
+                          if(oif[OCOL_MAXVZ]) oi[ OCOL_MAXVZ ] += c[ ndim-3 
]+1;
+                        }
+                    }
+
                   /* For flux weighted centers, we can only use positive
                      values, so do those measurements here. */
                   if( *V > 0.0f )
@@ -608,13 +663,19 @@ parse_objects(struct mkcatalog_passparams *pp)
 
 
               /* Sky value based measurements. */
-              if(p->sky)
-                if(oif[ OCOL_SUMSKY ])
-                  oi[ OCOL_SUMSKY  ] += ( pp->st_sky
-                                          ? *SK             /* Full array   */
-                                          : ( p->sky->size>1
-                                              ? sky[tid]    /* Tile         */
-                                              : sky[0] ) ); /* Single value */
+              if(p->sky && oif[ OCOL_SUMSKY ])
+                {
+                  skyval = ( pp->st_sky
+                             ? (isnan(*SK)?0:*SK)               /* Full array  
*/
+                             : ( p->sky->size>1
+                                 ? (isnan(sky[tid])?0:sky[tid]) /* Tile        
*/
+                                 : sky[0] ) );                  /* Single 
value*/
+                  if(!isnan(skyval))
+                    {
+                      oi[ OCOL_NUMSKY  ]++;
+                      oi[ OCOL_SUMSKY  ] += skyval;
+                    }
+                }
 
 
               /* Sky standard deviation based measurements.*/
@@ -622,20 +683,26 @@ parse_objects(struct mkcatalog_passparams *pp)
                 {
                   sval = pp->st_std ? *ST : (p->std->size>1?std[tid]:std[0]);
                   var = p->variance ? sval : sval*sval;
-                  if(oif[ OCOL_SUMVAR ]) oi[ OCOL_SUMVAR  ] += var;
+                  if(oif[ OCOL_SUMVAR ] && (!isnan(var)))
+                    {
+                      oi[ OCOL_NUMVAR  ]++;
+                      oi[ OCOL_SUMVAR  ] += var;
+                    }
                   /* For each pixel, we have a sky contribution to the
                      counts and the signal's contribution. The standard
-                     deviation in the sky is simply `sval', but the
+                     deviation in the sky is simply 'sval', but the
                      standard deviation of the signal (independent of the
-                     sky) is `sqrt(*V)'. Therefore the total variance of
+                     sky) is 'sqrt(*V)'. Therefore the total variance of
                      this pixel is the variance of the sky added with the
                      absolute value of its sky-subtracted flux. We use the
                      absolute value, because especially as the signal gets
                      noisy there will be negative values, and we don't want
                      them to decrease the variance. */
-                  if(oif[ OCOL_SUM_VAR ])
-                    oi[ OCOL_SUM_VAR ] += ( (p->variance ? var : sval)
-                                            + fabs(*V) );
+                  if(oif[ OCOL_SUM_VAR ] && goodvalue)
+                    {
+                      varval=p->variance ? var : sval;
+                      if(!isnan(varval)) oi[ OCOL_SUM_VAR ] += varval + 
fabs(*V);
+                    }
                 }
             }
 
@@ -653,7 +720,7 @@ parse_objects(struct mkcatalog_passparams *pp)
                                               num_increment++, NULL) );
 
       /* If a 2D projection is requested, see if we should initialize (set
-         to zero) the projection-index (`pind') not. */
+         to zero) the projection-index ('pind') not. */
       if(xybin && (num_increment-1)%tsize[1]==0 )
         pind=0;
     }
@@ -694,6 +761,43 @@ parse_objects(struct mkcatalog_passparams *pp)
 
 
 
+/* To keep the main function easier to read. */
+static void *
+parse_init_extrema(uint8_t *cif, uint8_t type, size_t num, int max1min0)
+{
+  void *out;
+  double *out_d;
+  size_t i, *out_s;
+
+  /* Allocate the array. */
+  out=gal_pointer_allocate(type, num, 0, __func__, "out");
+
+  /* Initialize the array. */
+  switch(type)
+    {
+    case GAL_TYPE_FLOAT64:
+      out_d=out;
+      for(i=0;i<num;++i) out_d[i]= max1min0 ? -FLT_MAX : FLT_MAX;
+      break;
+    case GAL_TYPE_SIZE_T:
+      out_s=out;
+      for(i=0;i<num;++i) out_s[i]= max1min0 ? 0 : GAL_BLANK_SIZE_T;
+      break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+            "the problem. Type code %d isn't recognized", __func__,
+            PACKAGE_BUGREPORT, type);
+    }
+
+  /* Return the allocated array. */
+  return out;
+}
+
+
+
+
+
+
 /* Macro to help in finding the minimum and maximum coordinates. */
 #define CMIN(COL, DIM) ( ci[ CCOL_NUMALL ]==1.0f                        \
                          ? (c[ DIM ]+1)                                 \
@@ -704,9 +808,6 @@ parse_objects(struct mkcatalog_passparams *pp)
                          : ( (c[ DIM ]+1) > ci[ COL ]                   \
                              ? (c[ DIM ]+1) : ci[ COL ] ) )
 
-
-
-
 /* Parse over the clumps within an object.  */
 void
 parse_clumps(struct mkcatalog_passparams *pp)
@@ -716,16 +817,17 @@ parse_clumps(struct mkcatalog_passparams *pp)
 
   double *ci, *cir;
   gal_data_t *xybin=NULL;
-  size_t *tsize=pp->tile->dsize;
   int32_t *O, *OO, *C=NULL, nlab;
-  uint8_t *u, *uf, *cif=p->ciflag;
-  float var, sval, *V=NULL, *SK=NULL, *ST=NULL;
+  size_t cind, *tsize=pp->tile->dsize;
+  double *minima_v=NULL, *maxima_v=NULL;
+  uint8_t *u, *uf, goodvalue, *cif=p->ciflag;
   size_t nngb=gal_dimension_num_neighbors(ndim);
   size_t i, ii, d, pind=0, increment=0, num_increment=1;
+  float var, sval, varval, skyval, *V=NULL, *SK=NULL, *ST=NULL;
   int32_t *objects=p->objects->array, *clumps=p->clumps->array;
   float *std=p->std?p->std->array:NULL, *sky=p->sky?p->sky->array:NULL;
 
-  /* If tile processing isn't necessary, set `tid' to a blank value. */
+  /* If tile processing isn't necessary, set 'tid' to a blank value. */
   size_t tid = ( ( (p->sky     && p->sky->size>1 && pp->st_sky == NULL )
                    || ( p->std && p->std->size>1 && pp->st_std == NULL ) )
                  ? 0 : GAL_BLANK_SIZE_T );
@@ -737,18 +839,26 @@ parse_clumps(struct mkcatalog_passparams *pp)
                  : NULL );
 
   /* If any coordinate columns are requested. */
-  size_t *c = ( ( cif[    CCOL_GX ]
-                  || cif[ CCOL_GY ]
-                  || cif[ CCOL_GZ ]
-                  || cif[ CCOL_VX ]
-                  || cif[ CCOL_VY ]
-                  || cif[ CCOL_VZ ]
-                  || cif[ CCOL_MINX ]
-                  || cif[ CCOL_MAXX ]
-                  || cif[ CCOL_MINY ]
-                  || cif[ CCOL_MAXY ]
-                  || cif[ CCOL_MINZ ]
-                  || cif[ CCOL_MAXZ ]
+  size_t *c = ( ( cif[    CCOL_GX    ]
+                  || cif[ CCOL_GY    ]
+                  || cif[ CCOL_GZ    ]
+                  || cif[ CCOL_VX    ]
+                  || cif[ CCOL_VY    ]
+                  || cif[ CCOL_VZ    ]
+                  || cif[ CCOL_MINX  ]
+                  || cif[ CCOL_MAXX  ]
+                  || cif[ CCOL_MINY  ]
+                  || cif[ CCOL_MAXY  ]
+                  || cif[ CCOL_MINZ  ]
+                  || cif[ CCOL_MAXZ  ]
+                  || cif[ CCOL_MINVX ]
+                  || cif[ CCOL_MAXVX ]
+                  || cif[ CCOL_MINVY ]
+                  || cif[ CCOL_MAXVY ]
+                  || cif[ CCOL_MINVZ ]
+                  || cif[ CCOL_MAXVZ ]
+                  || cif[ CCOL_MINVNUM ]
+                  || cif[ CCOL_MAXVNUM ]
                   || sc
                   || tid==GAL_BLANK_SIZE_T )
                 ? gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
@@ -776,12 +886,19 @@ parse_clumps(struct mkcatalog_passparams *pp)
                             NULL, NULL, NULL);
     }
 
+  /* For the extrema columns. */
+  if( cif[    CCOL_MINVNUM ] || cif[ CCOL_MINVX ]
+      || cif[ CCOL_MINVX   ] || cif[ CCOL_MINVZ ] )
+    minima_v=parse_init_extrema(cif, GAL_TYPE_FLOAT64, pp->clumpsinobj, 0);
+  if( cif[    CCOL_MAXVNUM ] || cif[ CCOL_MAXVX ]
+      || cif[ CCOL_MAXVY   ] || cif[ CCOL_MAXVZ ] )
+    maxima_v=parse_init_extrema(cif, GAL_TYPE_FLOAT64, pp->clumpsinobj, 1);
 
   /* Parse each contiguous patch of memory covered by this object. */
   while( pp->start_end_inc[0] + increment <= pp->start_end_inc[1] )
     {
       /* Set the contiguous range to parse. The pixel-to-pixel counting
-         along the fastest dimension will be done over the `O' pointer. */
+         along the fastest dimension will be done over the 'O' pointer. */
       C = pp->st_c + increment;
       if( p->values            ) V  = pp->st_v   + increment;
       if( p->sky && pp->st_sky ) SK = pp->st_sky + increment;
@@ -800,7 +917,8 @@ parse_clumps(struct mkcatalog_passparams *pp)
                 {
                   /* Pointer to make things easier. Note that the clump
                      labels start from 1, but the array indexs from 0.*/
-                  ci=&pp->ci[ (*C-1) * CCOL_NUMCOLS ];
+                  cind = *C-1;
+                  ci=&pp->ci[ cind * CCOL_NUMCOLS ];
 
                   /* Add to the area of this object. */
                   if( cif[ CCOL_NUMALL ]
@@ -809,7 +927,7 @@ parse_clumps(struct mkcatalog_passparams *pp)
                       || cif[ CCOL_MINZ ] || cif[ CCOL_MAXZ ] )
                     ci[ CCOL_NUMALL ]++;
                   if(cif[ CCOL_NUMALLXY ])
-                    ((uint8_t *)(xybin[*C-1].array))[ pind ] = 1;
+                    ((uint8_t *)(xybin[cind].array))[ pind ] = 1;
 
                   /* Raw-position related measurements. */
                   if(c)
@@ -853,15 +971,59 @@ parse_clumps(struct mkcatalog_passparams *pp)
                         }
                     }
 
-                  /* Value related measurements, see `parse_objects' for
+                  /* Value related measurements, see 'parse_objects' for
                      comments. */
+                  goodvalue=0;
                   if( p->values && !( p->hasblank && isnan(*V) ) )
                     {
+                      /* For the standard-deviation measurement. */
+                      goodvalue=1;
+
                       /* Fill in the necessary information. */
                       if(cif[ CCOL_NUM   ]) ci[ CCOL_NUM ]++;
                       if(cif[ CCOL_SUM   ]) ci[ CCOL_SUM ] += *V;
                       if(cif[ CCOL_NUMXY ])
-                        ((uint8_t *)(xybin[*C-1].array))[ pind ] = 2;
+                        ((uint8_t *)(xybin[cind].array))[ pind ] = 2;
+
+                      /* Minimum/maximum pixel positions. */
+                      if( cif[ CCOL_MINVNUM ] && *V<=minima_v[cind] )
+                        {
+                          if( *V<minima_v[cind] )
+                            {
+                              minima_v[cind] = *V;
+                              ci[ CCOL_MINVNUM ]=1;
+                              if(cif[CCOL_MINVX]) ci[ CCOL_MINVX ] = c[ ndim-1 
]+1;
+                              if(cif[CCOL_MINVY]) ci[ CCOL_MINVY ] = c[ ndim-2 
]+1;
+                              if(cif[CCOL_MINVZ]) ci[ CCOL_MINVZ ] = c[ ndim-3 
]+1;
+                            }
+                          else
+                            {
+                              ci[ CCOL_MINVNUM ]++;
+                              if(cif[CCOL_MINVX]) ci[ CCOL_MINVX ] += c[ 
ndim-1 ]+1;
+                              if(cif[CCOL_MINVY]) ci[ CCOL_MINVY ] += c[ 
ndim-2 ]+1;
+                              if(cif[CCOL_MINVZ]) ci[ CCOL_MINVZ ] += c[ 
ndim-3 ]+1;
+                            }
+                        }
+                      if( cif[ CCOL_MAXVNUM ] && *V>=maxima_v[cind] )
+                        {
+                          if( *V>maxima_v[cind] )
+                            {
+                              maxima_v[cind] = *V;
+                              ci[ CCOL_MAXVNUM ]=1;
+                              if(cif[CCOL_MAXVX]) ci[ CCOL_MAXVX ] = c[ ndim-1 
]+1;
+                              if(cif[CCOL_MAXVY]) ci[ CCOL_MAXVY ] = c[ ndim-2 
]+1;
+                              if(cif[CCOL_MAXVZ]) ci[ CCOL_MAXVZ ] = c[ ndim-3 
]+1;
+                            }
+                          else
+                            {
+                              ci[ CCOL_MAXVNUM ]++;
+                              if(cif[CCOL_MAXVX]) ci[ CCOL_MAXVX ] += c[ 
ndim-1 ]+1;
+                              if(cif[CCOL_MAXVY]) ci[ CCOL_MAXVY ] += c[ 
ndim-2 ]+1;
+                              if(cif[CCOL_MAXVZ]) ci[ CCOL_MAXVZ ] += c[ 
ndim-3 ]+1;
+                            }
+                        }
+
+                      /* Columns that need positive values. */
                       if( *V > 0.0f )
                         {
                           if(cif[ CCOL_NUMWHT ]) ci[ CCOL_NUMWHT ]++;
@@ -882,26 +1044,39 @@ parse_clumps(struct mkcatalog_passparams *pp)
                     }
 
                   /* Sky based measurements. */
-                  if(p->sky)
-                    if(cif[ CCOL_SUMSKY ])
-                      ci[ CCOL_SUMSKY  ] += ( pp->st_sky
-                                              ? *SK             /* Full */
-                                              : ( p->sky->size>1
-                                                  ? sky[tid]    /* Tile */
-                                                  : sky[0] ) ); /* 1 value */
+                  if(p->sky && cif[ CCOL_SUMSKY ])
+                    {
+                      skyval = ( pp->st_sky
+                                 ? *SK             /* Full. */
+                                 : ( p->sky->size>1
+                                     ? sky[tid]    /* Tile. */
+                                     : sky[0] ) ); /* 1 value. */
+                      if(!isnan(skyval))
+                        {
+                          ci[ CCOL_NUMSKY  ]++;
+                          ci[ CCOL_SUMSKY  ] += skyval;
+                        }
+                    }
 
                   /* Sky Standard deviation based measurements, see
-                     `parse_objects' for comments. */
+                     'parse_objects' for comments. */
                   if(p->std)
                     {
                       sval = ( pp->st_std
                                ? *ST
                                : (p->std->size>1 ? std[tid] : std[0]) );
                       var = p->variance ? sval : sval*sval;
-                      if(cif[ CCOL_SUMVAR  ]) ci[ CCOL_SUMVAR ] += var;
-                      if(cif[ CCOL_SUM_VAR ])
-                        ci[ CCOL_SUM_VAR ] += ( (p->variance ? var : sval)
-                                                + fabs(*V) );
+                      if(cif[ CCOL_SUMVAR  ] && (!isnan(var)))
+                        {
+                          ci[ CCOL_NUMVAR ]++;
+                          ci[ CCOL_SUMVAR ] += var;
+                        }
+                      if(cif[ CCOL_SUM_VAR ] && goodvalue)
+                        {
+                          varval=p->variance ? var : sval;
+                          if(!isnan(varval))
+                            ci[ CCOL_SUM_VAR ] += varval + fabs(*V);
+                        }
                     }
                 }
 
@@ -914,10 +1089,10 @@ parse_clumps(struct mkcatalog_passparams *pp)
                   /* We are on a diffuse (possibly a river) pixel. So the
                      value of this pixel has to be added to any of the
                      clumps in touches. But since it might touch a labeled
-                     region more than once, we use `ngblabs' to keep track
+                     region more than once, we use 'ngblabs' to keep track
                      of which label we have already added its value
-                     to. `ii' is the number of different labels this river
-                     pixel has already been considered for. `ngblabs' will
+                     to. 'ii' is the number of different labels this river
+                     pixel has already been considered for. 'ngblabs' will
                      keep the list labels. */
                   ii=0;
                   memset(ngblabs, 0, nngb*sizeof *ngblabs);
@@ -986,42 +1161,169 @@ parse_clumps(struct mkcatalog_passparams *pp)
                                               num_increment++, NULL) );
 
       /* If a 2D projection is requested, see if we should initialize (set
-         to zero) the projection-index (`pind') not. */
+         to zero) the projection-index ('pind') not. */
       if(xybin && (num_increment-1) % tsize[1]==0 )
         pind=0;
     }
 
 
-  /* Write the projected area columns. */
-  if(xybin)
-    for(i=0;i<pp->clumpsinobj;++i)
-      {
-        /* Pointer to make things easier. */
-        ci=&pp->ci[ i * CCOL_NUMCOLS ];
-
-        /* Any non-zero pixel must be set for NUMALLXY. */
-        uf=(u=xybin[i].array)+xybin[i].size;
-        do
-          if(*u)
-            {
-              if(cif[ CCOL_NUMALLXY ]          ) ci[ CCOL_NUMALLXY ]++;
-              if(cif[ CCOL_NUMXY    ] && *u==2 ) ci[ CCOL_NUMXY    ]++;
-            }
-        while(++u<uf);
+  /* Write the higher-level columns. */
+  for(i=0;i<pp->clumpsinobj;++i)
+    {
+      /* Pointer to make things easier. */
+      ci=&pp->ci[ i * CCOL_NUMCOLS ];
 
-        /* For a check on the projected 2D areas. */
-        if(xybin && pp->object==2)
-          gal_fits_img_write(&xybin[i], "xybin.fits", NULL, NULL);
+      /* Write the XY projection columns. */
+      if(xybin)
+        {
+          /* Any non-zero pixel must be set for NUMALLXY. */
+          uf=(u=xybin[i].array)+xybin[i].size;
+          do
+            if(*u)
+              {
+                if(cif[ CCOL_NUMALLXY ]          ) ci[ CCOL_NUMALLXY ]++;
+                if(cif[ CCOL_NUMXY    ] && *u==2 ) ci[ CCOL_NUMXY    ]++;
+              }
+          while(++u<uf);
+
+          /* For a check on the projected 2D areas. */
+          if(xybin && pp->object==2)
+            gal_fits_img_write(&xybin[i], "xybin.fits", NULL, NULL);
 
-      }
+        }
+    }
 
 
   /* Clean up. */
-  if(c)       free(c);
-  if(sc)      free(sc);
-  if(dinc)    free(dinc);
+  if(c) free(c);
+  if(sc) free(sc);
+  if(dinc) free(dinc);
   if(ngblabs) free(ngblabs);
-  if(xybin)   gal_data_array_free(xybin, pp->clumpsinobj, 1);
+  if(minima_v) free(minima_v);
+  if(maxima_v) free(maxima_v);
+  if(xybin) gal_data_array_free(xybin, pp->clumpsinobj, 1);
+}
+
+
+
+
+
+static size_t
+parse_frac_find(gal_data_t *sorted_d, double value, double frac, int dosum)
+{
+  size_t i;
+  double check=0.0f;
+  double *sorted=sorted_d->array;
+
+  /* Parse over the sorted array and find the index. */
+  for(i=0;i<sorted_d->size;++i)
+    if(dosum)
+      { if( (check+=sorted[i]) > value*frac ) break; }
+    else
+      { if(         sorted[i]  < value*frac ) break; }
+
+  /* Return the final value. Note that if the index is zero, we should
+     actually return 1, because we are starting with the maximum. */
+  return i==0 ? 1 : i;
+}
+
+
+
+
+
+static double
+parse_frac_sum(gal_data_t *sorted_d, double value, double frac, int dosum)
+{
+  double sum=0.0f, *sorted=sorted_d->array;
+  size_t i, ind=parse_frac_find(sorted_d, value, frac, 0);
+
+  for(i=0;i<ind;++i) sum+=sorted[i];
+  return sum;
+}
+
+
+
+
+
+static void
+parse_area_of_frac_sum(struct mkcatalog_passparams *pp, gal_data_t *values,
+                       double *outarr, int o1c0)
+{
+  struct mkcatalogparams *p=pp->p;
+
+  double max, *sorted;
+  gal_data_t *sorted_d;
+  uint8_t *flag = o1c0 ? p->oiflag : p->ciflag;
+  double *fracmax = p->fracmax ? p->fracmax->array : NULL;
+  double sumlab = o1c0 ? outarr[OCOL_SUM] : outarr[CCOL_SUM];
+
+  /* Allocate the array to use. */
+  sorted_d = ( values->type==GAL_TYPE_FLOAT64
+                ? values
+                : gal_data_copy_to_new_type(values, GAL_TYPE_FLOAT64) );
+
+  /* Sort the desired labels and find the number of elements where we reach
+     half the total sum. */
+  gal_statistics_sort_decreasing(sorted_d);
+
+  /* Set the required fractions. */
+  if(flag[ o1c0 ? OCOL_HALFSUMNUM : CCOL_HALFSUMNUM ])
+    outarr[ o1c0 ? OCOL_HALFSUMNUM : CCOL_HALFSUMNUM ]
+      = parse_frac_find(sorted_d, sumlab, 0.5f, 1);
+
+  /* Values related to the maximum. */
+  if( flag[    o1c0 ? OCOL_MAXIMUM     : CCOL_MAXIMUM     ]
+      || flag[ o1c0 ? OCOL_HALFMAXNUM  : CCOL_HALFMAXNUM  ]
+      || flag[ o1c0 ? OCOL_HALFMAXSUM  : CCOL_HALFMAXSUM  ]
+      || flag[ o1c0 ? OCOL_FRACMAX1NUM : CCOL_FRACMAX1NUM ]
+      || flag[ o1c0 ? OCOL_FRACMAX1SUM : CCOL_FRACMAX1SUM ]
+      || flag[ o1c0 ? OCOL_FRACMAX2NUM : CCOL_FRACMAX2NUM ]
+      || flag[ o1c0 ? OCOL_FRACMAX2SUM : CCOL_FRACMAX2SUM ] )
+    {
+      /* Set the array and maximum value. We'll use the median of the top
+         three pixels for the maximum (to avoid noise) */
+      sorted=sorted_d->array;
+      max = ( sorted_d->size>3
+              ? (sorted[0]+sorted[1]+sorted[2])/3
+              : sorted[0] );
+
+      /* If we want the maximum value, then write it in. */
+      if(flag[ o1c0 ? OCOL_MAXIMUM : CCOL_MAXIMUM ])
+        outarr[ o1c0 ? OCOL_MAXIMUM : CCOL_MAXIMUM ] = max;
+
+      /* The number of pixels within half the maximum. */
+      if(flag[ o1c0 ? OCOL_HALFMAXNUM : CCOL_HALFMAXNUM ])
+        outarr[ o1c0 ? OCOL_HALFMAXNUM : CCOL_HALFMAXNUM ]
+          = parse_frac_find(sorted_d, max, 0.5f, 0);
+
+      /* The number of pixels within the first requested fraction of maximum */
+      if(flag[ o1c0 ? OCOL_FRACMAX1NUM : CCOL_FRACMAX1NUM ])
+        outarr[ o1c0 ? OCOL_FRACMAX1NUM : CCOL_FRACMAX1NUM ]
+          = parse_frac_find(sorted_d, max, fracmax[0], 0);
+
+      /* The number of pixels within the first requested fraction of maximum */
+      if(flag[ o1c0 ? OCOL_FRACMAX2NUM : CCOL_FRACMAX2NUM ])
+        outarr[ o1c0 ? OCOL_FRACMAX2NUM : CCOL_FRACMAX2NUM ]
+          = parse_frac_find(sorted_d, max, fracmax[1], 0);
+
+      /* The sum of the pixels within the given fraction of the maximum. */
+      if( flag[ o1c0 ? OCOL_HALFMAXSUM : CCOL_HALFMAXSUM ] )
+        outarr[ o1c0 ? OCOL_HALFMAXSUM : CCOL_HALFMAXSUM ]
+          = parse_frac_sum(sorted_d, max, 0.5f, 0);
+
+      /* Sum of the pixels within the 1st given fraction of the maximum. */
+      if( flag[ o1c0 ? OCOL_FRACMAX1SUM : CCOL_FRACMAX1SUM ] )
+        outarr[ o1c0 ? OCOL_FRACMAX1SUM : CCOL_FRACMAX1SUM ]
+          = parse_frac_sum(sorted_d, max, fracmax[0], 0);
+
+      /* Sum of the pixels within the 1st given fraction of the maximum. */
+      if( flag[ o1c0 ? OCOL_FRACMAX2SUM : CCOL_FRACMAX2SUM ] )
+        outarr[ o1c0 ? OCOL_FRACMAX2SUM : CCOL_FRACMAX2SUM ]
+          = parse_frac_sum(sorted_d, max, fracmax[1], 0);
+    }
+
+  /* Clean up and return. */
+  if(sorted_d!=values) gal_data_free(sorted_d);
 }
 
 
@@ -1029,41 +1331,84 @@ parse_clumps(struct mkcatalog_passparams *pp)
 
 
 void
-parse_median(struct mkcatalog_passparams *pp)
+parse_order_based(struct mkcatalog_passparams *pp)
 {
   struct mkcatalogparams *p=pp->p;
 
   float *V;
   double *ci;
-  gal_data_t *median;
+  float *sigcliparr;
+  gal_data_t *result;
   int32_t *O, *OO, *C=NULL;
-  gal_data_t **clumpsmed=NULL;
   size_t i, increment=0, num_increment=1;
+  gal_data_t *objvals=NULL, **clumpsvals=NULL;
   size_t *tsize=pp->tile->dsize, ndim=p->objects->ndim;
   size_t counter=0, *ccounter=NULL, tmpsize=pp->oi[OCOL_NUM];
-  gal_data_t *objmed=gal_data_alloc(NULL, p->values->type, 1, &tmpsize, NULL,
-                                    0, p->cp.minmapsize, p->cp.quietmmap,
-                                    NULL, NULL, NULL);
 
-  /* Allocate space for the clump medians. */
+  /* It may happen that there are no usable pixels for this object (and
+     thus its possible clumps). In this case `tmpsize' will be zero and we
+     can just write NaN values for the necessary columns. */
+  if(tmpsize==0)
+    {
+      if(p->oiflag[ OCOL_MEDIAN        ]) pp->oi[ OCOL_MEDIAN       ] = NAN;
+      if(p->oiflag[ OCOL_MAXIMUM       ]) pp->oi[ OCOL_MAXIMUM      ] = NAN;
+      if(p->oiflag[ OCOL_HALFMAXSUM    ]) pp->oi[ OCOL_HALFMAXSUM   ] = NAN;
+      if(p->oiflag[ OCOL_HALFMAXNUM    ]) pp->oi[ OCOL_HALFMAXNUM   ] = 0;
+      if(p->oiflag[ OCOL_HALFSUMNUM    ]) pp->oi[ OCOL_HALFSUMNUM   ] = 0;
+      if(p->oiflag[ OCOL_FRACMAX1NUM   ]) pp->oi[ OCOL_FRACMAX1NUM  ] = 0;
+      if(p->oiflag[ OCOL_FRACMAX2NUM   ]) pp->oi[ OCOL_FRACMAX2NUM  ] = 0;
+      if(p->oiflag[ OCOL_SIGCLIPNUM    ]) pp->oi[ OCOL_SIGCLIPNUM   ] = 0;
+      if(p->oiflag[ OCOL_SIGCLIPSTD    ]) pp->oi[ OCOL_SIGCLIPSTD   ] = 0;
+      if(p->oiflag[ OCOL_SIGCLIPMEAN   ]) pp->oi[ OCOL_SIGCLIPMEAN  ] = NAN;
+      if(p->oiflag[ OCOL_SIGCLIPMEDIAN ]) pp->oi[ OCOL_SIGCLIPMEDIAN] = NAN;
+      if(p->clumps)
+        for(i=0;i<pp->clumpsinobj;++i)
+          {
+            ci=&pp->ci[ i * CCOL_NUMCOLS ];
+            if(p->ciflag[ CCOL_MEDIAN        ]) ci[ CCOL_MEDIAN      ] = NAN;
+            if(p->ciflag[ CCOL_MAXIMUM       ]) ci[ CCOL_MAXIMUM     ] = NAN;
+            if(p->ciflag[ CCOL_HALFMAXSUM    ]) ci[ CCOL_HALFMAXSUM  ] = NAN;
+            if(p->ciflag[ CCOL_HALFMAXNUM    ]) ci[ CCOL_HALFMAXNUM  ] = 0;
+            if(p->ciflag[ CCOL_HALFSUMNUM    ]) ci[ CCOL_HALFSUMNUM  ] = 0;
+            if(p->ciflag[ CCOL_FRACMAX1NUM   ]) ci[ CCOL_FRACMAX1NUM ] = 0;
+            if(p->ciflag[ CCOL_FRACMAX2NUM   ]) ci[ CCOL_FRACMAX2NUM ] = 0;
+            if(p->ciflag[ CCOL_SIGCLIPNUM    ]) ci[ CCOL_SIGCLIPNUM  ] = 0;
+            if(p->ciflag[ CCOL_SIGCLIPSTD    ]) ci[ CCOL_SIGCLIPSTD  ] = 0;
+            if(p->ciflag[ CCOL_SIGCLIPMEAN   ]) ci[ CCOL_SIGCLIPMEAN ] = NAN;
+            if(p->ciflag[ CCOL_SIGCLIPMEDIAN ]) ci[ CCOL_SIGCLIPMEDIAN] = NAN;
+          }
+      return;
+    }
+
+  /* We know we have pixels to use, so allocate space for the values within
+     the object. */
+  objvals=gal_data_alloc(NULL, p->values->type, 1, &tmpsize, NULL, 0,
+                         p->cp.minmapsize, p->cp.quietmmap, NULL, NULL,
+                         NULL);
+
+  /* Clump preparations. */
   if(p->clumps)
     {
+      /* Allocate the necessary space. */
       errno=0;
-      clumpsmed=malloc(pp->clumpsinobj * sizeof *clumpsmed);
-      if(clumpsmed==NULL)
-        error(EXIT_FAILURE, errno, "%s: couldn't allocate `clumpsmed' for "
+      clumpsvals=malloc(pp->clumpsinobj * sizeof *clumpsvals);
+      if(clumpsvals==NULL)
+        error(EXIT_FAILURE, errno, "%s: couldn't allocate 'clumpsvals' for "
               "%zu clumps", __func__, pp->clumpsinobj);
 
-
       /* Allocate the array necessary to keep the values of each clump. */
       ccounter=gal_pointer_allocate(GAL_TYPE_SIZE_T, pp->clumpsinobj, 1,
                                     __func__, "ccounter");
       for(i=0;i<pp->clumpsinobj;++i)
         {
           tmpsize=pp->ci[ i * CCOL_NUMCOLS + CCOL_NUM ];
-          clumpsmed[i]=gal_data_alloc(NULL, p->values->type, 1, &tmpsize,
-                                      NULL, 0, p->cp.minmapsize,
-                                      p->cp.quietmmap, NULL, NULL, NULL);
+          clumpsvals[i] = ( tmpsize
+                            ? gal_data_alloc(NULL, p->values->type, 1,
+                                             &tmpsize, NULL, 0,
+                                             p->cp.minmapsize,
+                                             p->cp.quietmmap,
+                                             NULL, NULL, NULL)
+                            : NULL );
         }
     }
 
@@ -1072,7 +1417,7 @@ parse_median(struct mkcatalog_passparams *pp)
   while( pp->start_end_inc[0] + increment <= pp->start_end_inc[1] )
     {
       /* Set the contiguous range to parse. The pixel-to-pixel counting
-         along the fastest dimension will be done over the `O' pointer. */
+         along the fastest dimension will be done over the 'O' pointer. */
       V = pp->st_v + increment;
       if(p->clumps) C = pp->st_c + increment;
       OO = ( O = pp->st_o + increment ) + tsize[ndim-1];
@@ -1081,20 +1426,20 @@ parse_median(struct mkcatalog_passparams *pp)
       do
         {
           /* If this pixel belongs to the requested object, then do the
-             processing. `hasblank' is constant, so when the values doesn't
-             have any blank values, the `isnan' will never be checked. */
+             processing. 'hasblank' is constant, so when the values doesn't
+             have any blank values, the 'isnan' will never be checked. */
           if( *O==pp->object && !( p->hasblank && isnan(*V) ) )
             {
               /* Copy the value for the whole object. */
-              memcpy( gal_pointer_increment(objmed->array, counter++,
+              memcpy( gal_pointer_increment(objvals->array, counter++,
                                              p->values->type), V,
                       gal_type_sizeof(p->values->type) );
 
               /* We are also on a clump. */
-              if(p->clumps && *C>0)
-                memcpy( gal_pointer_increment(clumpsmed[*C-1]->array,
-                                               ccounter[*C-1]++,
-                                               p->values->type), V,
+              if(p->clumps && *C>0 && clumpsvals[*C-1]!=NULL)
+                memcpy( gal_pointer_increment(clumpsvals[*C-1]->array,
+                                              ccounter[*C-1]++,
+                                              p->values->type), V,
                         gal_type_sizeof(p->values->type) );
             }
 
@@ -1110,28 +1455,134 @@ parse_median(struct mkcatalog_passparams *pp)
     }
 
 
-  /* Calculate the final medians for objects. */
-  median=gal_data_copy_to_new_type_free(gal_statistics_median(objmed, 1),
-                                        GAL_TYPE_FLOAT64);
-  pp->oi[OCOL_MEDIAN]=*((double *)(median->array));
-  gal_data_free(objmed);
-  gal_data_free(median);
+  /* Calculate the necessary values for the objects. */
+  if(p->oiflag[ OCOL_MEDIAN ])
+    {
+      result=gal_data_copy_to_new_type_free(gal_statistics_median(objvals, 1),
+                                            GAL_TYPE_FLOAT64);
+      pp->oi[OCOL_MEDIAN]=*((double *)(result->array));
+      gal_data_free(result);
+    }
+  if(p->oiflag[ OCOL_SIGCLIPNUM ]
+     || p->oiflag[ OCOL_SIGCLIPSTD ]
+     || p->oiflag[ OCOL_SIGCLIPMEAN ]
+     || p->oiflag[ OCOL_SIGCLIPMEDIAN ])
+    {
+      /* Calculate the sigma-clipped results and write them in any
+         requested column. */
+      result=gal_statistics_sigma_clip(objvals, p->sigmaclip[0],
+                                       p->sigmaclip[1], 1, 1);
+      sigcliparr=result->array;
+      if(p->oiflag[ OCOL_SIGCLIPNUM ])
+        pp->oi[OCOL_SIGCLIPNUM]=sigcliparr[0];
+      if(p->oiflag[ OCOL_SIGCLIPSTD ])
+        pp->oi[OCOL_SIGCLIPSTD]=sigcliparr[3];
+      if(p->oiflag[ OCOL_SIGCLIPMEAN ])
+        pp->oi[OCOL_SIGCLIPMEAN]=sigcliparr[2];
+      if(p->oiflag[ OCOL_SIGCLIPMEDIAN ])
+        pp->oi[OCOL_SIGCLIPMEDIAN]=sigcliparr[1];
+
+      /* Clean up the sigma-clipped values. */
+      gal_data_free(result);
+    }
 
+  /* Fractional values. */
+  if( p->oiflag[    OCOL_MAXIMUM     ]
+      || p->oiflag[ OCOL_HALFMAXNUM  ]
+      || p->oiflag[ OCOL_HALFMAXSUM  ]
+      || p->oiflag[ OCOL_HALFSUMNUM  ]
+      || p->oiflag[ OCOL_FRACMAX1NUM ]
+      || p->oiflag[ OCOL_FRACMAX2NUM ] )
+    parse_area_of_frac_sum(pp, objvals, pp->oi, 1);
 
-  /* Calculate the median for clumps. */
+  /* Clean up the object values. */
+  gal_data_free(objvals);
+
+
+  /* Calculate the necessary value for clumps. */
   if(p->clumps)
     {
       for(i=0;i<pp->clumpsinobj;++i)
         {
+          /* Set the main row to fill. */
           ci=&pp->ci[ i * CCOL_NUMCOLS ];
-          median=gal_statistics_median(clumpsmed[i], 1);
-          median=gal_data_copy_to_new_type_free(median, GAL_TYPE_FLOAT64);
-          ci[ CCOL_MEDIAN ] = ( *((double *)(median->array))
-                                - (ci[ CCOL_RIV_SUM ]/ci[ CCOL_RIV_NUM ]) );
-          gal_data_free(clumpsmed[i]);
-          gal_data_free(median);
+
+          /* Median. */
+          if(p->ciflag[ CCOL_MEDIAN ])
+            {
+              if(clumpsvals[i])
+                {
+                  result=gal_statistics_median(clumpsvals[i], 1);
+                  result=gal_data_copy_to_new_type_free(result, 
GAL_TYPE_FLOAT64);
+                  ci[ CCOL_MEDIAN ] = ( *((double *)(result->array))
+                                        - (ci[ CCOL_RIV_SUM ]/ci[ CCOL_RIV_NUM 
]) );
+                  gal_data_free(result);
+                }
+              else ci[ CCOL_MEDIAN ] = NAN;
+            }
+
+          /* Sigma-clipping measurements. */
+          if(p->ciflag[ CCOL_SIGCLIPNUM ]
+             || p->ciflag[ CCOL_SIGCLIPSTD ]
+             || p->ciflag[ CCOL_SIGCLIPMEAN ]
+             || p->ciflag[ CCOL_SIGCLIPMEDIAN ])
+            {
+              if(clumpsvals[i])
+                {
+                  result=gal_statistics_sigma_clip(clumpsvals[i], 
p->sigmaclip[0],
+                                                   p->sigmaclip[1], 1, 1);
+                  sigcliparr=result->array;
+                  if(p->ciflag[ CCOL_SIGCLIPNUM ])
+                    ci[CCOL_SIGCLIPNUM]=sigcliparr[0];
+                  if(p->ciflag[ CCOL_SIGCLIPSTD ])
+                    ci[CCOL_SIGCLIPSTD]=( sigcliparr[3]
+                                          - (ci[ CCOL_RIV_SUM ]/ci[ 
CCOL_RIV_NUM ]));
+                  if(p->ciflag[ CCOL_SIGCLIPMEAN ])
+                    ci[CCOL_SIGCLIPMEAN]=( sigcliparr[2]
+                                           - (ci[ CCOL_RIV_SUM ]/ci[ 
CCOL_RIV_NUM ]));
+                  if(p->ciflag[ CCOL_SIGCLIPMEDIAN ])
+                    ci[CCOL_SIGCLIPMEDIAN]=( sigcliparr[1]
+                                             - (ci[ CCOL_RIV_SUM ]/ci[ 
CCOL_RIV_NUM ]));
+                  gal_data_free(result);
+                }
+              else
+                {
+                  if(p->ciflag[ CCOL_SIGCLIPNUM    ]) ci[ CCOL_SIGCLIPNUM  
]=NAN;
+                  if(p->ciflag[ CCOL_SIGCLIPSTD    ]) ci[ CCOL_SIGCLIPSTD  
]=NAN;
+                  if(p->ciflag[ CCOL_SIGCLIPMEAN   ]) ci[ CCOL_SIGCLIPMEAN 
]=NAN;
+                  if(p->ciflag[ CCOL_SIGCLIPMEDIAN ]) 
ci[CCOL_SIGCLIPMEDIAN]=NAN;
+                }
+            }
+
+          /* Estimate half of the total sum. */
+          if( p->ciflag[    CCOL_MAXIMUM     ]
+              || p->ciflag[ CCOL_HALFMAXNUM  ]
+              || p->ciflag[ CCOL_HALFMAXSUM  ]
+              || p->ciflag[ CCOL_HALFSUMNUM  ]
+              || p->ciflag[ CCOL_FRACMAX1NUM ]
+              || p->ciflag[ CCOL_FRACMAX1SUM ]
+              || p->ciflag[ CCOL_FRACMAX2NUM ]
+              || p->ciflag[ CCOL_FRACMAX2SUM ] )
+            {
+              if(clumpsvals[i])
+                parse_area_of_frac_sum(pp, clumpsvals[i], ci, 0);
+              else
+                {
+                  if( p->ciflag[ CCOL_MAXIMUM     ]) ci[ CCOL_MAXIMUM     
]=NAN;
+                  if( p->ciflag[ CCOL_HALFMAXNUM  ]) ci[ CCOL_HALFMAXNUM  
]=NAN;
+                  if( p->ciflag[ CCOL_HALFMAXSUM  ]) ci[ CCOL_HALFMAXSUM  
]=NAN;
+                  if( p->ciflag[ CCOL_HALFSUMNUM  ]) ci[ CCOL_HALFSUMNUM  
]=NAN;
+                  if( p->ciflag[ CCOL_FRACMAX1NUM ]) ci[ CCOL_FRACMAX1NUM 
]=NAN;
+                  if( p->ciflag[ CCOL_FRACMAX1SUM ]) ci[ CCOL_FRACMAX1SUM 
]=NAN;
+                  if( p->ciflag[ CCOL_FRACMAX2NUM ]) ci[ CCOL_FRACMAX2NUM 
]=NAN;
+                  if( p->ciflag[ CCOL_FRACMAX2SUM ]) ci[ CCOL_FRACMAX2SUM 
]=NAN;
+                }
+            }
+
+          /* Clean up this clump's values. */
+          gal_data_free(clumpsvals[i]);
         }
-      free(clumpsmed);
+      free(clumpsvals);
       free(ccounter);
     }
 }
diff --git a/bin/mkcatalog/parse.h b/bin/mkcatalog/parse.h
index 6b197a0..bcaa91f 100644
--- a/bin/mkcatalog/parse.h
+++ b/bin/mkcatalog/parse.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -33,6 +33,6 @@ void
 parse_clumps(struct mkcatalog_passparams *pp);
 
 void
-parse_median(struct mkcatalog_passparams *pp);
+parse_order_based(struct mkcatalog_passparams *pp);
 
 #endif
diff --git a/bin/mkcatalog/ui.c b/bin/mkcatalog/ui.c
index fcdae2a..37bef56 100644
--- a/bin/mkcatalog/ui.c
+++ b/bin/mkcatalog/ui.c
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -126,6 +126,9 @@ ui_initialize_options(struct mkcatalogparams *p,
   p->sfmagarea      = NAN;
   p->upnsigma       = NAN;
   p->zeropoint      = NAN;
+  p->sigmaclip[0]   = NAN;
+  p->sigmaclip[1]   = NAN;
+  p->pixelarcsecsq  = NAN;
   p->upsigmaclip[0] = NAN;
   p->upsigmaclip[1] = NAN;
   p->checkuplim[0]  = GAL_BLANK_INT32;
@@ -166,18 +169,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct mkcatalogparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -207,7 +210,7 @@ parse_opt(int key, char *arg, struct argp_state *state)
 
 
 /* Read the user's desired columns. Because the types of these options are
-   `GAL_TYPE_INVALID', this function will not be called when printing the
+   'GAL_TYPE_INVALID', this function will not be called when printing the
    full list of parameters and their values. */
 void *
 ui_column_codes_ll(struct argp_option *option, char *arg,
@@ -222,17 +225,17 @@ ui_column_codes_ll(struct argp_option *option, char *arg,
     {
       if( arg[0]=='0' && arg[1]=='\0' ) return NULL;
       else if ( !(arg[0]=='1' && arg[1]=='\0')  )
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' is not a "
-                      "valid value to the `%s' option: (\"%s\").\n\n`%s' is "
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' is not a "
+                      "valid value to the '%s' option: ('%s').\n\n'%s' is "
                       "an on/off option specifying if you want this column "
                       "in the output catalog, it doesn't take any "
                       "arguments. In a configuration file, it can only take "
-                      "a value of `0' (to be ignored) or `1'", arg,
+                      "a value of '0' (to be ignored) or '1'", arg,
                       option->name, option->doc, option->name);
     }
 
 
-  /* The user wants this column, so add it to the list. Note that the `ids'
+  /* The user wants this column, so add it to the list. Note that the 'ids'
      column means three columns. */
   if(option->key==UI_KEY_IDS)
     {
@@ -287,8 +290,8 @@ ui_check_upperlimit(struct argp_option *option, char *arg,
 
       /* Make sure there is at most only two numbers given. */
       if(raw->size>2)
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' (value to "
-                      "`--%s') contains %zu numbers, but only one or two "
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
+                      "'--%s') contains %zu numbers, but only one or two "
                       "are acceptable.\n\n"
                       "With this option MakeCatalog will write all the "
                       "positions and values of the random distribution for "
@@ -309,13 +312,13 @@ ui_check_upperlimit(struct argp_option *option, char *arg,
         {
           if( ceil(d[i]) != d[i])
             error_at_line(EXIT_FAILURE, 0, filename, lineno, "%g (value "
-                          "number %zu given to `--%s') is not an "
+                          "number %zu given to '--%s') is not an "
                           "integer. The value(s) to this option are "
                           "object/clump labels/identifiers, so they must be "
                           "integers", d[i], i+1, option->name);
           if( d[i]<=0 )
             error_at_line(EXIT_FAILURE, 0, filename, lineno, "%g (value "
-                          "number %zu given to `--%s') is not positive. "
+                          "number %zu given to '--%s') is not positive. "
                           "The value(s) to this option are object/clump "
                           "labels/identifiers, so they must be positive "
                           "integers", d[i], i+1, option->name);
@@ -354,29 +357,31 @@ ui_check_upperlimit(struct argp_option *option, char *arg,
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct mkcatalogparams *p)
 {
   float tmp;
-  size_t one=1;
+  double *darr;
   char *tailptr;
+  size_t i, one=1;
+  gal_list_i32_t *colcode;
 
   /* If an upper-limit check table is requested with a specific clump, but
      no clump catalog has been requested, then abort and inform the
      user. */
   if( p->checkuplim[1]!=GAL_BLANK_INT32 && p->clumpscat==0 )
     error(EXIT_FAILURE, 0, "no clumps catalog is requested, hence "
-          "`--checkuplim' is only available for objects (one value "
+          "'--checkuplim' is only available for objects (one value "
           "must be given to it).\n\n"
-          "To ask for a clumps catalog, please append `--clumpscat' to the "
+          "To ask for a clumps catalog, please append '--clumpscat' to the "
           "command calling MakeCatalog.\n\n"
           "If you want the upperlimit check table for an object, only give "
-          "one value (the object's label) to `--checkuplim'.");
+          "one value (the object's label) to '--checkuplim'.");
 
-  /* See if `--skyin' is a filename or a value. When the string is ONLY a
-     number (and nothing else), `tailptr' will point to the end of the
-     string (`\0'). */
+  /* See if '--skyin' is a filename or a value. When the string is ONLY a
+     number (and nothing else), 'tailptr' will point to the end of the
+     string ('\0'). */
   if(p->skyfile)
     {
       tmp=strtod(p->skyfile, &tailptr);
@@ -405,6 +410,38 @@ ui_read_check_only_options(struct mkcatalogparams *p)
           *((float *)(p->std->array))=tmp;
         }
     }
+
+  /* Make sure that '--fracmax' is given if necessary and that the fracsum
+     values are less than one. */
+  for(colcode=p->columnids; colcode!=NULL; colcode=colcode->next)
+    if( (colcode->v==UI_KEY_FRACMAXAREA1 || colcode->v==UI_KEY_FRACMAXAREA2)
+        && p->fracmax==NULL )
+      error(EXIT_FAILURE, 0, "please specify your required fractions for "
+            "'--fracmaxarea' using the '--fracmax' option (for example "
+            "'--fracmax=0.25,0.75' for two columns)");
+  if(p->fracmax)
+    {
+      /* Currently only 2 fracsums are accepted. */
+      if(p->fracmax->size>2)
+        error(EXIT_FAILURE, 0, "%zu values given to '--fracmax', only two "
+              "values are currently supported", p->fracmax->size);
+
+      /* Check the values. */
+      darr=p->fracmax->array;
+      for(i=0;i<p->fracmax->size;++i)
+        if(darr[i]<=0.0f || darr[i]>=1.0f)
+          error(EXIT_FAILURE, 0, "%g is not acceptable for '--fracmax', "
+                "values should be larger than 0 and less than 1", darr[i]);
+
+      /* If a second fracum column is necessary, make sure two values are
+         given to --fracmax. */
+      for(colcode=p->columnids; colcode!=NULL; colcode=colcode->next)
+        if(colcode->v==UI_KEY_FRACMAXAREA2 && p->fracmax->size==1)
+          error(EXIT_FAILURE, 0, "only one value given to '--fracmax', "
+                "but '--fracmaxarea2' was requested! You need to give "
+                "the requested fraction as a second value to '--fracmax', "
+                "separated by a comma (,)");
+    }
 }
 
 
@@ -419,8 +456,8 @@ ui_check_options_and_arguments(struct mkcatalogparams *p)
     {
       if( gal_fits_name_is_fits(p->objectsfile) && p->cp.hdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified. When the input is a FITS "
-              "file, a HDU must also be specified, you can use the `--hdu' "
-              "(`-h') option and give it the HDU number (starting from "
+              "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");
 
     }
@@ -511,13 +548,13 @@ ui_wcs_info(struct mkcatalogparams *p)
       errno=0;
       p->ctype=malloc(p->objects->ndim * sizeof *p->ctype);
       if(p->ctype==NULL)
-        error(EXIT_FAILURE, 0, "%s: %zu bytes for `p->ctype'", __func__,
+        error(EXIT_FAILURE, 0, "%s: %zu bytes for 'p->ctype'", __func__,
               p->objects->ndim * sizeof *p->ctype);
 
       /* Fill in the values. */
       for(i=0;i<p->objects->ndim;++i)
         {
-          /* CTYPE might contain `-' characters, we just want the first
+          /* CTYPE might contain '-' characters, we just want the first
              non-dash characters. The loop will either stop either at the end
              or where there is a dash. So we can just replace it with an
              end-of-string character. */
@@ -535,6 +572,7 @@ ui_wcs_info(struct mkcatalogparams *p)
 static size_t
 ui_num_clumps(struct mkcatalogparams *p)
 {
+  int32_t olab;
   char *basename;
   int keepinputdir;
   size_t i, counter, numclumps=0;
@@ -546,7 +584,7 @@ ui_num_clumps(struct mkcatalogparams *p)
   labsinobj=calloc(p->numobjects+1, sizeof *labsinobj);
   if(labsinobj==NULL)
     error(EXIT_FAILURE, 0, "%s: couldn't allocate %zu bytes for "
-          "`labsinobj'", __func__, p->numobjects * sizeof *labsinobj);
+          "'labsinobj'", __func__, p->numobjects * sizeof *labsinobj);
 
   /* Go over each pixel and find the unique clump labels within each
      object. */
@@ -556,13 +594,14 @@ ui_num_clumps(struct mkcatalogparams *p)
       if(*o>0 && *c>0)
         {
           /* See if the label has already been found. */
-          for(tmp=labsinobj[*o];tmp!=NULL;tmp=tmp->next) if(tmp->v==*c) break;
+          olab = p->outlabsinv ? p->outlabsinv[*o] : *o;
+          for(tmp=labsinobj[olab];tmp!=NULL;tmp=tmp->next) if(tmp->v==*c) 
break;
 
-          /* When it wasn't found, `tmp==NULL'. */
+          /* When it wasn't found, 'tmp==NULL'. */
           if(tmp==NULL)
             {
               ++numclumps;
-              gal_list_i32_add(&labsinobj[*o], *c);
+              gal_list_i32_add(&labsinobj[olab], *c);
             }
         }
 
@@ -581,7 +620,8 @@ ui_num_clumps(struct mkcatalogparams *p)
       if(*o>0 && *c>0)
         {
           counter=0;
-          for(tmp=labsinobj[*o];tmp!=NULL;tmp=tmp->next)
+          olab = p->outlabsinv ? p->outlabsinv[*o] : *o;
+          for(tmp=labsinobj[olab];tmp!=NULL;tmp=tmp->next)
             { counter++; if(tmp->v==*c) {*c=counter; break;} }
         }
 
@@ -612,6 +652,160 @@ ui_num_clumps(struct mkcatalogparams *p)
 
 
 
+/* To make the catalog processing more scalable (and later allow for
+   over-lappping regions), we will define a tile for each object. */
+static void
+ui_one_tile_per_object_correct_numobjects(struct mkcatalogparams *p)
+{
+  size_t ndim=p->objects->ndim;
+
+  uint8_t *rarray=NULL;
+  int32_t *l, *lf, *start;
+  gal_data_t *rowsremove=NULL;
+  size_t i, j, d, no, *min, *max, exists, width=2*ndim;
+  size_t *minmax=gal_pointer_allocate(GAL_TYPE_SIZE_T,
+                                      width*p->numobjects, 0, __func__,
+                                      "minmax");
+  size_t *coord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
+                                      "coord");
+
+  /* Initialize the minimum and maximum position for each tile/object. So,
+     we'll initialize the minimum coordinates to the maximum possible
+     'size_t' value (in 'GAL_BLANK_SIZE_T') and the maximums to zero. */
+  for(i=0;i<p->numobjects;++i)
+    for(d=0;d<ndim;++d)
+      {
+        minmax[ i * width +        d ] = GAL_BLANK_SIZE_T; /* Minimum. */
+        minmax[ i * width + ndim + d ] = 0;                /* Maximum. */
+      }
+
+  /* Go over the objects label image and correct the minimum and maximum
+     coordinates. */
+  start=p->objects->array;
+  lf=(l=p->objects->array)+p->objects->size;
+  do
+    if(*l>0)
+      {
+        /* Get the coordinates of this pixel. */
+        gal_dimension_index_to_coord(l-start, ndim, p->objects->dsize,
+                                     coord);
+
+        /* Check to see this coordinate is the smallest/largest found so
+           far for this label. Note that labels start from 1, while indexs
+           here start from zero. */
+        min = &minmax[ (*l-1) * width        ];
+        max = &minmax[ (*l-1) * width + ndim ];
+        for(d=0;d<ndim;++d)
+          {
+            if( coord[d] < min[d] ) min[d] = coord[d];
+            if( coord[d] > max[d] ) max[d] = coord[d];
+          }
+      }
+  while(++l<lf);
+
+  /* If a label doesn't exist in the image, then write over it and define
+     the unique labels to use for the next steps. To over-write, we have
+     two counters: 'i' (for the position in the input array) and 'no' (or
+     'num-objects' for the counter in the output table). In the end, 'no'
+     counts the total number of unique labels in the input. */
+  no=0;
+  for(i=0;i<p->numobjects;++i)
+    {
+      /* Make sure a pixel with this label exists in all dimensions. */
+      exists=0;
+      for(d=0;d<ndim;++d)
+        if ( minmax[ i * width + d ] == GAL_BLANK_SIZE_T
+             && minmax[ i * width + ndim + d ] == 0 )
+          {
+            /* When the object doesn't exist, but the user wants a row
+               anyway, make all the minimums and maximums of all
+               coordinates 0, note that the maximum is already zero. */
+            if(p->inbetweenints)
+              minmax[ i * width + d ] = 0;
+          }
+        else
+          {
+            /* Write over the blank elements when necessary
+               (i!=j). When i==j, then these statements are
+               redundant. */
+            minmax[no*width+d]=minmax[i*width+d];
+            minmax[no*width+ndim+d]=minmax[i*width+ndim+d];
+
+            /* Set the checked flag. */
+            exists=1;
+          }
+
+      /* If it does (or if the user wants to keep all integers), then
+         increment the output counter.*/
+      if(p->inbetweenints || exists) ++no;
+      else
+        {
+          /* If 'rarray' isn't defined yet, define it. */
+          if(rarray==NULL)
+            {
+              /* Note that by initializing with zeros, all (the possibly
+                 existing) previous rows that shouldn't be removed are
+                 flagged as zero in this array. */
+              rowsremove=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1,
+                                        &p->numobjects, NULL, 1,
+                                        p->cp.minmapsize, p->cp.quietmmap,
+                                        NULL, NULL, NULL);
+              rarray=rowsremove->array;
+            }
+          rarray[i]=1;
+        }
+    }
+
+  /* If 'rarray!=NULL', then there are elements to remove and we need to
+     make some modifications/corrections. */
+  if(rarray)
+    {
+      /* Build an array to keep the real ID of each tile. */
+      j=0;
+      p->outlabs=gal_pointer_allocate(GAL_TYPE_INT32, no, 0, __func__,
+                                      "p->outlabs");
+      for(i=0;i<p->numobjects;++i) if(rarray[i]==0) p->outlabs[j++]=i+1;
+
+      /* Allocate an array for easy finding of the proper label and fill it
+         with the new labels. This array should have an element for each of
+         the original labels (that are not contiguous). */
+      if(p->clumpscat)
+        {
+          p->outlabsinv=gal_pointer_allocate(GAL_TYPE_UINT32,
+                                             p->numobjects+1, 1,
+                                             __func__, "p->outlabsinv");
+          for(i=0;i<no;++i) p->outlabsinv[ p->outlabs[i] ] = i;
+        }
+
+      /* Correct numobjects and clean up. */
+      p->numobjects=no;
+      gal_data_free(rowsremove);
+
+      /* For a check:
+      for(i=0;i<p->numobjects;++i)
+        printf("outlabs[%zu]: %d\n", i, p->outlabs[i]);
+      */
+    }
+
+  /* For a check.
+  for(i=0;i<p->numobjects;++i)
+    printf("%zu: (%zu, %zu) --> (%zu, %zu)\n",
+           p->outlabs ? p->outlabs[i] : i+1, minmax[i*width],
+           minmax[i*width+1], minmax[i*width+2], minmax[i*width+3]);
+  */
+
+  /* Make the tiles. */
+  p->tiles=gal_tile_series_from_minmax(p->objects, minmax, p->numobjects);
+
+  /* Clean up. */
+  free(coord);
+  free(minmax);
+}
+
+
+
+
+
 /* The only mandatory input is the objects image, so first read that and
    make sure its type is correct. */
 static void
@@ -630,7 +824,7 @@ ui_read_labels(struct mkcatalogparams *p)
   ui_check_type_int(p->objectsfile, p->cp.hdu, p->objects->type);
 
 
-  /* Convert it to `int32' type (if it already isn't). */
+  /* Convert it to 'int32' type (if it already isn't). */
   p->objects=gal_data_copy_to_new_type_free(p->objects, GAL_TYPE_INT32);
 
 
@@ -640,12 +834,13 @@ ui_read_labels(struct mkcatalogparams *p)
           "currently only supports 2D or 3D datasets", p->objectsfile,
           p->cp.hdu, p->objects->ndim);
 
-  /* Make sure the `--spectrum' option is not given on a 2D image.  */
+  /* Make sure the '--spectrum' option is not given on a 2D image.  */
   if(p->spectrum && p->objects->ndim!=3)
-    error(EXIT_FAILURE, 0, "%s (hdu %s) has %zu dimensions, but `--spectrum' "
+    error(EXIT_FAILURE, 0, "%s (hdu %s) has %zu dimensions, but '--spectrum' "
           "is currently only defined on 3D datasets", p->objectsfile,
           p->cp.hdu, p->objects->ndim);
 
+
   /* See if the total number of objects is given in the header keywords. */
   keys[0].name="NUMLABS";
   keys[0].type=GAL_TYPE_SIZE_T;
@@ -658,6 +853,7 @@ ui_read_labels(struct mkcatalogparams *p)
       gal_data_free(tmp);
     }
 
+
   /* If there were no objects in the input, then inform the user with an
      error (it is pointless to build a catalog). */
   if(p->numobjects==0)
@@ -675,17 +871,22 @@ ui_read_labels(struct mkcatalogparams *p)
   ui_wcs_info(p);
 
 
+  /* Make the tiles that cover each object and also correct the total
+     number of objects based on the parsing of the image. */
+  ui_one_tile_per_object_correct_numobjects(p);
+
+
   /* Read the clumps array if necessary. */
   if(p->clumpscat)
     {
       /* Make sure the HDU is also given. */
       if(p->clumpshdu==NULL)
         error(EXIT_FAILURE, 0, "%s: no HDU/extension provided for the "
-              "CLUMPS dataset. Please use the `--clumpshdu' option to "
+              "CLUMPS dataset. Please use the '--clumpshdu' option to "
               "give a specific HDU using its number (counting from zero) "
               "or name. If the dataset is in another file, please use "
-              "`--clumpsfile' to give the filename. If you don't want any "
-              "clumps catalog output, remove the `--clumpscat' option from "
+              "'--clumpsfile' to give the filename. If you don't want any "
+              "clumps catalog output, remove the '--clumpscat' option from "
               "the command-line or give it a value of zero in a "
               "configuration file", p->usedclumpsfile);
 
@@ -698,7 +899,7 @@ ui_read_labels(struct mkcatalogparams *p)
 
       /* Check its size. */
       if( gal_dimension_is_different(p->objects, p->clumps) )
-        error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+        error(EXIT_FAILURE, 0, "'%s' (hdu: %s) and '%s' (hdu: %s) have a"
               "different dimension/size", p->usedclumpsfile, p->clumpshdu,
               p->objectsfile, p->cp.hdu);
 
@@ -722,14 +923,14 @@ ui_read_labels(struct mkcatalogparams *p)
       if(p->numclumps==0)
         {
           /* Just as a sanity check, see if there are any clumps (positive
-             valued pixels) in the array. If there are, then `NUMCLUMPS'
+             valued pixels) in the array. If there are, then 'NUMCLUMPS'
              wasn't set properly and we should abort with an error. */
           tmp=gal_statistics_maximum(p->clumps);
           if( *((int32_t *)(p->clumps->array))>0 )
-            error(EXIT_FAILURE, 0, "%s (hdu: %s): the `NUMCLUMPS' header "
+            error(EXIT_FAILURE, 0, "%s (hdu: %s): the 'NUMCLUMPS' header "
                   "keyword has a value of zero, but there are positive "
                   "pixels in the array, showing that there are clumps in "
-                  "image. This is a wrong usage of the `NUMCLUMPS' keyword."
+                  "image. This is a wrong usage of the 'NUMCLUMPS' keyword."
                   "It must contain the total number of clumps (irrespective "
                   "of how many objects there are). Please correct this issue "
                   "and run MakeCatalog again", p->usedclumpsfile,
@@ -747,7 +948,6 @@ ui_read_labels(struct mkcatalogparams *p)
         }
     }
 
-
   /* Clean up. */
   keys[0].name=keys[1].name=NULL;
   keys[0].array=keys[1].array=NULL;
@@ -761,19 +961,21 @@ ui_read_labels(struct mkcatalogparams *p)
 /* See which inputs are necessary. Ultimate, there are only three extra
    inputs: a values image, a sky image and a sky standard deviation
    image. However, there are many raw column measurements. So to keep
-   things clean, we'll just put a value of `1' in the three `values', `sky'
-   and `std' pointers everytime a necessary input is found. */
+   things clean, we'll just put a value of '1' in the three 'values', 'sky'
+   and 'std' pointers everytime a necessary input is found. */
 static void
 ui_necessary_inputs(struct mkcatalogparams *p, int *values, int *sky,
                     int *std)
 {
   size_t i;
 
+  /* Set necessary inputs based on options. */
+  if(p->forcereadstd) *std=1;
   if(p->upperlimit) *values=1;
-  if(p->spectrum)   *values=*std=1;
+  if(p->spectrum) *values=*std=1;
 
   /* Go over all the object columns. Note that the objects and clumps (if
-     the `--clumpcat' option is given) inputs are mandatory and it is not
+     the '--clumpcat' option is given) inputs are mandatory and it is not
      necessary to specify it here. */
   for(i=0; i<OCOL_NUMCOLS; ++i)
     if(p->oiflag[i])
@@ -786,12 +988,25 @@ ui_necessary_inputs(struct mkcatalogparams *p, int 
*values, int *sky,
         case OCOL_SUM:                *values        = 1;          break;
         case OCOL_SUM_VAR:            *values = *std = 1;          break;
         case OCOL_MEDIAN:             *values        = 1;          break;
+        case OCOL_MAXIMUM:            *values        = 1;          break;
+        case OCOL_SIGCLIPNUM:         *values        = 1;          break;
+        case OCOL_SIGCLIPMEDIAN:      *values        = 1;          break;
+        case OCOL_SIGCLIPMEAN:        *values        = 1;          break;
+        case OCOL_SIGCLIPSTD:         *values        = 1;          break;
         case OCOL_VX:                 *values        = 1;          break;
         case OCOL_VY:                 *values        = 1;          break;
         case OCOL_VZ:                 *values        = 1;          break;
         case OCOL_VXX:                *values        = 1;          break;
         case OCOL_VYY:                *values        = 1;          break;
         case OCOL_VXY:                *values        = 1;          break;
+        case OCOL_MINVX:              *values        = 1;          break;
+        case OCOL_MAXVX:              *values        = 1;          break;
+        case OCOL_MINVY:              *values        = 1;          break;
+        case OCOL_MAXVY:              *values        = 1;          break;
+        case OCOL_MINVZ:              *values        = 1;          break;
+        case OCOL_MAXVZ:              *values        = 1;          break;
+        case OCOL_MINVNUM:            *values        = 1;          break;
+        case OCOL_MAXVNUM:            *values        = 1;          break;
         case OCOL_SUMSKY:             *sky           = 1;          break;
         case OCOL_SUMVAR:             *std           = 1;          break;
         case OCOL_SUMWHT:             *values        = 1;          break;
@@ -806,6 +1021,13 @@ ui_necessary_inputs(struct mkcatalogparams *p, int 
*values, int *sky,
         case OCOL_UPPERLIMIT_S:       *values        = 1;          break;
         case OCOL_UPPERLIMIT_Q:       *values        = 1;          break;
         case OCOL_UPPERLIMIT_SKEW:    *values        = 1;          break;
+        case OCOL_HALFMAXNUM:         *values        = 1;          break;
+        case OCOL_HALFMAXSUM:         *values        = 1;          break;
+        case OCOL_HALFSUMNUM:         *values        = 1;          break;
+        case OCOL_FRACMAX1NUM:        *values        = 1;          break;
+        case OCOL_FRACMAX1SUM:        *values        = 1;          break;
+        case OCOL_FRACMAX2NUM:        *values        = 1;          break;
+        case OCOL_FRACMAX2SUM:        *values        = 1;          break;
         case OCOL_C_NUMALL:           /* Only clump labels.  */    break;
         case OCOL_C_NUM:              *values        = 1;          break;
         case OCOL_C_SUM:              *values        = 1;          break;
@@ -837,6 +1059,11 @@ ui_necessary_inputs(struct mkcatalogparams *p, int 
*values, int *sky,
           case CCOL_SUM:              *values        = 1;          break;
           case CCOL_SUM_VAR:          *values = *std = 1;          break;
           case CCOL_MEDIAN:           *values        = 1;          break;
+          case CCOL_MAXIMUM:          *values        = 1;          break;
+          case CCOL_SIGCLIPNUM:       *values        = 1;          break;
+          case CCOL_SIGCLIPMEDIAN:    *values        = 1;          break;
+          case CCOL_SIGCLIPMEAN:      *values        = 1;          break;
+          case CCOL_SIGCLIPSTD:       *values        = 1;          break;
           case CCOL_RIV_NUM:          /* Only clump labels. */     break;
           case CCOL_RIV_SUM:          *values        = 1;          break;
           case CCOL_RIV_SUM_VAR:      *values = *std = 1;          break;
@@ -846,6 +1073,14 @@ ui_necessary_inputs(struct mkcatalogparams *p, int 
*values, int *sky,
           case CCOL_VXX:              *values        = 1;          break;
           case CCOL_VYY:              *values        = 1;          break;
           case CCOL_VXY:              *values        = 1;          break;
+          case CCOL_MINVX:            *values        = 1;          break;
+          case CCOL_MINVY:            *values        = 1;          break;
+          case CCOL_MINVZ:            *values        = 1;          break;
+          case CCOL_MAXVX:            *values        = 1;          break;
+          case CCOL_MAXVY:            *values        = 1;          break;
+          case CCOL_MAXVZ:            *values        = 1;          break;
+          case CCOL_MINVNUM:          *values        = 1;          break;
+          case CCOL_MAXVNUM:          *values        = 1;          break;
           case CCOL_SUMSKY:           *sky           = 1;          break;
           case CCOL_SUMVAR:           *std           = 1;          break;
           case CCOL_SUMWHT:           *values        = 1;          break;
@@ -866,6 +1101,13 @@ ui_necessary_inputs(struct mkcatalogparams *p, int 
*values, int *sky,
           case CCOL_UPPERLIMIT_S:     *values        = 1;          break;
           case CCOL_UPPERLIMIT_Q:     *values        = 1;          break;
           case CCOL_UPPERLIMIT_SKEW:  *values        = 1;          break;
+          case CCOL_HALFMAXNUM:       *values        = 1;          break;
+          case CCOL_HALFMAXSUM:       *values        = 1;          break;
+          case CCOL_HALFSUMNUM:       *values        = 1;          break;
+          case CCOL_FRACMAX1NUM:      *values        = 1;          break;
+          case CCOL_FRACMAX1SUM:      *values        = 1;          break;
+          case CCOL_FRACMAX2NUM:      *values        = 1;          break;
+          case CCOL_FRACMAX2SUM:      *values        = 1;          break;
           default:
             error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
                   "fix the problem. The code %zu is not a recognized "
@@ -890,8 +1132,8 @@ ui_preparation_check_size_read_tiles(struct 
mkcatalogparams *p,
   /* See if we should treat this dataset as tile values or not. */
   if( gal_dimension_is_different(p->objects, in) )
     {
-      /* The `tl' structure is initialized here. But this function may be
-         called multiple times. So, first check if the `tl' structure has
+      /* The 'tl' structure is initialized here. But this function may be
+         called multiple times. So, first check if the 'tl' structure has
          already been initialized and if so, don't repeat it. */
       if(tl->ndim==0)
         {
@@ -901,20 +1143,20 @@ ui_preparation_check_size_read_tiles(struct 
mkcatalogparams *p,
           gal_tile_full_permutation(tl);
         }
 
-      /* See if the size of the `in' dataset corresponds to the
+      /* See if the size of the 'in' dataset corresponds to the
          tessellation. */
       if(in->size!=tl->tottiles)
         error(EXIT_FAILURE, 0, "%s (hdu: %s): doesn't have the right "
               "size (%zu elements or pixels).\n\n"
-              "It must either be the same size as `%s' (hdu: `%s'), or "
+              "It must either be the same size as '%s' (hdu: '%s'), or "
               "it must have the same number of elements as the total "
               "number of tiles in the tessellation (%zu). In the latter "
               "case, each pixel is assumed to be a fixed value for a "
               "complete tile.\n\n"
-              "Run with `-P' to see the (tessellation) options/settings "
+              "Run with '-P' to see the (tessellation) options/settings "
               "and their values). For more information on tessellation in "
               "Gnuastro, please run the following command (use the arrow "
-              "keys for up and down and press `q' to return to the "
+              "keys for up and down and press 'q' to return to the "
               "command-line):\n\n"
               "    $ info gnuastro tessellation",
               filename, hdu, in->size, p->objectsfile, p->cp.hdu,
@@ -926,7 +1168,7 @@ ui_preparation_check_size_read_tiles(struct 
mkcatalogparams *p,
 
 
 
-/* Subtract `sky' from the input dataset depending on its size (it may be
+/* Subtract 'sky' from the input dataset depending on its size (it may be
    the whole array or a tile-values array).. */
 static void
 ui_subtract_sky(struct mkcatalogparams *p)
@@ -955,7 +1197,7 @@ ui_subtract_sky(struct mkcatalogparams *p)
           tile=&tl->tiles[tid];
 
           /* Subtract the Sky value from the input image. */
-          GAL_TILE_PARSE_OPERATE(tile, NULL, 0, 0, {*i-=skyarr[tid];});
+          GAL_TILE_PARSE_OPERATE(tile, p->values, 1, 0, {*o-=skyarr[tid];});
         }
     }
 
@@ -965,10 +1207,6 @@ ui_subtract_sky(struct mkcatalogparams *p)
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
           "the problem. For some reason, the size doesn't match", __func__,
           PACKAGE_BUGREPORT);
-
-  /* Inform the user that this operation is done (if necessary). */
-  if(!p->cp.quiet)
-    printf("  - Sky subtracted from input values.\n");
 }
 
 
@@ -995,9 +1233,9 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
       if(p->valueshdu==NULL)
         error(EXIT_FAILURE, 0, "%s: no HDU/extension provided for the "
               "VALUES dataset. Atleast one column needs this dataset. "
-              "Please use the `--valueshdu' option to give a specific HDU "
+              "Please use the '--valueshdu' option to give a specific HDU "
               "using its number (counting from zero) or name. If the "
-              "dataset is in another file, please use `--valuesfile' to "
+              "dataset is in another file, please use '--valuesfile' to "
               "give the filename", p->usedvaluesfile);
 
       /* Read the values dataset. */
@@ -1010,14 +1248,14 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
 
       /* Make sure it has the correct size. */
       if( gal_dimension_is_different(p->objects, p->values) )
-        error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+        error(EXIT_FAILURE, 0, "'%s' (hdu: %s) and '%s' (hdu: %s) have a"
               "different dimension/size", p->usedvaluesfile, p->valueshdu,
               p->objectsfile, p->cp.hdu);
 
-      /* Initially, `p->hasblank' was set based on the objects image, but
+      /* Initially, 'p->hasblank' was set based on the objects image, but
          it may happen that the objects image only has zero values for
          blank pixels, so we'll also do a check on the input image. */
-      p->hasblank = gal_blank_present(p->objects, 1);
+      p->hasblank = gal_blank_present(p->values, 1);
 
       /* Reset the units of the value-based columns if the input dataset
          has defined units. */
@@ -1025,10 +1263,12 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
         {
           for(column=p->objectcols; column!=NULL; column=column->next)
             if( !strcmp(column->unit, MKCATALOG_NO_UNIT) )
-              { free(column->unit); column->unit=p->values->unit; }
+              { free(column->unit);
+                gal_checkset_allocate_copy(p->values->unit, &column->unit); }
           for(column=p->clumpcols; column!=NULL; column=column->next)
             if( !strcmp(column->unit, MKCATALOG_NO_UNIT) )
-              { free(column->unit); column->unit=p->values->unit; }
+              { free(column->unit);
+                gal_checkset_allocate_copy(p->values->unit, &column->unit); }
         }
     }
 
@@ -1045,9 +1285,9 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
             error(EXIT_FAILURE, 0, "%s: no HDU/extension provided for the "
                   "SKY dataset. Atleast one column needs this dataset, or "
                   "you have asked to subtract the Sky from the values.\n\n"
-                  "Please use the `--skyhdu' option to give a specific HDU "
+                  "Please use the '--skyhdu' option to give a specific HDU "
                   "using its number (counting from zero) or name. If the "
-                  "dataset is in another file, please use `--skyin' to "
+                  "dataset is in another file, please use '--skyin' to "
                   "give the filename", p->usedskyfile);
 
           /* Read the Sky dataset. */
@@ -1077,9 +1317,9 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
         error(EXIT_FAILURE, 0, "%s: no HDU/extension provided for the "
               "SKY STANDARD DEVIATION dataset.\n\n"
               "Atleast one column needs this dataset. Please use the "
-              "`--stdhdu' option to give a specific HDU using its number "
+              "'--stdhdu' option to give a specific HDU using its number "
               "(counting from zero) or name. If the dataset is in another "
-              "file, please use `--stdin' to give the filename",
+              "file, please use '--stdin' to give the filename",
               p->usedstdfile);
 
       /* Read the Sky standard deviation image into memory. */
@@ -1104,7 +1344,7 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
       if(p->checkuplim[0] != GAL_BLANK_INT32
          && p->checkuplim[0] > p->numobjects)
         error(EXIT_FAILURE, 0, "%d (object identifier for the "
-              "`--checkuplim' option) is larger than the number of "
+              "'--checkuplim' option) is larger than the number of "
               "objects in the input labels (%zu)", p->checkuplim[0],
               p->numobjects);
 
@@ -1114,7 +1354,7 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
           /* Make sure the HDU for the mask image is given. */
           if(p->upmaskhdu==NULL)
             error(EXIT_FAILURE, 0, "%s: no HDU/extension provided, please "
-                  "use the `--upmaskhdu' option to specify a specific HDU "
+                  "use the '--upmaskhdu' option to specify a specific HDU "
                   "using its number (counting from zero) or name",
                   p->upmaskfile);
 
@@ -1128,7 +1368,7 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
 
           /* Check its size. */
           if( gal_dimension_is_different(p->objects, p->upmask) )
-            error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+            error(EXIT_FAILURE, 0, "'%s' (hdu: %s) and '%s' (hdu: %s) have a"
                   "different dimension/size", p->upmaskfile, p->upmaskhdu,
                   p->objectsfile, p->cp.hdu);
 
@@ -1166,41 +1406,49 @@ ui_preparations_read_inputs(struct mkcatalogparams *p)
 static void
 ui_preparations_read_keywords(struct mkcatalogparams *p)
 {
-  float minstd;
   gal_data_t *tmp;
+  float std, minstd;
   gal_data_t *keys=NULL;
 
-  /* When a Sky standard deviation dataset (not number) is given. */
-  if(p->std && p->std->size>1)
+  /* Set the counts-per-second correction. */
+  if(p->std)
     {
-      /* Read the keywords from the standard deviation image. */
-      keys=gal_data_array_calloc(2);
-      keys[0].next=&keys[1];
-      keys[0].name="MINSTD";              keys[1].name="MEDSTD";
-      keys[0].type=GAL_TYPE_FLOAT32;      keys[1].type=GAL_TYPE_FLOAT32;
-      keys[0].array=&minstd;              keys[1].array=&p->medstd;
-      gal_fits_key_read(p->usedstdfile, p->stdhdu, keys, 0, 0);
-
-      /* If the two keywords couldn't be read. We don't want to slow down
-         the user for the median (which needs sorting). So we'll just
-         calculate the minimum which is necessary for the `p->cpscorr'. */
-      if(keys[1].status) p->medstd=NAN;
-      if(keys[0].status)
+      if(p->std->size>1)
         {
-          /* Calculate the minimum STD. */
-          tmp=gal_statistics_minimum(p->std);
-          minstd=*((float *)(tmp->array));
-          gal_data_free(tmp);
+          /* Read the keywords from the standard deviation image. */
+          keys=gal_data_array_calloc(2);
+          keys[0].next=&keys[1];
+          keys[0].name="MINSTD";              keys[1].name="MEDSTD";
+          keys[0].type=GAL_TYPE_FLOAT32;      keys[1].type=GAL_TYPE_FLOAT32;
+          keys[0].array=&minstd;              keys[1].array=&p->medstd;
+          gal_fits_key_read(p->usedstdfile, p->stdhdu, keys, 0, 0);
+
+          /* If the two keywords couldn't be read. We don't want to slow down
+             the user for the median (which needs sorting). So we'll just
+             calculate the minimum which is necessary for the 'p->cpscorr'. */
+          if(keys[1].status) p->medstd=NAN;
+          if(keys[0].status)
+            {
+              /* Calculate the minimum STD. */
+              tmp=gal_statistics_minimum(p->std);
+              minstd=*((float *)(tmp->array));
+              gal_data_free(tmp);
 
-          /* If the units are in variance, then take the square root. */
-          if(p->variance) minstd=sqrt(minstd);
-        }
-      p->cpscorr = minstd>1 ? 1.0f : minstd;
+              /* If the units are in variance, then take the square root. */
+              if(p->variance) minstd=sqrt(minstd);
+            }
+          p->cpscorr = minstd>1 ? 1.0f : minstd;
 
-      /* Clean up. */
-      keys[0].name=keys[1].name=NULL;
-      keys[0].array=keys[1].array=NULL;
-      gal_data_array_free(keys, 2, 1);
+          /* Clean up. */
+          keys[0].name=keys[1].name=NULL;
+          keys[0].array=keys[1].array=NULL;
+          gal_data_array_free(keys, 2, 1);
+        }
+      else
+        {
+          std=((float *)(p->std->array))[0];
+          p->cpscorr=std>1 ? 1.0f : std;
+        }
     }
 }
 
@@ -1223,7 +1471,7 @@ ui_preparations_both_names(struct mkcatalogparams *p)
     {
       /* When the user has specified a name, any possible directories in
          that name must be respected. So we have kept the actual
-         `keepinputdir' value in a temporary variable above and set it to 1
+         'keepinputdir' value in a temporary variable above and set it to 1
          only for this operation. Later we set it back to what it was. */
       p->cp.keepinputdir=1;
 
@@ -1234,8 +1482,8 @@ ui_preparations_both_names(struct mkcatalogparams *p)
       if( gal_fits_name_is_fits(p->cp.output) )
         {
           /* The output file name that the user has given supersedes the
-             `tableformat' argument. In this case, the filename is a FITS
-             file, so if `tableformat' is a text file, we will change it to
+             'tableformat' argument. In this case, the filename is a FITS
+             file, so if 'tableformat' is a text file, we will change it to
              a default binary FITS table. */
           if( p->cp.tableformat==GAL_TABLE_FORMAT_TXT )
             p->cp.tableformat=GAL_TABLE_FORMAT_BFITS;
@@ -1264,7 +1512,7 @@ ui_preparations_both_names(struct mkcatalogparams *p)
       p->clumpsout=p->objectsout;
     }
 
-  /* Revert `keepinputdir' to what it was. */
+  /* Revert 'keepinputdir' to what it was. */
   p->cp.keepinputdir=keepinputdir;
 }
 
@@ -1283,9 +1531,9 @@ ui_preparations_outnames(struct mkcatalogparams *p)
   if(p->cp.output)
     {
       /* If the output name is a FITS file, then
-         `gal_tableintern_check_fits_format' will see if the tableformat
+         'gal_tableintern_check_fits_format' will see if the tableformat
          corresponds to a FITS table or not. If the output name isn't a
-         FITS file then the current value of `p->cp.tableformat' is
+         FITS file then the current value of 'p->cp.tableformat' is
          irrelevant and it must be set to text. We use this value in the
          end to determine specific features. */
       if( gal_fits_name_is_fits(p->cp.output) )
@@ -1333,11 +1581,11 @@ ui_preparations_outnames(struct mkcatalogparams *p)
                                                     : p->objectsfile),
                                                   suffix);
 
-      /* Set `keepinputdir' to what it was before. */
+      /* Set 'keepinputdir' to what it was before. */
       p->cp.keepinputdir=keepinputdir;
     }
 
-  /* Just to avoid bugs (`p->cp.output' must no longer be used), we'll free
+  /* Just to avoid bugs ('p->cp.output' must no longer be used), we'll free
      it and set it to NULL.*/
   free(p->cp.output);
   p->cp.output=NULL;
@@ -1347,73 +1595,6 @@ ui_preparations_outnames(struct mkcatalogparams *p)
 
 
 
-/* To make the catalog processing more scalable (and later allow for
-   over-lappping regions), we will define a tile for each object. */
-void
-ui_one_tile_per_object(struct mkcatalogparams *p)
-{
-  size_t ndim=p->objects->ndim;
-
-  int32_t *l, *lf, *start;
-  size_t i, d, *min, *max, width=2*ndim;
-  size_t *minmax=gal_pointer_allocate(GAL_TYPE_SIZE_T,
-                                      width*p->numobjects, 0, __func__,
-                                      "minmax");
-  size_t *coord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
-                                      "coord");
-
-
-  /* Initialize the minimum and maximum position for each tile/object. So,
-     we'll initialize the minimum coordinates to the maximum possible
-     `size_t' value (in `GAL_BLANK_SIZE_T') and the maximums to zero. */
-  for(i=0;i<p->numobjects;++i)
-    for(d=0;d<ndim;++d)
-      {
-        minmax[ i * width +        d ] = GAL_BLANK_SIZE_T; /* Minimum. */
-        minmax[ i * width + ndim + d ] = 0;                /* Maximum. */
-      }
-
-  /* Go over the objects label image and correct the minimum and maximum
-     coordinates. */
-  start=p->objects->array;
-  lf=(l=p->objects->array)+p->objects->size;
-  do
-    if(*l>0)
-      {
-        /* Get the coordinates of this pixel. */
-        gal_dimension_index_to_coord(l-start, ndim, p->objects->dsize, coord);
-
-        /* Check to see this coordinate is the smallest/largest found so
-           far for this label. Note that labels start from 1, while indexs
-           here start from zero. */
-        min = &minmax[ (*l-1) * width        ];
-        max = &minmax[ (*l-1) * width + ndim ];
-        for(d=0;d<ndim;++d)
-          {
-            if( coord[d] < min[d] ) min[d] = coord[d];
-            if( coord[d] > max[d] ) max[d] = coord[d];
-          }
-      }
-  while(++l<lf);
-
-  /* For a check.
-  for(i=0;i<p->numobjects;++i)
-    printf("%zu: (%zu, %zu) --> (%zu, %zu)\n", i+1, minmax[i*width],
-           minmax[i*width+1], minmax[i*width+2], minmax[i*width+3]);
-  */
-
-  /* Make the tiles. */
-  p->tiles=gal_tile_series_from_minmax(p->objects, minmax, p->numobjects);
-
-  /* Clean up. */
-  free(coord);
-  free(minmax);
-}
-
-
-
-
-
 /* When a spectrum is requested, the slice information (slice number and
    slice WCS) is common to all different spectra. So instead of calculating
    it every time, we'll just make it once here, then copy it for every
@@ -1434,12 +1615,12 @@ ui_preparations_spectrum_wcs(struct mkcatalogparams *p)
 
   /* A small sanity check. */
   if(p->objects->ndim!=3)
-    error(EXIT_FAILURE, 0, "%s (hdu %s) is a %zuD dataset, but `--spectrum' "
+    error(EXIT_FAILURE, 0, "%s (hdu %s) is a %zuD dataset, but '--spectrum' "
           "is currently only defined on 3D datasets", p->objectsfile,
           p->cp.hdu, p->objects->ndim);
 
   /* Allocate space for the slice number as well as the X and Y positions
-     for WCS conversion. Note that the `z' axis is going to be converted to
+     for WCS conversion. Note that the 'z' axis is going to be converted to
      WCS later, so we'll just give it the basic information now.*/
   x=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &numslices, NULL, 0,
                    p->cp.minmapsize, p->cp.quietmmap, NULL, NULL, NULL);
@@ -1478,15 +1659,15 @@ ui_preparations_spectrum_wcs(struct mkcatalogparams *p)
   else
     for(i=0;i<numslices;++i) ((uint32_t *)(p->specsliceinfo->array))[i]=i+1;
 
-  /* Set the slice WCS column information. Note that `z' is now the WCS
+  /* Set the slice WCS column information. Note that 'z' is now the WCS
      coordinate value of the third dimension, and to avoid wasting extra
      space (this column is repeated one very object's spectrum), we'll
      convert it to a 32-bit floating point dataset. */
   p->specsliceinfo->next=gal_data_copy_to_new_type(z, GAL_TYPE_FLOAT32);
 
   /* For a final check.
-  gal_table_write(p->specsliceinfo, NULL, GAL_TABLE_FORMAT_BFITS,
-                  "specsliceinfo.fits", "test-debug",0);
+  gal_table_write(p->specsliceinfo, NULL, NULL, GAL_TABLE_FORMAT_BFITS,
+                  "specsliceinfo.fits", "test-debug", 0);
   */
 
   /* Clean up. */
@@ -1511,19 +1692,19 @@ ui_preparations_upperlimit(struct mkcatalogparams *p)
     {
       for(i=0;p->uprange[i]!=-1;++i) ++c;
       if(c!=p->objects->ndim)
-        error(EXIT_FAILURE, 0, "%zu values given to `--uprange', but input "
+        error(EXIT_FAILURE, 0, "%zu values given to '--uprange', but input "
               "has %zu dimensions", c, p->objects->ndim);
     }
 
   /* Check the number of random samples. */
   if( p->upnum < MKCATALOG_UPPERLIMIT_MINIMUM_NUM )
-    error(EXIT_FAILURE, 0, "%zu not acceptable as `--upnum'. The minimum "
+    error(EXIT_FAILURE, 0, "%zu not acceptable as '--upnum'. The minimum "
           "acceptable number of random samples for the upper limit "
           "magnitude is %d", p->upnum, MKCATALOG_UPPERLIMIT_MINIMUM_NUM);
 
   /* Check if sigma-clipping parameters have been given. */
   if( isnan(p->upsigmaclip[0]) )
-    error(EXIT_FAILURE, 0, "`--upsigmaclip' is mandatory for measuring "
+    error(EXIT_FAILURE, 0, "'--upsigmaclip' is mandatory for measuring "
           "the upper-limit magnitude. It takes two numbers separated by "
           "a comma. The first is the multiple of sigma and the second is "
           "the aborting criteria: <1: tolerance level, >1: number of "
@@ -1531,7 +1712,7 @@ ui_preparations_upperlimit(struct mkcatalogparams *p)
 
   /* Check if the sigma multiple is given. */
   if( isnan(p->upnsigma) )
-    error(EXIT_FAILURE, 0, "`--upnsigma' is mandatory for measuring the "
+    error(EXIT_FAILURE, 0, "'--upnsigma' is mandatory for measuring the "
           "upperlimit magnitude. Its value is the multiple of final sigma "
           "that is reported as the upper-limit");
 
@@ -1557,7 +1738,7 @@ ui_preparations(struct mkcatalogparams *p)
   /* If no columns are requested, then inform the user. */
   if(p->columnids==NULL && p->spectrum==0)
     error(EXIT_FAILURE, 0, "no measurements requested! Please run again "
-          "with `--help' for the possible list of measurements");
+          "with '--help' for the possible list of measurements");
 
 
   /* Set the actual filenames to use. */
@@ -1585,10 +1766,6 @@ ui_preparations(struct mkcatalogparams *p)
   ui_preparations_outnames(p);
 
 
-  /* Make the tiles that cover each object. */
-  ui_one_tile_per_object(p);
-
-
   /* If a spectrum is requested, generate the two WCS columns. */
   if(p->spectrum)
     {
@@ -1599,7 +1776,7 @@ ui_preparations(struct mkcatalogparams *p)
 
   /* Allocate the reference random number generator and seed values. It
      will be cloned once for every thread. If the user hasn't called
-     `envseed', then we want it to be different for every run, so we need
+     'envseed', then we want it to be different for every run, so we need
      to re-set the seed. */
   if(p->upperlimit) ui_preparations_upperlimit(p);
 
@@ -1612,7 +1789,7 @@ ui_preparations(struct mkcatalogparams *p)
      separately (and not using the actual output columns that have the same
      values), because playing with the output columns can cause bad
      bugs. If the user wants performance, they are encouraged to run
-     MakeCatalog with `--noclumpsort' and avoid the whole process all
+     MakeCatalog with '--noclumpsort' and avoid the whole process all
      together. */
   if(p->clumps && !p->noclumpsort && p->cp.numthreads>1)
     {
@@ -1654,9 +1831,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
mkcatalogparams *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
+  /* 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
@@ -1728,16 +1905,20 @@ ui_read_check_inputs_setup(int argc, char *argv[], 
struct mkcatalogparams *p)
       if(p->subtractsky || p->sky)
         {
           if(p->sky->size==1)
-            printf("  - Sky: %g\n", *((float *)(p->sky->array)) );
+            printf("  - Sky: %g (single value for all pixels)\n",
+                   *((float *)(p->sky->array)) );
           else
             printf("  - Sky: %s (hdu: %s)\n", p->usedskyfile, p->skyhdu);
+          if(p->subtractsky)
+            printf("    - Sky has been subtracted from values internally.\n");
         }
 
       if(p->std)
         {
           tmp = p->variance ? "VAR" : "STD";
           if(p->std->size==1)
-            printf("  - Sky %s: %g\n", tmp, *((float *)(p->std->array)) );
+            printf("  - Sky %s: %g (single value for all pixels)\n", tmp,
+                   *((float *)(p->std->array)) );
           else
             printf("  - Sky %s: %s (hdu: %s)\n", tmp, p->usedstdfile,
                    p->stdhdu);
@@ -1825,7 +2006,11 @@ ui_free_report(struct mkcatalogparams *p, struct timeval 
*t1)
   gal_data_free(p->upmask);
   gal_data_free(p->clumps);
   gal_data_free(p->objects);
+  if(p->outlabs) free(p->outlabs);
+  gal_list_data_free(p->clumpcols);
+  gal_list_data_free(p->objectcols);
   gal_list_data_free(p->specsliceinfo);
+  if(p->outlabsinv) free(p->outlabsinv);
   if(p->upcheckout) free(p->upcheckout);
   gal_data_array_free(p->tiles, p->numobjects, 0);
 
@@ -1834,7 +2019,7 @@ ui_free_report(struct mkcatalogparams *p, struct timeval 
*t1)
     {
       /* Note that each element of the array is the first node in a list of
          datasets. So we can't free the first one with
-         `gal_list_data_free', we'll delete all the nodes after it in the
+         'gal_list_data_free', we'll delete all the nodes after it in the
          loop. */
       for(i=0;i<p->numobjects;++i)
         {
@@ -1847,7 +2032,7 @@ ui_free_report(struct mkcatalogparams *p, struct timeval 
*t1)
 
   /* If the Sky or its STD image were given in tiles, then we defined a
      tile structure to deal with them. The initialization of the tile
-     structure is checked with its `ndim' element. */
+     structure is checked with its 'ndim' element. */
   if(p->cp.tl.ndim) gal_tile_full_free_contents(&p->cp.tl);
 
   /* If an upper limit range warning is necessary, print it here. */
@@ -1861,7 +2046,7 @@ ui_free_report(struct mkcatalogparams *p, struct timeval 
*t1)
             "input is less than double their length. If the input is taken "
             "from a larger dataset, this issue can be solved by using a "
             "larger part of it. You can also run MakeCatalog with "
-            "`--checkuplim' to see the distribution for a special "
+            "'--checkuplim' to see the distribution for a special "
             "object or clump as a table and personally inspect its "
             "reliability. \n\n");
 
diff --git a/bin/mkcatalog/ui.h b/bin/mkcatalog/ui.h
index f4d2f05..572720e 100644
--- a/bin/mkcatalog/ui.h
+++ b/bin/mkcatalog/ui.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -34,6 +34,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum program_args_groups
 {
   UI_GROUP_UPPERLIMIT = GAL_OPTIONS_GROUP_AFTER_COMMON,
+  UI_GROUP_OTHERSETTINGS,
   UI_GROUP_COLUMNS_IDS,
   UI_GROUP_COLUMNS_POSITION_PIXEL,
   UI_GROUP_COLUMNS_POSITION_WCS,
@@ -86,12 +87,15 @@ enum option_keys_enum
   UI_KEY_SKYHDU,
   UI_KEY_STDHDU,
   UI_KEY_WITHCLUMPS,
+  UI_KEY_FORCEREADSTD,
   UI_KEY_ZEROPOINT,
+  UI_KEY_SIGMACLIP,
   UI_KEY_VARIANCE,
   UI_KEY_SUBTRACTSKY,
   UI_KEY_SFMAGNSIGMA,
   UI_KEY_SFMAGAREA,
   UI_KEY_SPECTRUM,
+  UI_KEY_INBETWEENINTS,
   UI_KEY_UPMASKFILE,
   UI_KEY_UPMASKHDU,
   UI_KEY_UPNUM,
@@ -100,9 +104,12 @@ enum option_keys_enum
   UI_KEY_UPNSIGMA,
   UI_KEY_CHECKUPLIM,
   UI_KEY_NOCLUMPSORT,
+  UI_KEY_FRACMAX,
 
   UI_KEY_OBJID,                         /* Catalog columns. */
   UI_KEY_IDINHOSTOBJ,
+  UI_KEY_AREAARCSEC2,
+  UI_KEY_SURFACEBRIGHTNESS,
   UI_KEY_AREAXY,
   UI_KEY_CLUMPSAREA,
   UI_KEY_WEIGHTAREA,
@@ -111,6 +118,14 @@ enum option_keys_enum
   UI_KEY_GEOX,
   UI_KEY_GEOY,
   UI_KEY_GEOZ,
+  UI_KEY_MINVX,
+  UI_KEY_MAXVX,
+  UI_KEY_MINVY,
+  UI_KEY_MAXVY,
+  UI_KEY_MINVZ,
+  UI_KEY_MAXVZ,
+  UI_KEY_MINVNUM,
+  UI_KEY_MAXVNUM,
   UI_KEY_CLUMPSX,
   UI_KEY_CLUMPSY,
   UI_KEY_CLUMPSZ,
@@ -140,6 +155,7 @@ enum option_keys_enum
   UI_KEY_BRIGHTNESSNORIVER,
   UI_KEY_MEAN,
   UI_KEY_MEDIAN,
+  UI_KEY_MAXIMUM,
   UI_KEY_CLUMPSMAGNITUDE,
   UI_KEY_UPPERLIMIT,
   UI_KEY_UPPERLIMITONESIGMA,
@@ -150,10 +166,28 @@ enum option_keys_enum
   UI_KEY_RIVERNUM,
   UI_KEY_SKY,
   UI_KEY_STD,
+  UI_KEY_SIGCLIPNUMBER,
+  UI_KEY_SIGCLIPMEDIAN,
+  UI_KEY_SIGCLIPMEAN,
+  UI_KEY_SIGCLIPSTD,
   UI_KEY_GEOSEMIMAJOR,
   UI_KEY_GEOSEMIMINOR,
   UI_KEY_GEOAXISRATIO,
   UI_KEY_GEOPOSITIONANGLE,
+  UI_KEY_FWHM,
+  UI_KEY_HALFMAXAREA,
+  UI_KEY_HALFMAXRADIUS,
+  UI_KEY_HALFMAXSUM,
+  UI_KEY_HALFMAXSB,
+  UI_KEY_HALFSUMAREA,
+  UI_KEY_HALFSUMSB,
+  UI_KEY_HALFSUMRADIUS,
+  UI_KEY_FRACMAXSUM1,
+  UI_KEY_FRACMAXSUM2,
+  UI_KEY_FRACMAXAREA1,
+  UI_KEY_FRACMAXAREA2,
+  UI_KEY_FRACMAXRADIUS1,
+  UI_KEY_FRACMAXRADIUS2,
 };
 
 
diff --git a/bin/mkcatalog/upperlimit.c b/bin/mkcatalog/upperlimit.c
index 7e3c5ad..b1b27bc 100644
--- a/bin/mkcatalog/upperlimit.c
+++ b/bin/mkcatalog/upperlimit.c
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -63,7 +63,7 @@ upperlimit_make_clump_tiles(struct mkcatalog_passparams *pp)
 
   /* Initialize the minimum and maximum position for each tile/clump. So,
      we'll initialize the minimum coordinates to the maximum possible
-     `size_t' value (in `GAL_BLANK_SIZE_T') and the maximums to zero. */
+     'size_t' value (in 'GAL_BLANK_SIZE_T') and the maximums to zero. */
   for(i=0;i<pp->clumpsinobj;++i)
     for(d=0;d<ndim;++d)
       {
@@ -173,7 +173,7 @@ upperlimit_random_range(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
          just use the full possible range. */
       if( p->uprange && p->uprange[d] )
         {
-          /* Set the minimum of the random range. Since `size_t' is always
+          /* Set the minimum of the random range. Since 'size_t' is always
              positive, to make sure the difference isn't negative, we need
              to convert them to integer first. */
           if( (int)coord[d] - ((int)p->uprange[d])/2 > 0 )
@@ -200,7 +200,7 @@ upperlimit_random_range(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
                          - (dsize[d] - tile->dsize[d]) );
             }
 
-          /* `minadd' and `maxadd' were defined to account for the removed
+          /* 'minadd' and 'maxadd' were defined to account for the removed
              smaller range when an object is on the edge. Their role is to
              add to the other side of the range as much as possible when
              one side is decreased on an edge. */
@@ -215,7 +215,7 @@ upperlimit_random_range(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
           /* We are positioning the FIRST pixel of the tile, not the
              center. So, the minimum possible value is zero, and in order
              to not push out of the image, the maximum is the
-             `tile->dsize[d]' away from the edge. */
+             'tile->dsize[d]' away from the edge. */
           min[d]=0;
           max[d]=dsize[d]-tile->dsize[d]-1;
         }
@@ -249,7 +249,7 @@ upperlimit_random_position(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
   size_t r;
   struct mkcatalogparams *p=pp->p;
 
-  /* `gsl_rng_get' returns an inclusive value between the minimum and
+  /* 'gsl_rng_get' returns an inclusive value between the minimum and
      maximum of the particular generator. It may happen that the labeled
      region extends the full range of a dimension. In that case, the only
      possible starting point would be 0. */
@@ -311,7 +311,7 @@ upperlimit_write_comments(struct mkcatalogparams *p,
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
                 "address the problem. The value %zu is not recognized for "
-                "`p->input->ndim'", __func__, PACKAGE_BUGREPORT,
+                "'p->input->ndim'", __func__, PACKAGE_BUGREPORT,
                 p->objects->ndim);
         }
       gal_list_str_add(comments, str, 0);
@@ -401,11 +401,11 @@ upperlimit_write_check(struct mkcatalogparams *p, 
gal_list_sizet_t *check_x,
                      "Z-axis position of random footprint's first pixel.");
   s=gal_data_alloc(sarr, GAL_TYPE_FLOAT32, 1, &num, NULL, 0, p->cp.minmapsize,
                    p->cp.quietmmap, "RANDOM_SUM",
-                   p->values->unit ? p->values->unit : "input-units",
+                   p->values->unit ? p->values->unit : MKCATALOG_NO_UNIT,
                    "Sum of pixel values over random footprint.");
 
 
-  /* If `size_t' isn't 32-bit on this system, then convert the unsigned
+  /* If 'size_t' isn't 32-bit on this system, then convert the unsigned
      64-bit values to 32-bit because the FITS table format doesn't
      recognize 64-bit integers.*/
   if( GAL_TYPE_SIZE_T != GAL_TYPE_UINT32 )
@@ -446,7 +446,7 @@ upperlimit_write_check(struct mkcatalogparams *p, 
gal_list_sizet_t *check_x,
   if(check_z) { y->next=z; z->next=s; }
   else        { y->next=s;            }
   gal_list_str_reverse(&comments);
-  gal_table_write(x, comments, p->cp.tableformat, p->upcheckout,
+  gal_table_write(x, NULL, comments, p->cp.tableformat, p->upcheckout,
                   "UPPERLIMIT_CHECK", 0);
 
   /* Inform the user. */
@@ -513,7 +513,7 @@ upperlimit_measure(struct mkcatalog_passparams *pp, int32_t 
clumplab,
                   col = clumplab ? CCOL_UPPERLIMIT_S : OCOL_UPPERLIMIT_S;
                   o[col] = scarr[3];
 
-                  /* sigma multiplied by `upnsigma'. */
+                  /* sigma multiplied by 'upnsigma'. */
                   col = clumplab ? CCOL_UPPERLIMIT_B : OCOL_UPPERLIMIT_B;
                   o[col] = scarr[3] * p->upnsigma;
 
@@ -611,8 +611,8 @@ upperlimit_one_tile(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
   upperlimit_random_range(pp, tile, min, max, clumplab);
 
 
-  /* `se_inc' is just used temporarily, the important thing here is
-     `st_oo'. */
+  /* 'se_inc' is just used temporarily, the important thing here is
+     'st_oo'. */
   st_oo = ( clumplab
             ? gal_tile_start_end_ind_inclusive(tile, p->objects, se_inc)
             : pp->st_o );
@@ -632,7 +632,7 @@ upperlimit_one_tile(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
                                            p->objects->type);
 
       /* Starting and ending coordinates for this random position, note
-         that in `pp' we have the starting and ending coordinates of the
+         that in 'pp' we have the starting and ending coordinates of the
          actual tile. */
       increment     = 0;
       num_increment = 1;
@@ -689,7 +689,7 @@ upperlimit_one_tile(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
 
 
       /* Further processing is only necessary if this random tile was fully
-         parsed. If it was, we must reset `nfailed' to zero again. */
+         parsed. If it was, we must reset 'nfailed' to zero again. */
       if(continueparse)
         {
           nfailed=0;
@@ -717,7 +717,7 @@ upperlimit_one_tile(struct mkcatalog_passparams *pp, 
gal_data_t *tile,
 
             default:
               error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s "
-                    "to fix the problem. `ndim' value of %zu is not "
+                    "to fix the problem. 'ndim' value of %zu is not "
                     "recognized", __func__, PACKAGE_BUGREPORT, ndim);
             }
           gal_list_f32_add(&check_s, continueparse ? sum : NAN);
@@ -784,7 +784,7 @@ upperlimit_calculate(struct mkcatalog_passparams *pp)
           && p->checkuplim[1] != GAL_BLANK_INT32
           && p->checkuplim[1] > pp->clumpsinobj )
         error(EXIT_FAILURE, 0, "object %d has %zu clumps, but an upperlimit "
-              "check table (using the `--checkuplim' option) has been "
+              "check table (using the '--checkuplim' option) has been "
               "requested for clump %d", pp->object, pp->clumpsinobj,
               p->checkuplim[1]);
 
diff --git a/bin/mkcatalog/upperlimit.h b/bin/mkcatalog/upperlimit.h
index b25589d..666c3a0 100644
--- a/bin/mkcatalog/upperlimit.h
+++ b/bin/mkcatalog/upperlimit.h
@@ -5,7 +5,7 @@ MakeCatalog is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/mknoise/Makefile.am b/bin/mknoise/Makefile.am
index 555f7fd..1b22a4c 100644
--- a/bin/mknoise/Makefile.am
+++ b/bin/mknoise/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astmknoise
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astmknoise_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                   $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astmknoise_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                   -lgnuastro $(CONFIG_LDADD)
 
 astmknoise_SOURCES = main.c ui.c mknoise.c
 
@@ -41,7 +40,6 @@ EXTRA_DIST = main.h authors-cite.h args.h ui.h mknoise.h
 
 
 
-
 ## The configuration file (distribute and install).
 ## NOTE: the man page is created in doc/Makefile.am
 dist_sysconf_DATA = astmknoise.conf
diff --git a/bin/mknoise/args.h b/bin/mknoise/args.h
index 719a561..4c5f262 100644
--- a/bin/mknoise/args.h
+++ b/bin/mknoise/args.h
@@ -5,7 +5,7 @@ MakeNoise is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -64,7 +64,7 @@ struct argp_option program_options[] =
       0,
       "Fixed background magnitude for whole input.",
       GAL_OPTIONS_GROUP_INPUT,
-      &p->background_mag,
+      &p->background,
       GAL_TYPE_FLOAT64,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
@@ -83,6 +83,19 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "bgisbrightness",
+      UI_KEY_BGISBRIGHTNESS,
+      0,
+      0,
+      "Background is brightness, not magnitude.",
+      GAL_OPTIONS_GROUP_OPERATING_MODE,
+      &p->bgisbrightness,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
 
 
 
diff --git a/bin/mknoise/astmknoise.conf b/bin/mknoise/astmknoise.conf
index 5748e2f..b28237f 100644
--- a/bin/mknoise/astmknoise.conf
+++ b/bin/mknoise/astmknoise.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astmknoise                     # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
@@ -20,9 +20,6 @@
 # warranty.
 
 # Input:
- background     -10.00
- instrumental   0.000
- zeropoint      0.00
 
 # Output:
  type           float32
diff --git a/bin/mknoise/authors-cite.h b/bin/mknoise/authors-cite.h
index ba297a8..47e931e 100644
--- a/bin/mknoise/authors-cite.h
+++ b/bin/mknoise/authors-cite.h
@@ -5,7 +5,7 @@ MakeNoise is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/mknoise/main.c b/bin/mknoise/main.c
index 5e5ae13..373b49d 100644
--- a/bin/mknoise/main.c
+++ b/bin/mknoise/main.c
@@ -5,7 +5,7 @@ MakeNoise is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/mknoise/main.h b/bin/mknoise/main.h
index c315b4b..48a2326 100644
--- a/bin/mknoise/main.h
+++ b/bin/mknoise/main.h
@@ -5,7 +5,7 @@ MakeNoise is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -47,12 +47,12 @@ struct mknoiseparams
   double           sigma;    /* Total noise sigma (ignoring others).     */
   double    instrumental;    /* Standard deviation constants.            */
   double       zeropoint;    /* Zeropoint magnitude of image.            */
-  double  background_mag;    /* Background in magnitudes.                */
+  double      background;    /* Background in magnitudes.                */
+  uint8_t bgisbrightness;    /* Background is brightness, not magnitude. */
   uint8_t        envseed;    /* ==1, generate a random seed.             */
 
   /* Internal */
   gal_data_t      *input;    /* Input image data in double precision.    */
-  double      background;    /* Background in units of brightness.       */
   gsl_rng           *rng;    /* Main instance of random number generator.*/
   const char   *rng_name;    /* The type/name of the Random number gen.  */
   unsigned long rng_seed;    /* Seed of Random number generator.         */
diff --git a/bin/mknoise/mknoise.c b/bin/mknoise/mknoise.c
index 27c5735..6731ed7 100644
--- a/bin/mknoise/mknoise.c
+++ b/bin/mknoise/mknoise.c
@@ -5,7 +5,7 @@ MakeNoise is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -35,6 +35,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gsl/gsl_randist.h>     /* To make noise.        */
 
 #include <gnuastro-internal/timing.h>
+#include <gnuastro-internal/checkset.h>
 
 #include "main.h"
 
@@ -54,53 +55,66 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 void
 convertsaveoutput(struct mknoiseparams *p)
 {
-  char keyname1[FLEN_KEYWORD];
+  double tmp;
+  char *keyname;
   gal_fits_list_key_t *headers=NULL;
-  char keyname2[FLEN_KEYWORD], keyname3[FLEN_KEYWORD];
-  char keyname4[FLEN_KEYWORD], keyname5[FLEN_KEYWORD];
-
 
   /* Add the proper information to the header of the output: */
   gal_fits_key_write_filename("INF", p->inputname, &headers, 0);
-  if( !isnan(p->background_mag) )
+  if( !isnan(p->background) )
     {
-      strcpy(keyname1, "BCKGRND");
-      gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname1, 0,
-                                &p->background_mag, 0, "Background "
-                                "value (in magnitude) for noise.",
-                                0, NULL);
-      strcpy(keyname2, "BZRPNT");
-      gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname2, 0,
-                                &p->zeropoint, 0,
-                                "Zeropoint magnitude of image.", 0, NULL);
-      strcpy(keyname3, "INSTRU");
-      gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname3, 0,
-                                &p->instrumental, 0,
-                                "Instrumental noise in units of flux.",
-                                0, NULL);
+      gal_checkset_allocate_copy("BCKGRND", &keyname);
+      gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname, 1,
+                                &p->background, 0,
+                                "Background value for Poisson noise.",
+                                0, NULL, 0);
+      if( !isnan(p->zeropoint) )
+        {
+          tmp=-2.5 * log10(p->background) + p->zeropoint;
+          gal_checkset_allocate_copy("BCKGMAG", &keyname);
+          gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname, 1,
+                                    &tmp, 0,
+                                    "Background value in magnitudes",
+                                    0, NULL, 0);
+          gal_checkset_allocate_copy("BCKGZP", &keyname);
+          gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname, 1,
+                                    &p->zeropoint, 0,
+                                    "Zeropoint for interpreting magnitudes.",
+                                    0, NULL, 0);
+        }
+      if( !isnan(p->instrumental) )
+        {
+          gal_checkset_allocate_copy("INSTRU", &keyname);
+          gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname, 1,
+                                    &p->instrumental, 0,
+                                    "Instrumental noise in units of flux.",
+                                    0, NULL, 0);
+        }
     }
   else
     {
-      strcpy(keyname1, "SIGMA");
-      gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname1, 0,
-                                &p->sigma, 0, "Total noise sigma", 0, NULL);
+      gal_checkset_allocate_copy("SIGMA", &keyname);
+      gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64, keyname, 1,
+                                &p->sigma, 0, "Total noise sigma", 0,
+                                NULL, 0);
     }
-  strcpy(keyname4, "RNGTYPE");
-  gal_fits_key_list_add_end(&headers, GAL_TYPE_STRING, keyname4, 0,
+  gal_checkset_allocate_copy("RNGTYPE", &keyname);
+  gal_fits_key_list_add_end(&headers, GAL_TYPE_STRING, keyname, 1,
                             (void *)(p->rng_name), 0,
                             "Random number generator (by GSL) type.",
-                            0, NULL);
-  strcpy(keyname5, "RNGSEED");
-  gal_fits_key_list_add_end(&headers, GAL_TYPE_ULONG, keyname5, 0,
+                            0, NULL, 0);
+  gal_checkset_allocate_copy("RNGSEED", &keyname);
+  gal_fits_key_list_add_end(&headers, GAL_TYPE_ULONG, keyname, 1,
                             &p->rng_seed, 0,
                             "Random number generator (by GSL) seed.",
-                            0, NULL);
+                            0, NULL, 0);
 
-  /* Save the output: */
+  /* Save the output: first convert it to the desired type,  */
+  if(p->input->name) { free(p->input->name); p->input->name=NULL; }
   p->input=gal_data_copy_to_new_type_free(p->input, p->cp.type);
   p->input->name="NOISED";
   gal_fits_img_write(p->input, p->cp.output, headers, PROGRAM_NAME);
-  p->input->name=NULL;
+  p->input->name=NULL; /* because we didn't allocate it. */
 
   /* Write the configuration keywords. */
   gal_fits_key_write_filename("input", p->inputname, &p->cp.okeys, 1);
@@ -115,8 +129,10 @@ convertsaveoutput(struct mknoiseparams *p)
 void
 mknoise(struct mknoiseparams *p)
 {
-  double *d, *df, background=p->background;
-  double instpowtwo = p->instrumental*p->instrumental;
+  double *d, *df, back=p->background;
+  double inst = ( isnan(p->instrumental)
+                  ? 0.0f
+                  : p->instrumental*p->instrumental );
 
   /* Add the noise: */
   df=(d=p->input->array)+p->input->size;
@@ -129,9 +145,7 @@ mknoise(struct mknoiseparams *p)
   else
     {
       do
-        *d += ( background
-                + gsl_ran_gaussian(p->rng,
-                                   sqrt( instpowtwo + background + *d )) );
+        *d += back + gsl_ran_gaussian(p->rng, sqrt( inst + back + *d ));
       while(++d<df);
     }
 
diff --git a/bin/mknoise/mknoise.h b/bin/mknoise/mknoise.h
index 5d144be..dc10361 100644
--- a/bin/mknoise/mknoise.h
+++ b/bin/mknoise/mknoise.h
@@ -5,7 +5,7 @@ MakeNoise is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/mknoise/ui.c b/bin/mknoise/ui.c
index b1f775d..f39bbc9 100644
--- a/bin/mknoise/ui.c
+++ b/bin/mknoise/ui.c
@@ -5,7 +5,7 @@ MakeNoise is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -116,7 +116,8 @@ ui_initialize_options(struct mknoiseparams *p,
   /* Initialize options for this program. */
   p->sigma               = NAN;
   p->zeropoint           = NAN;
-  p->background_mag      = NAN;
+  p->background          = NAN;
+  p->instrumental        = NAN;
 
 
   /* Modify common options. */
@@ -158,18 +159,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct mknoiseparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -217,23 +218,46 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct mknoiseparams *p)
 {
-  /* At leaset one of `--sigma' or `--background' are necessary. */
-  if( isnan(p->sigma) && isnan(p->background_mag) )
-    error(EXIT_FAILURE, 0, "at least one of `--sigma' or `--background' "
-          "must be given to identify the noise level");
-
-
-  /* If a background magnitude is given (and the user hasn't given a
-     `--sigma'), the zeropoint is necessary. */
-  if( isnan(p->sigma) && !isnan(p->background_mag) && isnan(p->zeropoint) )
-    error(EXIT_FAILURE, 0, "no zeropoint magnitude given. When the noise is "
-          "identified by the background magnitude, a zeropoint magnitude "
-          "is mandatory. Please use the `--zeropoint' option to specify "
-          "a zeropoint magnitude");
+  /* At leaset one of '--sigma' or '--background' are necessary. */
+  if( isnan(p->sigma) && isnan(p->background) )
+    error(EXIT_FAILURE, 0, "noise not defined: please define the noise "
+          "level with either '--sigma' (for a fixed noise STD for all "
+          "the pixels, irrespective of their value) or '--background' "
+          "(to use in a Poisson noise model, where the noise will differ "
+          "based on pixel value)");
+
+
+  /* If a background magnitude is given ('--bgbrightness' hasn't been
+     called), the zeropoint is necessary. */
+  if( !isnan(p->background) )
+    {
+      /* Make sure that the background can be interpretted properly. */
+      if( p->bgisbrightness==0 && isnan(p->zeropoint) )
+        error(EXIT_FAILURE, 0, "missing background information. When the "
+              "noise is identified by the background, a zeropoint magnitude "
+              "is mandatory. Please use the '--zeropoint' option to specify "
+              "a zeropoint magnitude. Alternatively, if your background value "
+              "is brightness (which is in linear scale and doesn't need a "
+              "zeropoint), please use '--bgisbrightness'");
+
+      /* If the background is in units of magnitudes, convert it to
+         brightness. */
+      if( p->bgisbrightness==0 )
+        p->background = pow(10, (p->zeropoint-p->background)/2.5f);
+
+      /* Make sure that the background is larger than 1 (where Poisson
+         noise is actually defined). */
+      if( p->background < 1 )
+        error(EXIT_FAILURE, 0, "background value is smaller than 1. "
+              "Poisson noise is only defined on a positive distribution "
+              "with values larger than 1. You can use the '--sigma' "
+              "option to add a fixed noise level (with any positive value) "
+              "to any pixel.");
+    }
 }
 
 
@@ -249,8 +273,8 @@ ui_check_options_and_arguments(struct mknoiseparams *p)
     {
       if( gal_fits_name_is_fits(p->inputname) && p->cp.hdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified. When the input is a FITS "
-              "file, a HDU must also be specified, you can use the `--hdu' "
-              "(`-h') option and give it the HDU number (starting from "
+              "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");
 
     }
@@ -305,16 +329,6 @@ ui_preparations(struct mknoiseparams *p)
     p->cp.output=gal_checkset_automatic_output(&p->cp, p->inputname,
                                                "_noised.fits");
 
-
-  /* Convert the background value from magnitudes to flux. Note that
-     magnitudes are actually calculated from the ratio of brightness, not
-     flux. But in the context of MakeNoise where everything is done on
-     pixels independently, brightness and flux are the same (flux is
-     multiplied by the area of one pixel (=1) to give brightness).*/
-  if( !isnan(p->background_mag) )
-    p->background=pow(10, (p->zeropoint-p->background_mag)/2.5f);
-
-
   /* Allocate the random number generator: */
   p->rng=gal_checkset_gsl_rng(p->envseed, &p->rng_name, &p->rng_seed);
 }
@@ -348,9 +362,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
mknoiseparams *p)
   char message[GAL_TIMING_VERB_MSG_LENGTH_V];
 
 
-  /* 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
+  /* 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
diff --git a/bin/mknoise/ui.h b/bin/mknoise/ui.h
index b21e0e4..a2a5684 100644
--- a/bin/mknoise/ui.h
+++ b/bin/mknoise/ui.h
@@ -5,7 +5,7 @@ MakeNoise is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -33,16 +33,17 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Available letters for short options:
 
    a c d f g j k l m n p r t u v w x y
-   A B C E G H J L O Q R W X Y
+   A C E G H J L O Q R W X Y
 */
 enum option_keys_enum
 {
   /* With short-option version. */
-  UI_KEY_SIGMA        = 's',
-  UI_KEY_INSTRUMENTAL = 'i',
-  UI_KEY_BACKGROUND   = 'b',
-  UI_KEY_ZEROPOINT    = 'z',
-  UI_KEY_ENVSEED      = 'e',
+  UI_KEY_SIGMA          = 's',
+  UI_KEY_INSTRUMENTAL   = 'i',
+  UI_KEY_BACKGROUND     = 'b',
+  UI_KEY_ZEROPOINT      = 'z',
+  UI_KEY_ENVSEED        = 'e',
+  UI_KEY_BGISBRIGHTNESS = 'B',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
diff --git a/bin/mkprof/Makefile.am b/bin/mkprof/Makefile.am
index 884a270..ca0a5c8 100644
--- a/bin/mkprof/Makefile.am
+++ b/bin/mkprof/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astmkprof
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astmkprof_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                  $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astmkprof_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                  -lgnuastro $(CONFIG_LDADD)
 
 astmkprof_SOURCES = main.c ui.c mkprof.c oneprofile.c profiles.c
 
diff --git a/bin/mkprof/args.h b/bin/mkprof/args.h
index be1039c..c0d05fe 100644
--- a/bin/mkprof/args.h
+++ b/bin/mkprof/args.h
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -33,7 +33,7 @@ struct argp_option program_options[] =
     {
       "background",
       UI_KEY_BACKGROUND,
-      "STR",
+      "FITS",
       0,
       "A background image to make the profiles on.",
       GAL_OPTIONS_GROUP_INPUT,
@@ -83,6 +83,32 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET,
       ui_parse_kernel
     },
+    {
+      "customtable",
+      UI_KEY_CUSTOMTABLE,
+      "FITS/TXT",
+      0,
+      "File for 'custom' profile.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->customname,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "customtablehdu",
+      UI_KEY_CUSTOMTABLEHDU,
+      "INT/STR",
+      0,
+      "HDU of table given to '--customtable'.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->customhdu,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
 
 
 
@@ -169,7 +195,7 @@ struct argp_option program_options[] =
       UI_KEY_MODE,
       "STR",
       0,
-      "Mode of `--ccol': `img' or `wcs'.",
+      "Mode of '--ccol': 'img' or 'wcs'.",
       UI_GROUP_PROFILES,
       &p->mode,
       GAL_TYPE_STRING,
@@ -342,7 +368,7 @@ struct argp_option program_options[] =
 
     {
       0, 0, 0, 0,
-      "Columns, by info (see `--searchin'), or number (starting from 1):",
+      "Columns, by info (see '--searchin'), or number (starting from 1):",
       UI_GROUP_CATALOG
     },
     {
@@ -364,7 +390,8 @@ struct argp_option program_options[] =
       "STR/INT",
       0,
       "sersic (1), moffat (2), gaussian (3), point (4), "
-      "flat (5), circumference (6), distance (7).",
+      "flat (5), circumference (6), distance (7), "
+      "radial-table (8)",
       UI_GROUP_CATALOG,
       &p->fcol,
       GAL_TYPE_STRING,
@@ -560,7 +587,7 @@ struct argp_option program_options[] =
       UI_KEY_CUNIT,
       "STR[, ... ]",
       0,
-      "Units of the WCS coordinates (e.g., `deg').",
+      "Units of the WCS coordinates (e.g., 'deg').",
       UI_GROUP_WCS,
       &p->cunit,
       GAL_TYPE_FLOAT64,
diff --git a/bin/mkprof/astmkprof-3d.conf b/bin/mkprof/astmkprof-3d.conf
index 9ccb1d0..d6f07a3 100644
--- a/bin/mkprof/astmkprof-3d.conf
+++ b/bin/mkprof/astmkprof-3d.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,6 +12,8 @@
 #  $ info astmkprof                      # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
+# Copyright (C) 2019-2021, Free Software Foundation, Inc.
+#
 # 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
@@ -54,4 +56,4 @@
  cdelt   0.2/3600,0.2/3600,1.25e-10
  pc              -1,0,0,0,1,0,0,0,1
  cunit                    deg,deg,m
- ctype       RA---TAN,DEC--TAN,AWAV
\ No newline at end of file
+ ctype       RA---TAN,DEC--TAN,AWAV
diff --git a/bin/mkprof/astmkprof.conf b/bin/mkprof/astmkprof.conf
index 53c05e8..371b534 100644
--- a/bin/mkprof/astmkprof.conf
+++ b/bin/mkprof/astmkprof.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astmkprof                      # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
@@ -21,6 +21,7 @@
 
 #input
  backhdu                   1
+ customtablehdu            1
 
 # Output:
  mergedsize        1000,1000
diff --git a/bin/mkprof/authors-cite.h b/bin/mkprof/authors-cite.h
index 777969a..ec51735 100644
--- a/bin/mkprof/authors-cite.h
+++ b/bin/mkprof/authors-cite.h
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/mkprof/main.c b/bin/mkprof/main.c
index 630b415..66c618d 100644
--- a/bin/mkprof/main.c
+++ b/bin/mkprof/main.c
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/mkprof/main.h b/bin/mkprof/main.h
index 832b1c6..7440f8c 100644
--- a/bin/mkprof/main.h
+++ b/bin/mkprof/main.h
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -66,6 +66,7 @@ enum profile_types
   PROFILE_FLAT,                 /* Flat profile.               */
   PROFILE_CIRCUMFERENCE,        /* Circumference profile.      */
   PROFILE_DISTANCE,             /* Elliptical radius of pixel. */
+  PROFILE_CUSTOM,          /* Radial prof. in file/table. */
 
   PROFILE_MAXIMUM_CODE,         /* Just for a sanity check.    */
 };
@@ -114,6 +115,8 @@ struct mkprofparams
   char            *backname;  /* Name of background image file name.      */
   char             *catname;  /* Name of catalog of parameters.           */
   char             *backhdu;  /* HDU of background image.                 */
+  char          *customname;  /* Table to use for radial profile.         */
+  char           *customhdu;  /* HDU of table to use for radial profile.  */
   size_t             *dsize;  /* Size of the output image.                */
   uint8_t       clearcanvas;  /* Pixels in background image set to zero.  */
   gal_data_t        *kernel;  /* Parameters to define a kernel.           */
@@ -188,10 +191,12 @@ struct mkprofparams
   char           *wcsheader;  /* The WCS header information for main img. */
   int            wcsnkeyrec;  /* The number of keywords in the WCS header.*/
   char       *mergedimgname;  /* Name of merged image.                    */
+  gal_data_t        *custom;  /* Table containing custom values.          */
+  double   customregular[2];  /* Non-NaN if input table is regular.       */
   int                  nwcs;  /* for WCSLIB: no. coord. representations.  */
   struct wcsprm        *wcs;  /* WCS information for this dataset.        */
-  size_t               ndim;  /* Number of dimensions (for `nomerged').   */
-                              /* We can't put it in `out' because it is   */
+  size_t               ndim;  /* Number of dimensions (for 'nomerged').   */
+                              /* We can't put it in 'out' because it is   */
                               /* meaning ful there.                       */
 };
 
diff --git a/bin/mkprof/mkprof.c b/bin/mkprof/mkprof.c
index 2d30e99..60d188b 100644
--- a/bin/mkprof/mkprof.c
+++ b/bin/mkprof/mkprof.c
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -78,7 +78,7 @@ builtqueue_addempty(struct builtqueue **bq)
   errno=0;
   tbq=malloc(sizeof *tbq);
   if(tbq==NULL)
-    error(EXIT_FAILURE, 0, "%s: allocating %zu bytes for `tbq'",
+    error(EXIT_FAILURE, 0, "%s: allocating %zu bytes for 'tbq'",
           __func__, sizeof *tbq);
 
   /* Initialize the values (same order as in structure definition). */
@@ -134,8 +134,8 @@ saveindividual(struct mkonthread *mkp)
   char *filename, *jobname, *outdir=p->outdir;
 
 
-  /* Write the name and remove a similarly named file when the `--kernel'
-     option wasn't called. If `--kernel' is called, then we should just use
+  /* Write the name and remove a similarly named file when the '--kernel'
+     option wasn't called. If '--kernel' is called, then we should just use
      the final merged filename. */
   if(p->kernel)
     filename=p->mergedimgname;
@@ -154,7 +154,7 @@ saveindividual(struct mkonthread *mkp)
   else
     {
       /* Allocate space for the corrected crpix and fill it in. Both
-         `crpix' and `fpixel_i' are in FITS order. */
+         'crpix' and 'fpixel_i' are in FITS order. */
       crpix=gal_pointer_allocate(GAL_TYPE_FLOAT64, ndim, 0, __func__,
                                  "crpix");
       for(i=0;i<ndim;++i)
@@ -171,95 +171,98 @@ saveindividual(struct mkonthread *mkp)
   /* Write profile settings into the FITS file. */
   gal_fits_key_list_add(&keys, GAL_TYPE_STRING, "PROFILE", 0,
                         ui_profile_name_write(mkp->func), 0,
-                        "Radial function", 0, NULL);
+                        "Radial function", 0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT64, "XCENTER", 0,
                         &p->x[id], 0, "Center of profile in catalog "
-                        "(FITS axis 1)", 0, NULL);
+                        "(FITS axis 1)", 0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT64, "YCENTER", 0,
                         &p->y[id], 0, "Center of profile in catalog "
-                        "(FITS axis 2)", 0, NULL);
+                        "(FITS axis 2)", 0, NULL, 0);
   if(ndim==3)
     gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT64, "ZCENTER", 0,
                           &p->z[id], 0, "Center of profile in catalog "
-                          "(FITS axis 3)", 0, NULL);
+                          "(FITS axis 3)", 0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "RADIUS", 0,
                         &p->r[id], 0, "Radial parameter in catalog",
-                        0, NULL);
+                        0, NULL, 0);
   if( mkp->func==PROFILE_SERSIC || mkp->func==PROFILE_MOFFAT )
     gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "PINDEX", 0,
                           &p->r[id], 0, "Index (Sersic or Moffat) of profile"
-                          "in catalog", 0, NULL);
+                          "in catalog", 0, NULL, 0);
   if(ndim==2)
     {
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "PA_DEG", 0,
                             &p->p1[id], 0, "Position angle of profile in "
-                            "catalog", 0, "deg");
+                            "catalog", 0, "deg", 0);
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "AXISRATIO", 0,
                             &p->q1[id], 0, "Axis ratio of profile in catalog",
-                            0, NULL);
+                            0, NULL, 0);
     }
   else
     {
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "PA1_DEG", 0,
                             &p->p1[id], 0, "First X-Z-X Euler angle in 3D", 0,
-                            "deg");
+                            "deg", 0);
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "PA2_DEG", 0,
                             &p->p2[id], 0, "Second X-Z-X Euler angle in 3D", 0,
-                            "deg");
+                            "deg", 0);
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "PA3_DEG", 0,
                             &p->p3[id], 0, "Third X-Z-X Euler angle in 3D", 0,
-                            "deg");
+                            "deg", 0);
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "AXISRATIO1", 0,
                             &p->q1[id], 0, "Axis ratio along second dim",
-                            0, NULL);
+                            0, NULL, 0);
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "AXISRATIO2", 0,
                             &p->q2[id], 0, "Axis ratio along third dim",
-                            0, NULL);
+                            0, NULL, 0);
     }
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "MAGNITUDE", 0,
                         &p->m[id], 0, "Magnitude of profile in catalog",
-                        0, NULL);
+                        0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "TRUNCATION", 0,
                         &p->t[id], 0, "Truncation of profile in catalog",
-                        0, NULL);
+                        0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_STRING, "RNGNAME", 0,
                         (void *)(p->rng_name), 0,
-                        "Name of random number generator", 0, NULL);
+                        "Name of random number generator", 0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_ULONG, "RNGSEED", 0,
                         &mkp->rng_seed, 0, "Seed of random number generator",
-                        0, NULL);
+                        0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_SIZE_T, "NUMRANDOM", 0,
                         &p->numrandom, 0,
-                        "Number of random points in central pixels", 0, NULL);
+                        "Number of random points in central pixels", 0,
+                        NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "TOLERANCE", 0,
                         &p->tolerance, 0,
                         "Tolerance level to stop random integration",
-                        0, NULL);
+                        0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_STRING, "MODE", 0,
                         p->mode==MKPROF_MODE_IMG?"img":"wcs", 0,
-                        "Coordinates in image or WCS units", 0, NULL);
+                        "Coordinates in image or WCS units", 0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_UINT8, "OVERSAMPLE", 0,
-                        &p->oversample, 0, "Oversampling factor", 0, NULL);
+                        &p->oversample, 0, "Oversampling factor", 0,
+                        NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_UINT8, "TUNITINP", 0,
                         &p->tunitinp, 0, "Truncation is in units of pixels, "
-                        "not radius", 0, NULL);
+                        "not radius", 0, NULL, 0);
   if( !isnan(p->zeropoint) )
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "ZEROPOINT", 0,
-                            &p->zeropoint, 0, "Zeropoint magnitude", 0, NULL);
+                            &p->zeropoint, 0, "Zeropoint magnitude", 0,
+                            NULL, 0);
   if( mkp->func==PROFILE_CIRCUMFERENCE )
       gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "CIRCUMWIDTH", 0,
                             &p->circumwidth, 0, "Width of circumference "
-                            "(inward) profiles", 0, NULL);
+                            "(inward) profiles", 0, NULL, 0);
   if( mkp->func==PROFILE_FLAT || mkp->func==PROFILE_CIRCUMFERENCE )
       gal_fits_key_list_add(&keys, GAL_TYPE_UINT8, "MFORFLATPIX", 0,
                             &p->mforflatpix, 0, "Magnitude is flat pixel "
-                            "value", 0, NULL);
+                            "value", 0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_UINT8, "MCOLISBRIGHTNESS", 0,
                         &p->mcolisbrightness, 0, "Catalog's magnitude is "
-                        "actually brightness", 0, NULL);
+                        "actually brightness", 0, NULL, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_UINT8, "MAGATPEAK", 0,
                         &p->magatpeak, 0, "Magnitude is for peak pixel, "
-                        "not full profile", 0, NULL);
+                        "not full profile", 0, NULL, 0);
 
   gal_fits_key_list_reverse(&keys);
   gal_fits_key_write_config(&keys, "Profile configuration", "PROFILE-CONFIG",
@@ -345,7 +348,7 @@ mkprof_build_single(struct mkonthread *mkp, long *fpixel_i, 
long *lpixel_i,
      overlapping region. */
   if(p->out)
     {
-      /* Note that `fpixel_i' and `lpixel_o' were in the un-oversampled
+      /* Note that 'fpixel_i' and 'lpixel_o' were in the un-oversampled
          image, they are also in the FITS coordinates. */
       for(i=0;i<ndim;++i)
         {
@@ -413,10 +416,10 @@ mkprof_add_built_to_write_queue(struct mkonthread *mkp,
       p->bq=ibq;
 
       /* If the list was empty when you locked the mutex, then either
-         `mkprof_write` is waiting behind a condition variable for you to
+         'mkprof_write' is waiting behind a condition variable for you to
          fill it up or not (either it hasn't got to setting the condition
          variable yet (this function locked the mutex before
-         `mkprof_write`) or it just got the list to be made and is busy
+         'mkprof_write') or it just got the list to be made and is busy
          writing the arrays in the output). In either case,
          pthread_cond_signal will work. */
       if((*fbq)->next==NULL)
@@ -457,13 +460,13 @@ mkprof_add_built_to_write_queue(struct mkonthread *mkp,
    About the Central x and y of each profile:
 
    The user has asked for the profile to be built on the coordinates
-   (real numbers) of `x` and `y` in an output image in the FITS
+   (real numbers) of 'x' and 'y' in an output image in the FITS
    format. We are building the full image for each galaxy separately
    in an array with an odd number of sides which maybe oversampled.
 
    In the FITS format, the pixel centers have an integer value. So for
    example in 1D, a pixel whose center value is 10.00 covers the area
-   of: [9.5,10.5). We want the fractional part of `x` (don't forget,
+   of: [9.5,10.5). We want the fractional part of 'x' (don't forget,
    this example is 1D) to be in the central pixel of this separate
    array (with odd sides) that we will be building.
 
@@ -489,19 +492,19 @@ mkprof_build(void *inparam)
   /* Make each profile that was specified for this thread. */
   for(i=0; mkp->indexs[i]!=GAL_BLANK_SIZE_T; ++i)
     {
-      /* Create a new builtqueue element with all the information. `fbq'
-         will be used when we want to add `ibq' to `p->bq'. It is defined
-         so we don't have to waste time traversing the `ibq'. Its
-         characteristic compared to the other elements of `ibq' is that
-         `fbq->next==NULL'. So to add ibq to p->bq, we just have to set
-         `fbq->next=p->bq' and then set `p->bq' to `ibq'.*/
+      /* Create a new builtqueue element with all the information. 'fbq'
+         will be used when we want to add 'ibq' to 'p->bq'. It is defined
+         so we don't have to waste time traversing the 'ibq'. Its
+         characteristic compared to the other elements of 'ibq' is that
+         'fbq->next==NULL'. So to add ibq to p->bq, we just have to set
+         'fbq->next=p->bq' and then set 'p->bq' to 'ibq'.*/
       builtqueue_addempty(&mkp->ibq);
       ibq=mkp->ibq;
       id=ibq->id=mkp->indexs[i];
       if(fbq==NULL) fbq=ibq;
 
 
-      /* Write the necessary parameters for this profile into `mkp'.*/
+      /* Write the necessary parameters for this profile into 'mkp'.*/
       oneprofile_set_prof_params(mkp);
 
 
@@ -528,7 +531,7 @@ mkprof_build(void *inparam)
 
           default:
             error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to "
-                  "address the issue. %zu is not recognized for `ndim'",
+                  "address the issue. %zu is not recognized for 'ndim'",
                   __func__, PACKAGE_BUGREPORT, ndim);
           }
 
@@ -629,7 +632,7 @@ mkprof_write(struct mkprofparams *p)
          array. */
       if(ibq->overlaps && out)
         GAL_TILE_PO_OISET(float,float,ibq->overlap_i,ibq->overlap_m,1,0, {
-            *o  = p->replace ? ( *i==0.0f ? *o : *i ) :  (*i + *o);
+            *o  = p->replace ? ( *i>*o ? *i : *o ) :  (*i + *o);
             sum += *i;
           });
 
@@ -693,8 +696,8 @@ mkprof_write(struct mkprofparams *p)
       if(!p->cp.quiet) gettimeofday(&t1, NULL);
 
       /* Write the final image into a FITS file with the requested
-         type. Until now, we were using `p->wcs' for the WCS, but from now
-         on, will put it in `out' to also free it while freeing `out'. */
+         type. Until now, we were using 'p->wcs' for the WCS, but from now
+         on, will put it in 'out' to also free it while freeing 'out'. */
       out->wcs=p->wcs;
       gal_fits_img_write_to_type(out, p->mergedimgname, NULL,
                                  PROGRAM_NAME, p->cp.type);
@@ -744,13 +747,14 @@ mkprof_write(struct mkprofparams *p)
 void
 mkprof(struct mkprofparams *p)
 {
-  int err;
-  char *tmp;
   pthread_t t;            /* Thread id not used, all are saved here. */
   pthread_attr_t attr;
   pthread_barrier_t b;
+  size_t numforprint=50;
   struct mkonthread *mkp;
+  char *tmp, *mmapname=NULL;
   gal_list_str_t *comments=NULL;
+  int err, origquiet=p->cp.quiet;
   size_t i, fi, *indexs, thrdcols;
   long *onaxes=NULL, os=p->oversample;
   size_t nb, ndim=p->ndim, nt=p->cp.numthreads;
@@ -761,17 +765,19 @@ mkprof(struct mkprofparams *p)
   errno=0;
   mkp=malloc(nt*sizeof *mkp);
   if(mkp==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `mkp'",
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'mkp'",
           __func__, (nt-1)*sizeof *mkp);
 
 
   /* Distribute the different profiles for different threads. Note
      that one thread is left out for writing, while nt-1 are left
      for building. */
-  gal_threads_dist_in_threads(p->num, nt, &indexs, &thrdcols);
+  mmapname=gal_threads_dist_in_threads(p->num, nt,
+                                       p->cp.minmapsize, p->cp.quietmmap,
+                                       &indexs, &thrdcols);
 
 
-  /* `onaxes' are size of the merged output image without over-sampling or
+  /* 'onaxes' are size of the merged output image without over-sampling or
      shifting in FITS order. When no output merged image is needed, we can
      ignore it. */
   if(p->out)
@@ -829,8 +835,27 @@ mkprof(struct mkprofparams *p)
     }
 
 
-  /* Write the created arrays into the image. */
+  /* If there are too many profiles, don't print the fact that a profile
+     has been built. */
+  if(p->num>numforprint)
+    {
+      /* Let the user know that building is ongoing. */
+      if(p->cp.quiet==0)
+        printf("  ---- Building %zu profiles... ", p->num);
+
+      /* Disable the quiet flag.*/
+      p->cp.quiet=1;
+    }
+
+
+  /* Write the created arrays into the image. Set the original quiet flag
+     and let the user know that its done. */
   mkprof_write(p);
+  if(p->num>numforprint)
+    {
+      p->cp.quiet=origquiet;
+      if(p->cp.quiet==0) printf("done.\n");
+    }
 
 
   /* Write the log file. */
@@ -857,9 +882,13 @@ mkprof(struct mkprofparams *p)
       pthread_mutex_destroy(&p->qlock);
     }
 
+  /* If a merged image was created, let the user know.... */
+  if(p->mergedimgname)
+    printf("  -- Output: %s\n", p->mergedimgname);
 
   /* Clean up. */
-  free(mkp);
-  free(indexs);
+  if(mmapname) gal_pointer_mmap_free(&mmapname, p->cp.quietmmap);
+  else         free(indexs);
   if(onaxes) free(onaxes);
+  free(mkp);
 }
diff --git a/bin/mkprof/mkprof.h b/bin/mkprof/mkprof.h
index 8ddfe01..8411b10 100644
--- a/bin/mkprof/mkprof.h
+++ b/bin/mkprof/mkprof.h
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/mkprof/oneprofile.c b/bin/mkprof/oneprofile.c
index 0aacb3c..caa3140 100644
--- a/bin/mkprof/oneprofile.c
+++ b/bin/mkprof/oneprofile.c
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -55,7 +55,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
  **************          Radial distance       ******************
  ****************************************************************/
 /* Set the center position of the profile in the oversampled image. Note
-   that `mkp->width' is in the non-oversampled scale. IMPORTANT: the
+   that 'mkp->width' is in the non-oversampled scale. IMPORTANT: the
    ordering is in FITS coordinate order. */
 static void
 oneprofile_center_oversampled(struct mkonthread *mkp)
@@ -92,7 +92,7 @@ oneprofile_set_coord(struct mkonthread *mkp, size_t index)
   gal_dimension_index_to_coord(index, ndim, dsize, coord_c);
 
   /* Convert these coordinates to one where the profile center is at the
-     center and the image is not over-sampled. Note that only `coord_c' is
+     center and the image is not over-sampled. Note that only 'coord_c' is
      in C order.*/
   for(i=0;i<ndim;++i)
     mkp->coord[i] = ( coord_c[ndim-i-1] - mkp->center[i] )/os;
@@ -135,7 +135,7 @@ oneprofile_r_el(struct mkonthread *mkp)
     default:
       error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
             "the problem. The value %zu is not recognized for "
-            "`mkp->p->ndim'", __func__, PACKAGE_BUGREPORT, mkp->p->ndim);
+            "'mkp->p->ndim'", __func__, PACKAGE_BUGREPORT, mkp->p->ndim);
     }
 }
 
@@ -218,7 +218,7 @@ oneprofile_randompoints(struct mkonthread *mkp)
      in checks), but since it has a very negligible cost (compared to the
      random checks above) cost, its good to reset it to help in debugging
      when necessary (avoid confusion when un-commenting the checks in
-     `oneprofile_pix_by_pix'). */
+     'oneprofile_pix_by_pix'). */
   mkp->r=r_before;
   mkp->coord[0]=coord_before[0];
   mkp->coord[1]=coord_before[1];
@@ -325,7 +325,7 @@ integ2d(struct mkonthread *mkp)
 /************       Pixel by pixel building       *************/
 /*********        Positions are in C not FITS         *********/
 /**************************************************************/
-/* `oneprofile_center_oversampled' stored the center of the profile in
+/* 'oneprofile_center_oversampled' stored the center of the profile in
    floating point coordinates. This function will convert that into a
    pixel index. */
 static size_t
@@ -335,9 +335,9 @@ oneprofile_center_pix_index(struct mkonthread *mkp)
   size_t *dsize=mkp->ibq->image->dsize;
   size_t i, coord[3], ndim=mkp->p->ndim;
 
-  /* Find the coordinates of the center point. Note `mkp->center' is in
+  /* Find the coordinates of the center point. Note 'mkp->center' is in
      FITS coordinates, while coord must be in C coordinates (to be used in
-     `gal_dimension_coord_to_index'). */
+     'gal_dimension_coord_to_index'). */
   for(i=0;i<ndim;++i)
     {
       pixfrac = modf(mkp->center[i], &intpart);
@@ -379,7 +379,7 @@ oneprofile_pix_by_pix(struct mkonthread *mkp)
   if(mkp->func==PROFILE_POINT)
     { array[p]=1; return; }
 
-  /* Allocate the `byt' array. It is used as a flag to make sure that we
+  /* Allocate the 'byt' array. It is used as a flag to make sure that we
      don't re-calculate the profile value on a pixel more than once. */
   byt = gal_pointer_allocate(GAL_TYPE_UINT8,
                              gal_dimension_total_size(ndim, dsize), 1,
@@ -585,8 +585,8 @@ oneprofile_set_prof_params(struct mkonthread *mkp)
 
     default:
       error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
-            "address the problem. The value `%zu' is not recognized for "
-            "`ndim'", __func__, PACKAGE_BUGREPORT, ndim);
+            "address the problem. The value '%zu' is not recognized for "
+            "'ndim'", __func__, PACKAGE_BUGREPORT, ndim);
     }
 
 
@@ -694,6 +694,14 @@ oneprofile_set_prof_params(struct mkonthread *mkp)
 
 
 
+    case PROFILE_CUSTOM:
+      mkp->profile          = profiles_custom_table;
+      mkp->truncr           = tp ? p->t[id] : p->t[id]*p->r[id];
+      mkp->correction       = 0;
+      break;
+
+
+
     default:
       error(EXIT_FAILURE, 0, "%s: a bug! Please contact us so we can "
             "correct this problem. The profile code %u is not recognized.",
@@ -772,8 +780,8 @@ oneprofile_make(struct mkonthread *mkp)
       mkp->ibq->accufrac /= sum;
 
       /* Correct all the profile pixels. Note that ideally, if a user wants
-         a NaN valued profile, they should use the `flat' profile with
-         `--mforflatpix', which won't need this correction. However, it
+         a NaN valued profile, they should use the 'flat' profile with
+         '--mforflatpix', which won't need this correction. However, it
          might happen that they forget the later, or the catalog might be
          generated by a script that gives a NaN value for the magnitude
          with any kind of profile. In such cases if we don't check the NaN
diff --git a/bin/mkprof/oneprofile.h b/bin/mkprof/oneprofile.h
index e38b61e..eb83697 100644
--- a/bin/mkprof/oneprofile.h
+++ b/bin/mkprof/oneprofile.h
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/mkprof/profiles.c b/bin/mkprof/profiles.c
index f838eb9..ebd69ac 100644
--- a/bin/mkprof/profiles.c
+++ b/bin/mkprof/profiles.c
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -41,7 +41,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /****************************************************************
  *****************         Profiles:         ********************
  ****************************************************************/
-/* The Gaussian function at a point. */
+/* The distance of this pixel. */
 double
 profiles_radial_distance(struct mkonthread *mkp)
 {
@@ -52,6 +52,42 @@ profiles_radial_distance(struct mkonthread *mkp)
 
 
 
+/* Read the values based on the distance from a table. */
+double
+profiles_custom_table(struct mkonthread *mkp)
+{
+  double out;
+  long i;  /* May become negative. */
+  double *reg=mkp->p->customregular;
+  double *min=mkp->p->custom->array;
+  double *max=mkp->p->custom->next->array;
+  double *value=mkp->p->custom->next->next->array;
+
+  /* If the table isn't regular ('reg[0]' isn't NaN), then we have to parse
+     over the whole table. However, if its regular, we can find the proper
+     value much more easily. */
+  if( isnan(reg[0]) )
+    {
+      out=0;
+      for(i=0;i<mkp->p->custom->size;++i)
+        if( mkp->r >= min[i] && mkp->r < max[i] )
+          { out=value[i]; break; }
+    }
+  else
+    {
+      i=(mkp->r - reg[0])/reg[1];
+      if(i<0 || i>mkp->p->custom->size) out=0;
+      else                                   out=value[i];
+    }
+
+  /* Return the output value. */
+  return isnan(out) ? 0 : out;
+}
+
+
+
+
+
 /* The integral of the Gaussian from -inf to +inf equals the square root of
    PI. So from zero to +inf it equals half of that.*/
 double
diff --git a/bin/mkprof/profiles.h b/bin/mkprof/profiles.h
index 4ba0916..72c7b9b 100644
--- a/bin/mkprof/profiles.h
+++ b/bin/mkprof/profiles.h
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -27,6 +27,9 @@ double
 profiles_radial_distance(struct mkonthread *mkp);
 
 double
+profiles_custom_table(struct mkonthread *mkp);
+
+double
 profiles_gaussian_total(double q);
 
 double
diff --git a/bin/mkprof/ui.c b/bin/mkprof/ui.c
index ce000ab..5e1ad36 100644
--- a/bin/mkprof/ui.c
+++ b/bin/mkprof/ui.c
@@ -5,7 +5,7 @@ Arithmetic is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -124,17 +124,20 @@ ui_profile_name_read(char *string, size_t row)
   else if ( !strcmp("distance", string) )
     return PROFILE_DISTANCE;
 
+  else if ( !strcmp("custom", string) )
+    return PROFILE_CUSTOM;
+
   else if ( !strcmp(GAL_BLANK_STRING, string) )
     error(EXIT_FAILURE, 0, "atleast one profile function is blank");
 
   else
     {
       if(row)
-        error(EXIT_FAILURE, 0, "`%s' not recognized as a profile function "
+        error(EXIT_FAILURE, 0, "'%s' not recognized as a profile function "
               "name in row %zu", string, row);
       else
-        error(EXIT_FAILURE, 0, "`%s' not recognized as a profile function "
-              "name in values to `--kernel' option", string);
+        error(EXIT_FAILURE, 0, "'%s' not recognized as a profile function "
+              "name in values to '--kernel' option", string);
     }
 
   return PROFILE_INVALID;
@@ -187,6 +190,9 @@ ui_initialize_options(struct mkprofparams *p,
   cp->numthreads         = gal_threads_number();
   cp->coptions           = gal_commonopts_options;
 
+  p->customregular[0]    = NAN;
+  p->customregular[1]    = NAN;
+
   /* Default program parameters. */
   p->zeropoint           = NAN;
   p->cp.type             = GAL_TYPE_FLOAT32;
@@ -232,18 +238,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct mkprofparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -326,17 +332,30 @@ ui_parse_kernel(struct argp_option *option, char *arg,
     }
   else
     {
-      /* The first part of `arg' (before the first comma) is not
+      /* If the kernel has already been given, ignore it (the previously
+         read value has higher precedence). */
+      if( *(gal_data_t **)(option->value) ) return NULL;
+
+      /* The first part of 'arg' (before the first comma) is not
          necessarily a number. So we need to separate the first part from
          the rest.*/
       c=arg;while(*c!='\0' && *c!=',') ++c;
       profile=arg;
-      arg = (*c=='\0') ? NULL : c+1;  /* the `point' profile doesn't need */
+      arg = (*c=='\0') ? NULL : c+1;  /* the 'point' profile doesn't need */
       *c='\0';                        /* any numbers.                     */
 
+      /* Make sure something exists after the name of the profile. */
+      if(arg==NULL)
+        error(EXIT_FAILURE, 0, "the kernel option value couldn't be "
+              "parsed in the expected format: one name (of a profile), "
+              "followed by some numbers defining that profile. See the "
+              "description of '--kernel' in the manual (with the 'info "
+              "astmkprof' command) for the meaning of the numbers");
 
       /* Read the parameters. */
       kernel=gal_options_parse_list_of_numbers(arg, filename, lineno);
+
+      /* Put the kernel dataset into the main program structure. */
       *(gal_data_t **)(option->value) = kernel;
 
 
@@ -345,13 +364,13 @@ ui_parse_kernel(struct argp_option *option, char *arg,
       for(i=0;i<kernel->size;++i)
         if(darray[i]<=0)
           error(EXIT_FAILURE, 0, "value number %zu (%g) in the given list "
-                "of kernel parameters (`%s') is not acceptable. All "
-                "parameters to the `--kernel' option must be non-zero and "
+                "of kernel parameters ('%s') is not acceptable. All "
+                "parameters to the '--kernel' option must be non-zero and "
                 "positive", i+1, darray[i], arg);
 
 
       /* See if a 2D kernel is requested or a 3D kernel and keep the value
-         in `kernel->flag'. If no dimensionality is defined, then by
+         in 'kernel->flag'. If no dimensionality is defined, then by
          default, we'll assume it is 2D.*/
       c=profile;while(*c!='\0' && *c!='-') ++c;
       if(*c=='\0')
@@ -361,9 +380,9 @@ ui_parse_kernel(struct argp_option *option, char *arg,
           *c='\0';
           dstr=c+1;
           if( (dstr[1]!='d' && dstr[1]!='D') || dstr[2]!='\0')
-            error(EXIT_FAILURE, 0, "bad formatting in `--kernel' "
+            error(EXIT_FAILURE, 0, "bad formatting in '--kernel' "
                   "dimensionality. The dimensionality suffix must be either "
-                  "2d, 3d (not case sensitive). You have given `%s'", dstr);
+                  "2d, 3d (not case sensitive). You have given '%s'", dstr);
           switch(dstr[0])
             {
             case '2': kernel->flag=2; break;
@@ -376,21 +395,21 @@ ui_parse_kernel(struct argp_option *option, char *arg,
         }
 
 
-      /* Write the profile type code into `kernel->status'. If it starts
+      /* Write the profile type code into 'kernel->status'. If it starts
          with a digit, then the user might have given the code of the
          profile directly. In that case, parse the number. Otherwise,
-         let `ui_profile_name_read' find the value. */
+         let 'ui_profile_name_read' find the value. */
       if( isdigit(*profile) )
         {
           profcode=strtol(profile, &tailptr, 0);
           if(*tailptr!='\0')
-            error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' "
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' "
                           "couldn't be read as a profile code", profile);
           if(profcode<=0 || profcode>=PROFILE_MAXIMUM_CODE)
-            error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' "
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' "
                           "isn't a valid profile code. Please run with "
-                          "`--help' and see the acceptable codes in "
-                          "explanation of the `--fcol' option", profile);
+                          "'--help' and see the acceptable codes in "
+                          "explanation of the '--fcol' option", profile);
           kernel->status=profcode;
         }
       else
@@ -420,8 +439,8 @@ ui_parse_kernel(struct argp_option *option, char *arg,
          are needed. */
       if( kernel->size != need )
         error_at_line(EXIT_FAILURE, 0, filename, lineno, "as a %uD kernel, "
-                      "a `%s' profile needs %zu parameters, but %zu "
-                      "parameter%s given to `--kernel'", kernel->flag,
+                      "a '%s' profile needs %zu parameters, but %zu "
+                      "parameter%s given to '--kernel'", kernel->flag,
                       ui_profile_name_write(kernel->status), need,
                       kernel->size, kernel->size>1?"s are":" is");
 
@@ -456,9 +475,9 @@ ui_parse_coordinate_mode(struct argp_option *option, char 
*arg,
       else if (!strcmp(arg, "wcs"))
         *(uint8_t *)(option->value)=MKPROF_MODE_WCS;
       else
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' (value to "
-                      "`--mode') not recognized as a coordinate standard "
-                      "mode. Recognized values are `img' and `wcs'. This "
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
+                      "'--mode') not recognized as a coordinate standard "
+                      "mode. Recognized values are 'img' and 'wcs'. This "
                       "option is necessary to identify the nature of your "
                       "input coordinates", arg);
       return NULL;
@@ -487,7 +506,7 @@ ui_parse_coordinate_mode(struct argp_option *option, char 
*arg,
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct mkprofparams *p)
 {
@@ -495,7 +514,7 @@ ui_read_check_only_options(struct mkprofparams *p)
 
   /* When a no-merged image is to be created, type is necessary. */
   if( p->cp.type==GAL_TYPE_INVALID && p->nomerged==0)
-    error(EXIT_FAILURE, 0, "an output type `--type' is necessary when a "
+    error(EXIT_FAILURE, 0, "an output type '--type' is necessary when a "
           "merged image is to be built.");
 
   /* Check if one of the coordinate columns has been given, the other is
@@ -507,26 +526,26 @@ ui_read_check_only_options(struct mkprofparams *p)
   if(p->kernel==NULL)
     {
       if(p->mode==0)
-        error(EXIT_FAILURE, 0, "the `--mode' option is necessary when "
+        error(EXIT_FAILURE, 0, "the '--mode' option is necessary when "
               "building profiles from a catalog. It can take two values: "
-              "`img' or `wcs' which specify how to interpret the "
+              "'img' or 'wcs' which specify how to interpret the "
               "coordinate columns");
     }
 
-  /* The zeropoint magnitude is only necessary when `mcolisbrightness' is
+  /* The zeropoint magnitude is only necessary when 'mcolisbrightness' is
      not called.  */
   if( p->mcolisbrightness==0 && isnan(p->zeropoint) )
     error(EXIT_FAILURE, 0, "no zeropoint magnitude given. A zeropoint "
-          "magnitude is necessary when `--mcolisbrightness' is not called "
-          "(i.e., when the contents of `--mcol' must be interpretted as "
+          "magnitude is necessary when '--mcolisbrightness' is not called "
+          "(i.e., when the contents of '--mcol' must be interpretted as "
           "a magnitude, not brightness).");
 
-  /* Make sure no zero value is given for the `--mergedsize' option (only
+  /* Make sure no zero value is given for the '--mergedsize' option (only
      when it is necessary). */
   if(p->dsize && p->backname==NULL)
     for(i=0;p->dsize[i]!=GAL_BLANK_SIZE_T;++i)
       if(p->dsize[i]==0)
-        error(EXIT_FAILURE, 0, "values to `--mergedsize' option must not "
+        error(EXIT_FAILURE, 0, "values to '--mergedsize' option must not "
               "be zero");
 }
 
@@ -535,7 +554,7 @@ ui_read_check_only_options(struct mkprofparams *p)
 
 
 /* Sanity check on options AND arguments. If only option values are to be
-   checked, use `ui_read_check_only_options'. */
+   checked, use 'ui_read_check_only_options'. */
 static void
 ui_check_options_and_arguments(struct mkprofparams *p)
 {
@@ -544,14 +563,14 @@ ui_check_options_and_arguments(struct mkprofparams *p)
 
   /* If no kernel is given, make sure an input catalog is given, and if it
      is FITS, that the HDU is also provided. When a kernel option, we will
-     set a fiducial catalog name called `kernel.txt' to automatic output
+     set a fiducial catalog name called 'kernel.txt' to automatic output
      filename generation. */
   if(p->kernel)
     {
       if(p->catname)
-        error(EXIT_FAILURE, 0, "`--kernel' cannot be called with an input "
-              "catalog (`%s'). The parameters necessary to build a single "
-              "kernel output should be given to `--kernel', not in a "
+        error(EXIT_FAILURE, 0, "'--kernel' cannot be called with an input "
+              "catalog ('%s'). The parameters necessary to build a single "
+              "kernel output should be given to '--kernel', not in a "
               "catalog", p->catname);
       p->catname="kernel.option";
     }
@@ -560,7 +579,7 @@ ui_check_options_and_arguments(struct mkprofparams *p)
       if(p->catname)
         {
           if( gal_fits_name_is_fits(p->catname) && p->cp.hdu==NULL)
-            error(EXIT_FAILURE, 0, "no `hdu' specified for the input FITS "
+            error(EXIT_FAILURE, 0, "no 'hdu' specified for the input FITS "
                   "table '%s', to ", p->catname);
         }
     }
@@ -594,7 +613,7 @@ ui_check_options_and_arguments(struct mkprofparams *p)
   p->basename=gal_checkset_not_dir_part(p->mergedimgname);
 
 
-  /* If a merged image is requested (or `--kernel' the option is called),
+  /* If a merged image is requested (or '--kernel' the option is called),
      then delete the final filename if it exists. */
   if(p->nomerged==0 && p->kernel)
     gal_checkset_writable_remove(p->mergedimgname, p->cp.keep,
@@ -662,8 +681,8 @@ ui_read_cols_2d(struct mkprofparams *p)
 
   /* The name of the input catalog is only for informative steps from now
      on (we won't be dealing with the actual file any more). So if the
-     standard input was used (therefore `catname==NULL', set it to
-     `stdin'). */
+     standard input was used (therefore 'catname==NULL', set it to
+     'stdin'). */
   if(p->catname==NULL)
     gal_checkset_allocate_copy("standard-input", &p->catname);
 
@@ -686,8 +705,8 @@ ui_read_cols_2d(struct mkprofparams *p)
         case 1:
         case 2:
           colname = ( counter==1
-                      ? "first coordinate column (`--coordcol')"
-                      : "second coordinate column (`--coordcol')" );
+                      ? "first coordinate column ('--coordcol')"
+                      : "second coordinate column ('--coordcol')" );
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT64);
           switch(counter)
             {
@@ -711,7 +730,7 @@ ui_read_cols_2d(struct mkprofparams *p)
           else
             {
               /* Read the user's profile codes. */
-              colname="profile function code (`fcol')";
+              colname="profile function code ('fcol')";
               corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_UINT8);
               p->f=corrtype->array;
 
@@ -720,12 +739,12 @@ ui_read_cols_2d(struct mkprofparams *p)
                 if(p->f[i]<=PROFILE_INVALID || p->f[i]>=PROFILE_MAXIMUM_CODE)
                   error(EXIT_FAILURE, 0, "%s: row %zu, the function "
                         "code is %u. It should be >%d and <%d. Please run "
-                        "again with `--help' and check the acceptable "
+                        "again with '--help' and check the acceptable "
                         "codes.\n\nAlternatively, you can use alphabetic "
                         "strings to specify the profile functions, see the "
-                        "explanations under `fcol' from the command "
-                        "below (press the `SPACE' key to go down, and the "
-                        "`q' to return back to the command-line):\n\n"
+                        "explanations under 'fcol' from the command "
+                        "below (press the 'SPACE' key to go down, and the "
+                        "'q' to return back to the command-line):\n\n"
                         "    $ info %s\n", p->catname, i+1, p->f[i],
                         PROFILE_INVALID, PROFILE_MAXIMUM_CODE, PROGRAM_EXEC);
             }
@@ -733,7 +752,7 @@ ui_read_cols_2d(struct mkprofparams *p)
 
 
         case 4:
-          colname="radius (`rcol')";
+          colname="radius ('rcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->r=corrtype->array;
 
@@ -741,28 +760,28 @@ ui_read_cols_2d(struct mkprofparams *p)
           for(i=0;i<p->num;++i)
             if(p->f[i]!=PROFILE_POINT && p->r[i]<=0.0f)
               error(EXIT_FAILURE, 0, "%s: row %zu, the radius value %g is "
-                    "not acceptable for a `%s' profile. It has to be larger "
+                    "not acceptable for a '%s' profile. It has to be larger "
                     "than 0", p->catname, i+1, p->r[i],
                     ui_profile_name_write(p->f[i]));
           break;
 
 
         case 5:
-          colname="index (`ncol')";
+          colname="index ('ncol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->n=corrtype->array;
           break;
 
 
         case 6:
-          colname="position angle (`pcol')";
+          colname="position angle ('pcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->p1=corrtype->array;
           break;
 
 
         case 7:
-          colname="axis ratio (`qcol')";
+          colname="axis ratio ('qcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->q1=corrtype->array;
 
@@ -770,14 +789,14 @@ ui_read_cols_2d(struct mkprofparams *p)
           for(i=0;i<p->num;++i)
             if( p->f[i]!=PROFILE_POINT && (p->q1[i]<=0.0f || p->q1[i]>1.0f) )
               error(EXIT_FAILURE, 0, "%s: row %zu, the axis ratio value %g "
-                    "is not acceptable for a `%s' profile. It has to be >0 "
+                    "is not acceptable for a '%s' profile. It has to be >0 "
                     "and <=1", p->catname, i+1, p->q1[i],
                     ui_profile_name_write(p->f[i]));
           break;
 
 
         case 8:
-          colname="magnitude (`mcol')";
+          colname="magnitude ('mcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->m=corrtype->array;
           checkblank=0;          /* Magnitude can be NaN: to mask regions. */
@@ -785,7 +804,7 @@ ui_read_cols_2d(struct mkprofparams *p)
 
 
         case 9:
-          colname="truncation (`tcol')";
+          colname="truncation ('tcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->t=corrtype->array;
 
@@ -793,7 +812,7 @@ ui_read_cols_2d(struct mkprofparams *p)
           for(i=0;i<p->num;++i)
             if(p->f[i]!=PROFILE_POINT && p->t[i]<=0.0f)
               error(EXIT_FAILURE, 0, "%s: row %zu, the truncation radius "
-                    "value %g is not acceptable for a `%s' profile. It has "
+                    "value %g is not acceptable for a '%s' profile. It has "
                     "to be larger than 0", p->catname, i+1, p->t[i],
                     ui_profile_name_write(p->f[i]));
           break;
@@ -805,11 +824,11 @@ ui_read_cols_2d(struct mkprofparams *p)
           gal_tableintern_error_col_selection(p->catname, p->cp.hdu, "too "
                                               "many columns were selected "
                                               "by the given values to the "
-                                              "options ending in `col'.");
+                                              "options ending in 'col'.");
         }
 
       /* Sanity check and clean up.  Note that it might happen that the
-         input structure is already freed. In that case, `corrtype' will be
+         input structure is already freed. In that case, 'corrtype' will be
          NULL. */
       if(corrtype)
         {
@@ -819,7 +838,7 @@ ui_read_cols_2d(struct mkprofparams *p)
                   "Input columns cannot contain blank values", colname);
 
           /* Free the unnecessary sturcture information. The correct-type
-             (`corrtype') data structure's array is necessary for later
+             ('corrtype') data structure's array is necessary for later
              steps, so its pointer has been copied in the main program's
              structure. Hence, we should set the structure's pointer to
              NULL so the important data isn't freed.*/
@@ -838,12 +857,12 @@ ui_read_cols_2d(struct mkprofparams *p)
         {
           error(0, 0, "WARNING: atleast one single-valued profile (point, "
                 "flat, or circumference profiles) has a magnitude column "
-                "value of 0.0 while `--mforflatpix' or "
-                "`--mcolforbrightness' have also been given. In such cases "
+                "value of 0.0 while '--mforflatpix' or "
+                "'--mcolforbrightness' have also been given. In such cases "
                 "the profile's pixels will have a value of zero and thus "
                 "they will not be identifiable from the zero-valued "
                 "background. If this behavior is intended, this warning "
-                "can be suppressed with the `--quiet' (or `-q') option.\n");
+                "can be suppressed with the '--quiet' (or '-q') option.\n");
           break;
         }
 }
@@ -862,11 +881,11 @@ ui_read_cols_3d(struct mkprofparams *p)
   gal_data_t *cols, *tmp, *corrtype=NULL;
   gal_list_str_t *lines, *ccol, *colstrs=NULL;
 
-  /* The 3D-specific columns are not mandatory in `args.h', so we need to
+  /* The 3D-specific columns are not mandatory in 'args.h', so we need to
      check here if they are given or not before starting to read them. */
   if(p->p2col==NULL || p->p3col==NULL || p->q2col==NULL)
-    error(EXIT_FAILURE, 0, "at least one of `--p2col', `--p3col', or "
-          "`--q2col' have not been identified. When building a 3D profile, "
+    error(EXIT_FAILURE, 0, "at least one of '--p2col', '--p3col', or "
+          "'--q2col' have not been identified. When building a 3D profile, "
           "these three columns are also mandatory");
 
   /* The coordinate columns are a linked list of strings. */
@@ -920,10 +939,10 @@ ui_read_cols_3d(struct mkprofparams *p)
         case 2:
         case 3:
           colname = ( counter==1
-                      ? "first coordinate column (`--coordcol')"
+                      ? "first coordinate column ('--coordcol')"
                       : ( counter==2
-                          ? "second coordinate column (`--coordcol')"
-                          : "third coordinate column (`--coordcol')" ) );
+                          ? "second coordinate column ('--coordcol')"
+                          : "third coordinate column ('--coordcol')" ) );
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT64);
           switch(counter)
             {
@@ -947,7 +966,7 @@ ui_read_cols_3d(struct mkprofparams *p)
           else
             {
               /* Read the user's profile codes. */
-              colname="profile function code (`fcol')";
+              colname="profile function code ('fcol')";
               corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_UINT8);
               p->f=corrtype->array;
 
@@ -956,19 +975,19 @@ ui_read_cols_3d(struct mkprofparams *p)
                 if(p->f[i]<=PROFILE_INVALID || p->f[i]>=PROFILE_MAXIMUM_CODE)
                   error(EXIT_FAILURE, 0, "%s: row %zu, the function "
                         "code is %u. It should be >%d and <%d. Please run "
-                        "again with `--help' and check the acceptable "
+                        "again with '--help' and check the acceptable "
                         "codes.\n\nAlternatively, you can use alphabetic "
                         "strings to specify the profile functions, see the "
-                        "explanations under `fcol' from the command "
-                        "below (press the `SPACE' key to go down, and the "
-                        "`q' to return back to the command-line):\n\n"
+                        "explanations under 'fcol' from the command "
+                        "below (press the 'SPACE' key to go down, and the "
+                        "'q' to return back to the command-line):\n\n"
                         "    $ info %s\n", p->catname, i+1, p->f[i],
                         PROFILE_INVALID, PROFILE_MAXIMUM_CODE, PROGRAM_EXEC);
             }
           break;
 
         case 5:
-          colname="radius (`rcol')";
+          colname="radius ('rcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->r=corrtype->array;
 
@@ -976,37 +995,37 @@ ui_read_cols_3d(struct mkprofparams *p)
           for(i=0;i<p->num;++i)
             if(p->f[i]!=PROFILE_POINT && p->r[i]<=0.0f)
               error(EXIT_FAILURE, 0, "%s: row %zu, the radius value %g is "
-                    "not acceptable for a `%s' profile. It has to be larger "
+                    "not acceptable for a '%s' profile. It has to be larger "
                     "than 0", p->catname, i+1, p->r[i],
                     ui_profile_name_write(p->f[i]));
           break;
 
         case 6:
-          colname="index (`ncol')";
+          colname="index ('ncol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->n=corrtype->array;
           break;
 
         case 7:
-          colname="first euler angle (`pcol')";
+          colname="first euler angle ('pcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->p1=corrtype->array;
           break;
 
         case 8:
-          colname="second euler angle (`p2col')";
+          colname="second euler angle ('p2col')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->p2=corrtype->array;
           break;
 
         case 9:
-          colname="third euler angle (`p3col')";
+          colname="third euler angle ('p3col')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->p3=corrtype->array;
           break;
 
         case 10:
-          colname="axis ratio 1 (`qcol')";
+          colname="axis ratio 1 ('qcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->q1=corrtype->array;
 
@@ -1014,13 +1033,13 @@ ui_read_cols_3d(struct mkprofparams *p)
           for(i=0;i<p->num;++i)
             if( p->f[i]!=PROFILE_POINT && (p->q1[i]<=0.0f || p->q1[i]>1.0f) )
               error(EXIT_FAILURE, 0, "%s: row %zu, the first axis ratio "
-                    "value %g is not acceptable for a `%s' profile. It has "
+                    "value %g is not acceptable for a '%s' profile. It has "
                     "to be >0 and <=1", p->catname, i+1, p->q1[i],
                     ui_profile_name_write(p->f[i]));
           break;
 
         case 11:
-          colname="axis ratio 2 (`q2col')";
+          colname="axis ratio 2 ('q2col')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->q2=corrtype->array;
 
@@ -1028,20 +1047,20 @@ ui_read_cols_3d(struct mkprofparams *p)
           for(i=0;i<p->num;++i)
             if( p->f[i]!=PROFILE_POINT && (p->q2[i]<=0.0f || p->q2[i]>1.0f) )
               error(EXIT_FAILURE, 0, "%s: row %zu, the second axis ratio "
-                    "value %g is not acceptable for a `%s' profile. It has "
+                    "value %g is not acceptable for a '%s' profile. It has "
                     "to be >0 and <=1", p->catname, i+1, p->q2[i],
                     ui_profile_name_write(p->f[i]));
           break;
 
         case 12:
-          colname="magnitude (`mcol')";
+          colname="magnitude ('mcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->m=corrtype->array;
           checkblank=0;          /* Magnitude can be NaN: to mask regions. */
           break;
 
         case 13:
-          colname="truncation (`tcol')";
+          colname="truncation ('tcol')";
           corrtype=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT32);
           p->t=corrtype->array;
 
@@ -1049,7 +1068,7 @@ ui_read_cols_3d(struct mkprofparams *p)
           for(i=0;i<p->num;++i)
             if(p->f[i]!=PROFILE_POINT && p->t[i]<=0.0f)
               error(EXIT_FAILURE, 0, "%s: row %zu, the truncation radius "
-                    "value %g is not acceptable for a `%s' profile. It has "
+                    "value %g is not acceptable for a '%s' profile. It has "
                     "to be larger than 0", p->catname, i+1, p->t[i],
                     ui_profile_name_write(p->f[i]));
           break;
@@ -1060,11 +1079,11 @@ ui_read_cols_3d(struct mkprofparams *p)
           gal_tableintern_error_col_selection(p->catname, p->cp.hdu, "too "
                                               "many columns were selected "
                                               "by the given values to the "
-                                              "options ending in `col'.");
+                                              "options ending in 'col'.");
         }
 
       /* Sanity check and clean up.  Note that it might happen that the
-         input structure is already freed. In that case, `corrtype' will be
+         input structure is already freed. In that case, 'corrtype' will be
          NULL. */
       if(corrtype)
         {
@@ -1074,7 +1093,7 @@ ui_read_cols_3d(struct mkprofparams *p)
                   "Input columns cannot contain blank values", colname);
 
           /* Free the unnecessary sturcture information. The correct-type
-             (`corrtype') data structure's array is necessary for later
+             ('corrtype') data structure's array is necessary for later
              steps, so its pointer has been copied in the main program's
              structure. Hence, we should set the structure's pointer to
              NULL so the important data isn't freed.*/
@@ -1089,10 +1108,11 @@ ui_read_cols_3d(struct mkprofparams *p)
 
 
 /* It is possible to define the internal catalog through a catalog or the
-   `--kernel' option. This function will do the job. */
+   '--kernel' option. This function will do the job. */
 static void
 ui_prepare_columns(struct mkprofparams *p)
 {
+  size_t i;
   double *karr;
   float r, n, t, q2;
 
@@ -1161,7 +1181,7 @@ ui_prepare_columns(struct mkprofparams *p)
             }
 
           /* 3rd-dim axis ratio <=1: No extra rotation is necessary and
-             `q2'can simply be put in the respective column. */
+             'q2'can simply be put in the respective column. */
           else
             {
               p->q2[0] = q2;
@@ -1173,11 +1193,11 @@ ui_prepare_columns(struct mkprofparams *p)
     {
       /* Make sure the number of coordinate columns and number of
          dimensions in outputs are the same. There is no problem if it is
-         more than `ndim'. In that case, the last values (possibly in
+         more than 'ndim'. In that case, the last values (possibly in
          configuration files) will be ignored. */
       if( gal_list_str_number(p->ccol) < p->ndim )
         error(EXIT_FAILURE, 0, "%zu coordinate columns (calls to "
-              "`--coordcol') given but output has %zu dimensions",
+              "'--coordcol') given but output has %zu dimensions",
               gal_list_str_number(p->ccol), p->ndim);
 
       /* Call the respective function. */
@@ -1187,10 +1207,25 @@ ui_prepare_columns(struct mkprofparams *p)
         case 3: ui_read_cols_3d(p);   break;
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
-                "resolve the issue. %zu not recognized for `p->ndim'",
+                "resolve the issue. %zu not recognized for 'p->ndim'",
                 __func__, PACKAGE_BUGREPORT, p->ndim);
         }
     }
+
+  /* If a custom profile is requested, make sure that a custom file is
+     given. */
+  for(i=0;i<p->num;++i)
+    if(p->f[i]==PROFILE_CUSTOM)
+      {
+        if(p->customname==NULL)
+          error(EXIT_FAILURE, 0, "at least one custom profile requested "
+                "(first occurrence in row %zu), but no file/table was given "
+                "to the '--customtable' option. See the description of "
+                "this '--customtable' for more information on the "
+                "desired format", i+1);
+        break;
+      }
+
 }
 
 
@@ -1209,7 +1244,7 @@ ui_wcs_sanity_check(struct mkprofparams *p)
   if(p->crpix)
     {
       if(p->crpix->size!=ndim)
-        error(EXIT_FAILURE, 0, "%zu values given to `--crpix'. This must be "
+        error(EXIT_FAILURE, 0, "%zu values given to '--crpix'. This must be "
               "the same as the output dimension (%zu)", p->crpix->size, ndim);
       return 0;
     }
@@ -1218,7 +1253,7 @@ ui_wcs_sanity_check(struct mkprofparams *p)
   if(p->crval)
     {
       if(p->crval->size!=ndim)
-        error(EXIT_FAILURE, 0, "%zu values given to `--crval'. This must be "
+        error(EXIT_FAILURE, 0, "%zu values given to '--crval'. This must be "
               "the same as the output dimension (%zu)", p->crval->size, ndim);
       return 0;
     }
@@ -1227,7 +1262,7 @@ ui_wcs_sanity_check(struct mkprofparams *p)
   if(p->cdelt)
     {
       if(p->cdelt->size!=ndim)
-        error(EXIT_FAILURE, 0, "%zu values given to `--cdelt'. This must be "
+        error(EXIT_FAILURE, 0, "%zu values given to '--cdelt'. This must be "
               "the same as the output dimension (%zu)", p->cdelt->size, ndim);
       return 0;
     }
@@ -1236,7 +1271,7 @@ ui_wcs_sanity_check(struct mkprofparams *p)
   if(p->pc)
     {
       if(p->pc->size!=ndim*ndim)
-        error(EXIT_FAILURE, 0, "%zu values given to `--pc'. This must be "
+        error(EXIT_FAILURE, 0, "%zu values given to '--pc'. This must be "
               "the square as the output dimension (%zu)", p->pc->size,
               ndim*ndim);
       return 0;
@@ -1246,7 +1281,7 @@ ui_wcs_sanity_check(struct mkprofparams *p)
   if(p->cunit)
     {
       if(p->cunit->size!=ndim)
-        error(EXIT_FAILURE, 0, "%zu values given to `--cunit'. This must be "
+        error(EXIT_FAILURE, 0, "%zu values given to '--cunit'. This must be "
               "the same as the output dimension (%zu)", p->cunit->size, ndim);
       return 0;
     }
@@ -1255,7 +1290,7 @@ ui_wcs_sanity_check(struct mkprofparams *p)
   if(p->ctype)
     {
       if(p->ctype->size!=ndim)
-        error(EXIT_FAILURE, 0, "%zu values given to `--ctype'. This must be "
+        error(EXIT_FAILURE, 0, "%zu values given to '--ctype'. This must be "
               "the same as the output dimension (%zu)", p->ctype->size, ndim);
       return 0;
     }
@@ -1309,7 +1344,7 @@ ui_prepare_wcs(struct mkprofparams *p)
       /* IMPORTANT: At this point, we don't want the WCS to be over-sampled
          because if the user has given RA and Dec for the profiles, they
          need to be converted to non-oversampled and shifted image
-         coordinates. After the conversion (in `ui_finalize_coordinates')
+         coordinates. After the conversion (in 'ui_finalize_coordinates')
          we are going to correct for the oversampling in the WCS.*/
       wcs->crpix[i] = crpix[i];
       wcs->crval[i] = crval[i];
@@ -1347,7 +1382,7 @@ ui_prepare_canvas(struct mkprofparams *p)
       /* Read in the background image and its coordinates, note that when
          no merged image is desired, we just need the WCS information of
          the background image and the number of its dimensions. So
-         `ndim==0' and what `dsize' points to is irrelevant. */
+         'ndim==0' and what 'dsize' points to is irrelevant. */
       tdsize=gal_fits_img_info_dim(p->backname, p->backhdu, &tndim);
       p->wcs=gal_wcs_read(p->backname, p->backhdu, 0, 0, &p->nwcs);
       tndim=gal_dimension_remove_extra(tndim, tdsize, p->wcs);
@@ -1357,7 +1392,7 @@ ui_prepare_canvas(struct mkprofparams *p)
           /* If p->dsize was given as an option, free it. */
           if( p->dsize ) free(p->dsize);
 
-          /* Write the size of the background image into `dsize'. */
+          /* Write the size of the background image into 'dsize'. */
           p->dsize=gal_pointer_allocate(GAL_TYPE_SIZE_T, p->ndim, 0,
                                         __func__, "p->dsize");
           for(i=0;i<p->ndim;++i) p->dsize[i] = p->out->dsize[i];
@@ -1388,13 +1423,13 @@ ui_prepare_canvas(struct mkprofparams *p)
 
           /* Make sure it has the same number of elements as naxis. */
           if(p->ndim!=nshift)
-            error(EXIT_FAILURE, 0, "%zu and %zu elements given to `--ndim' "
-                  "and `--shift' respectively. These two numbers must be the "
+            error(EXIT_FAILURE, 0, "%zu and %zu elements given to '--ndim' "
+                  "and '--shift' respectively. These two numbers must be the "
                   "same", p->ndim, nshift);
         }
       else
         {
-          /* `prepforconv' is only valid when xshift and yshift are both
+          /* 'prepforconv' is only valid when xshift and yshift are both
              zero. Also, a PSF profile should exist in the image. */
           if(p->prepforconv)
             {
@@ -1516,15 +1551,15 @@ ui_finalize_coordinates(struct mkprofparams *p)
   double *cdelt=p->wcs->cdelt, *crpix=p->wcs->crpix;
 
   /* When the user specified RA and Dec columns, the respective values
-     where stored in the `p->x' and `p->y' arrays. So before proceeding, we
+     where stored in the 'p->x' and 'p->y' arrays. So before proceeding, we
      need to change them into actual image coordinates. */
   if(p->mode==MKPROF_MODE_WCS)
     {
-      /* Make list of coordinates for input of `gal_wcs_world_to_img'. */
+      /* Make list of coordinates for input of 'gal_wcs_world_to_img'. */
       for(i=0;i<ndim;++i)
         {
           /* Set the array pointer. Note that we read the WCS columns into
-         the `p->x', `p->y' and `p->z' arrays temporarily before. Here, we
+         the 'p->x', 'p->y' and 'p->z' arrays temporarily before. Here, we
          will convert them to image coordinates in place. */
           switch(i)
             {
@@ -1557,7 +1592,7 @@ ui_finalize_coordinates(struct mkprofparams *p)
                 "(%f, %f) coordinates into image coordinates", i, p->x[i],
                 p->y[i]);
 
-      /* We want the actual arrays of each `coords' column. So, first we'll
+      /* We want the actual arrays of each 'coords' column. So, first we'll
          set all the array elements to NULL, then free it. */
       for(tmp=coords;tmp!=NULL;tmp=tmp->next) tmp->array=NULL;
       gal_list_data_free(coords);
@@ -1569,7 +1604,7 @@ ui_finalize_coordinates(struct mkprofparams *p)
      non-over-sampled image.*/
   for(i=0;i<p->ndim;++i)
     {
-      /* Oversampling has already been applied in `p->shift'. Also note
+      /* Oversampling has already been applied in 'p->shift'. Also note
          that shift is in the C dimension ordring, while crpix is in FITS
          ordering. */
       crpix[i]  = crpix[i]*os + p->shift[ndim-i-1] - os/2;
@@ -1637,6 +1672,83 @@ ui_make_log(struct mkprofparams *p)
 
 
 
+/* Read the input radial table. */
+static void
+ui_read_custom_table(struct mkprofparams *p)
+{
+  size_t i;
+  double diff;
+  int isregular;
+  gal_data_t *cols;
+  double *min, *max;
+
+  /* Read the input radial table. */
+  cols=gal_table_read(p->customname, p->customhdu,
+                      NULL, NULL, p->cp.searchin, p->cp.ignorecase,
+                      p->cp.minmapsize, p->cp.quietmmap, NULL);
+
+  /* Make sure the table only has three columns. */
+  if(gal_list_data_number(cols) != 3 )
+    error(EXIT_FAILURE, 0, "%s: has %zu columns, but it should only have "
+          "three columns. Column1: the radial interval's lower value. "
+          "Column 2: the radial interval's higher value. Column 3: the value "
+          "to use for pixels within that radius interval",
+          gal_fits_name_save_as_string(p->customname, p->customhdu),
+          gal_list_data_number(cols));
+
+  /* Make sure none of the three columns are string type. */
+  if( cols->type==GAL_TYPE_STRING
+      || cols->next->type==GAL_TYPE_STRING
+      || cols->next->next->type==GAL_TYPE_STRING )
+    error(EXIT_FAILURE, 0, "%s: the columns should only have numeric "
+          "data types", gal_fits_name_save_as_string(p->customname,
+                                                     p->customhdu));
+
+  /* Fill the final table as a double type. */
+  p->custom=gal_data_copy_to_new_type(cols, GAL_TYPE_FLOAT64);
+  p->custom->next=gal_data_copy_to_new_type(cols->next,
+                                                 GAL_TYPE_FLOAT64);
+  p->custom->next->next=gal_data_copy_to_new_type(cols->next->next,
+                                                       GAL_TYPE_FLOAT64);
+
+  /* Make sure the first column values are smaller than the second column's
+     values. */
+  min=p->custom->array;
+  max=p->custom->next->array;
+  for(i=0;i<p->custom->size;++i)
+    if(min[i]>=max[i])
+      error(EXIT_FAILURE, 0, "%s: the first column of row %zu (with value "
+            "%g) is larger or equal to the second column (with value %g). "
+            "However, the first column is the lower-limit of the radial "
+            "interval and the second column is the upper-limit. So the "
+            "first column must have a lower value",
+            gal_fits_name_save_as_string(p->customname,
+                                         p->customhdu), i+1,
+            min[i], max[i]);
+
+  /* Check if the input table is regular and sorted (which can greatly
+     speed up its usage). */
+  isregular=1;
+  diff=max[0]-min[0];
+  for(i=1;i<p->custom->size;++i)
+    if( min[i]<min[i-1]
+        || min[i] != max[i-1]
+        || max[i]-min[i] != diff )
+      isregular=0;
+  if(isregular)
+    {
+      p->customregular[0]=min[0];
+      p->customregular[1]=diff;
+    }
+
+  /* Clean up. */
+  gal_list_data_free(cols);
+}
+
+
+
+
+
 static void
 ui_read_ndim(struct mkprofparams *p)
 {
@@ -1649,7 +1761,7 @@ ui_read_ndim(struct mkprofparams *p)
 
       /* Make sure the kernel and background are not given together. */
       if(p->backname)
-        error(EXIT_FAILURE, 0, "the `--kernel' and `--background' options "
+        error(EXIT_FAILURE, 0, "the '--kernel' and '--background' options "
               "cannot be called together");
     }
   else
@@ -1660,10 +1772,10 @@ ui_read_ndim(struct mkprofparams *p)
           /* Small sanity check. */
           if(p->backhdu==NULL)
             error(EXIT_FAILURE, 0, "no hdu specified for the background "
-                  "image %s. Please run again `--backhdu' option",
+                  "image %s. Please run again '--backhdu' option",
                   p->backname);
 
-          /* If `--nomerged' is given, we don't actually need to load the
+          /* If '--nomerged' is given, we don't actually need to load the
              image, we just need its WCS later. */
           if(p->nomerged)
             {
@@ -1699,7 +1811,7 @@ ui_read_ndim(struct mkprofparams *p)
 
           /* Make sure the dimensionality is supported. */
           if(p->ndim!=2 && p->ndim!=3)
-            error(EXIT_FAILURE, 0, "%zu values given to `--mergedsize'. "
+            error(EXIT_FAILURE, 0, "%zu values given to '--mergedsize'. "
                   "Currently only 2 or 3 dimensional outputs can be produced",
                   p->ndim);
         }
@@ -1717,10 +1829,13 @@ ui_preparations(struct mkprofparams *p)
      use). */
   ui_read_ndim(p);
 
-  /* Read in all the columns (necessary for `--prepforconf' when we want to
+  /* Read in all the columns (necessary for '--prepforconf' when we want to
      build the profiles). */
   ui_prepare_columns(p);
 
+  /* Read the radial table. */
+  if(p->customname) ui_read_custom_table(p);
+
   /* If the kernel option was given, some parameters need to be
      over-written: */
   if(p->kernel)
@@ -1845,9 +1960,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
mkprofparams *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
+  /* 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
diff --git a/bin/mkprof/ui.h b/bin/mkprof/ui.h
index 4b0898c..33bbb93 100644
--- a/bin/mkprof/ui.h
+++ b/bin/mkprof/ui.h
@@ -5,7 +5,7 @@ MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -93,6 +93,8 @@ enum option_keys_enum
   UI_KEY_PC,
   UI_KEY_CUNIT,
   UI_KEY_CTYPE,
+  UI_KEY_CUSTOMTABLE,
+  UI_KEY_CUSTOMTABLEHDU,
 };
 
 
diff --git a/bin/noisechisel/Makefile.am b/bin/noisechisel/Makefile.am
index 86633b1..56d429d 100644
--- a/bin/noisechisel/Makefile.am
+++ b/bin/noisechisel/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2016-2019, Free Software Foundation, Inc.
+## Copyright (C) 2016-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astnoisechisel
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
 astnoisechisel_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
-                       -lgnuastro $(MAYBE_NORPATH)
+                       -lgnuastro $(CONFIG_LDADD)
 
 astnoisechisel_SOURCES = main.c ui.c detection.c noisechisel.c sky.c     \
   threshold.c
diff --git a/bin/noisechisel/args.h b/bin/noisechisel/args.h
index 63560d2..4f51411 100644
--- a/bin/noisechisel/args.h
+++ b/bin/noisechisel/args.h
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -36,7 +36,7 @@ struct argp_option program_options[] =
     {
       "kernel",
       UI_KEY_KERNEL,
-      "STR",
+      "FITS",
       0,
       "Filename of kernel to convolve with input",
       GAL_OPTIONS_GROUP_INPUT,
@@ -62,7 +62,7 @@ struct argp_option program_options[] =
     {
       "convolved",
       UI_KEY_CONVOLVED,
-      "STR",
+      "FITS",
       0,
       "Convolved image file to avoid convolution.",
       GAL_OPTIONS_GROUP_INPUT,
@@ -88,7 +88,7 @@ struct argp_option program_options[] =
     {
       "widekernel",
       UI_KEY_WIDEKERNEL,
-      "STR",
+      "FITS",
       0,
       "Filename of wider kernel for better qthresh",
       GAL_OPTIONS_GROUP_INPUT,
diff --git a/bin/noisechisel/astnoisechisel.conf 
b/bin/noisechisel/astnoisechisel.conf
index edbadcc..f57f788 100644
--- a/bin/noisechisel/astnoisechisel.conf
+++ b/bin/noisechisel/astnoisechisel.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astnoisechisel                 # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
@@ -28,11 +28,11 @@
  largetilesize     200,200
 
 # Detection:
- meanmedqdiff        0.005
+ meanmedqdiff         0.01
  qthresh               0.3
- outliersigma           10
+ outliersigma            5
  outliersclip        3,0.2
- smoothwidth             3
+ smoothwidth             5
  erode                   2
  erodengb                4
  noerodequant         0.99
diff --git a/bin/noisechisel/authors-cite.h b/bin/noisechisel/authors-cite.h
index b7c5acb..6476187 100644
--- a/bin/noisechisel/authors-cite.h
+++ b/bin/noisechisel/authors-cite.h
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,12 +26,31 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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 ""
+   put a row of '-' with the same length and then put the BibTeX.
+
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
+
+#define PROGRAM_BIBTEX ""                                               \
+  "Paper on NoiseChisel improvements since 2015\n"                      \
+  "--------------------------------------------\n"                      \
+  "@ARTICLE{noisechisel_segment_2019,\n"                                \
+  "        author = {{Akhlaghi}, Mohammad},\n"                          \
+  "         title = \"{Carving out the low surface brightness universe with 
NoiseChisel}\",\n" \
+  "       journal = {arXiv e-prints},\n"                                \
+  "      keywords = {Astrophysics - Instrumentation and Methods for 
Astrophysics,\n" \
+  "                  Astrophysics - Astrophysics of Galaxies,\n"        \
+  "                  Computer Science - Computer Vision and Pattern 
Recognition},\n" \
+  "          year = \"2019\",\n"                                        \
+  "         month = \"Sep\",\n"                                         \
+  "           eid = {arXiv:1909.11230},\n"                              \
+  "         pages = {arXiv:1909.11230},\n"                              \
+  " archivePrefix = {arXiv},\n"                                         \
+  "        eprint = {1909.11230},\n"                                    \
+  "  primaryClass = {astro-ph.IM},\n"                                   \
+  "        adsurl = 
{https://ui.adsabs.harvard.edu/abs/2019arXiv190911230A},\n"; \
+  "       adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n" \
+  "}\n"
 
 #define PROGRAM_AUTHORS "Mohammad Akhlaghi"
 
diff --git a/bin/noisechisel/detection.c b/bin/noisechisel/detection.c
index 6baa8c4..66154dd 100644
--- a/bin/noisechisel/detection.c
+++ b/bin/noisechisel/detection.c
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -296,7 +296,7 @@ detection_fill_holes_open(void *in_prm)
       tile->block=fho_prm->workbin;
 
       /* Copy the tile into the contiguous patch of memory to work on, but
-         first reset the size element so `gal_data_copy_to_allocated' knows
+         first reset the size element so 'gal_data_copy_to_allocated' knows
          there is enough space. */
       copy->flag=0;
       copy->size=p->maxltcontig;
@@ -378,8 +378,8 @@ detection_pseudo_find(struct noisechiselparams *p, 
gal_data_t *workbin,
 
   /* Allocate the space necessary to work on each tile (to avoid having to
      allocate it it separately for each tile and within each
-     thread. `maxltcontig' is the maximum contiguous patch of memory needed
-     to store all tiles. Finally, since we are working on a `uint8_t' type,
+     thread. 'maxltcontig' is the maximum contiguous patch of memory needed
+     to store all tiles. Finally, since we are working on a 'uint8_t' type,
      the size of each element is only 1 byte. */
   fho_prm.copyspace=gal_pointer_allocate(GAL_TYPE_UINT8,
                                          p->cp.numthreads*p->maxltcontig, 0,
@@ -400,7 +400,7 @@ detection_pseudo_find(struct noisechiselparams *p, 
gal_data_t *workbin,
       /* Do each step. */
       while(fho_prm.step<3)
         {
-          /* Put a copy of `workbin' into `bin' for every step (only
+          /* Put a copy of 'workbin' into 'bin' for every step (only
              necessary for the second step and after). For the first time
              it was already copied.*/
           if(fho_prm.step>1)
@@ -408,7 +408,8 @@ detection_pseudo_find(struct noisechiselparams *p, 
gal_data_t *workbin,
 
           /* Do the respective step. */
           gal_threads_spin_off(detection_fill_holes_open, &fho_prm,
-                               p->ltl.tottiles, p->cp.numthreads);
+                               p->ltl.tottiles, p->cp.numthreads,
+                               p->cp.minmapsize, p->cp.quietmmap);
 
           /* Reset the blank values (if they were changed). */
           if( p->blankasforeground==0 && gal_blank_present(p->input,0) )
@@ -440,12 +441,12 @@ detection_pseudo_find(struct noisechiselparams *p, 
gal_data_t *workbin,
           ++fho_prm.step;
         }
 
-      /* Clean up: the array in `bin' should just be replaced with that in
-         `workbin' because it is used in later steps. */
+      /* Clean up: the array in 'bin' should just be replaced with that in
+         'workbin' because it is used in later steps. */
       if(workbin->mmapname)
         {
-          /* Delete the memory mapped file and set the filename of `bin'
-             for `workbin'. */
+          /* Delete the memory mapped file and set the filename of 'bin'
+             for 'workbin'. */
           remove(workbin->mmapname);
           free(workbin->mmapname);
           workbin->mmapname=bin->mmapname;
@@ -459,14 +460,15 @@ detection_pseudo_find(struct noisechiselparams *p, 
gal_data_t *workbin,
     }
   else
     gal_threads_spin_off(detection_fill_holes_open, &fho_prm,
-                         p->ltl.tottiles, p->cp.numthreads);
+                         p->ltl.tottiles, p->cp.numthreads,
+                         p->cp.minmapsize, p->cp.quietmmap);
 
   /* Clean up. */
   free(fho_prm.copyspace);
 
 
   /* Label all regions, but first, deal with the blank pixels in the
-     `workbin' dataset when working on the Sky. Recall that in this case,
+     'workbin' dataset when working on the Sky. Recall that in this case,
      the blank pixels are the detections. On the Sky image, blank should be
      set to 1 (because we want the detected objects to have the same labels
      as the pseudo-detections that cover them). This will allow us to later
@@ -493,8 +495,8 @@ detection_sn_write_to_file(struct noisechiselparams *p, 
gal_data_t *sn,
   gal_list_str_t *comments=NULL;
 
   /* Comment for extension on further explanation. */
-  if( asprintf(&str, "See also: `%s' HDU of output with "
-               "`--checkdetection'", ( s0d1D2<2
+  if( asprintf(&str, "See also: '%s' HDU of output with "
+               "'--checkdetection'", ( s0d1D2<2
                                        ? "PSEUDOS-FOR-SN": "DILATED" ))<0 )
     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
   gal_list_str_add(&comments, str, 0);
@@ -558,7 +560,7 @@ detection_sn(struct noisechiselparams *p, gal_data_t 
*worklab, size_t num,
 
   /* Allocate all the necessary arrays, note that since we want to put each
      object's information into the same index, the number of allocated
-     spaces has to be `tablen=num+1'. */
+     spaces has to be 'tablen=num+1'. */
   area       = gal_pointer_allocate(GAL_TYPE_SIZE_T,  tablen, 1, __func__,
                                     "area");
   brightness = gal_pointer_allocate(GAL_TYPE_FLOAT64, tablen, 1, __func__,
@@ -807,10 +809,10 @@ detection_pseudo_real(struct noisechiselparams *p)
       if( sn->size < p->minnumfalse)
         error(EXIT_FAILURE, 0, "only %zu pseudo-detections could be found "
               "over the sky region to estimate an S/N. This is less than "
-              "%zu (value to `--minnumfalse' option). Please adjust "
-              "parameters like `--dthresh', `--snminarea', or make sure "
+              "%zu (value to '--minnumfalse' option). Please adjust "
+              "parameters like '--dthresh', '--snminarea', or make sure "
               "that there actually is sufficient sky area after initial "
-              "detection. You can use `--checkdetection' to see every step "
+              "detection. You can use '--checkdetection' to see every step "
               "until this point", sn->size, p->minnumfalse);
 
 
@@ -928,12 +930,12 @@ detection_remove_false_initial(struct noisechiselparams 
*p,
                                           "newlabels");
 
   /* Find the new labels for all the existing labels. Recall that
-     `newlabels' was initialized to zero, so any label that is not given a
+     'newlabels' was initialized to zero, so any label that is not given a
      new label here will be automatically removed. After the first pixel of
      a label overlaps with dbyt[i], we don't need to check the rest of that
      object's pixels. At this point, tokeep is only binary: 0 or 1.
 
-     Note that the zeroth element of `tokeep' can also be non zero, this is
+     Note that the zeroth element of 'tokeep' can also be non zero, this is
      because the holes of the labeled regions might be filled during
      filling the holes, but have not been filled in the original labeled
      array. They are not important so you can just ignore them. */
@@ -965,7 +967,7 @@ detection_remove_false_initial(struct noisechiselparams *p,
      asked for growth, if the edges of the objects in the image are sharp
      enough, no growth will be necessary (and thus the labeled image won't
      be re-written during growth). So it is necessary to check for growth
-     here and later do it in `detection_quantile_expand'. */
+     here and later do it in 'detection_quantile_expand'. */
   p->numexpand=0;
   b=workbin->array;
   l=p->olabel->array;
@@ -1025,7 +1027,7 @@ detection_remove_false_initial(struct noisechiselparams 
*p,
 
 /* Expand the initial detections based on the quantile threshold and then
    label the connected regions. If expansion is not possible, then return
-   the `GAL_BLANK_SIZET'.*/
+   the 'GAL_BLANK_SIZET'.*/
 static size_t
 detection_quantile_expand(struct noisechiselparams *p, gal_data_t *workbin)
 {
@@ -1186,32 +1188,39 @@ detection(struct noisechiselparams *p)
   if(!p->cp.quiet) gettimeofday(&t1, NULL);
   if(p->detgrowquant!=1.0f)
     {
+      /* Grow the detections and report the number of expanded (if
+         necessary). */
       num_expanded=detection_quantile_expand(p, workbin);
       if(num_expanded!=GAL_BLANK_SIZE_T)
-        num_true_initial=num_expanded;
-    }
-
-
-  /* Update the user on the progress, if necessary. */
-  if(!p->cp.quiet && p->detgrowquant!=1.0f && num_expanded!=GAL_BLANK_SIZE_T )
-    {
-      /* If the user hasn't asked for a labeled image, then don't confuse
-         them with the number of detections, just let them know that growth
-         is complete. */
-      if(p->label)
-        {
-          if( asprintf(&msg, "%zu detections after growth to %.3f "
-                       "quantile.", num_true_initial, p->detgrowquant)<0 )
-            error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
-        }
-      else
         {
-          if( asprintf(&msg, "Growth to %.3f quantile complete.",
-                       p->detgrowquant)<0 )
-            error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
+          /* Set the number of expanded objects. */
+          num_true_initial=num_expanded;
+
+          /* If not in quiet-mode, let the user know how its going. */
+          if(!p->cp.quiet)
+            {
+              /* If the user hasn't asked for a labeled image, then don't
+                 confuse them with the number of detections, just let them
+                 know that growth is complete. */
+              if(p->label)
+                {
+                  if( asprintf(&msg, "%zu detections after growth to %.3f "
+                               "quantile.", num_true_initial,
+                               p->detgrowquant)<0 )
+                    error(EXIT_FAILURE, 0, "%s: asprintf allocation",
+                          __func__);
+                }
+              else
+                {
+                  if( asprintf(&msg, "Growth to %.3f quantile complete.",
+                               p->detgrowquant)<0 )
+                    error(EXIT_FAILURE, 0, "%s: asprintf allocation",
+                          __func__);
+                }
+              gal_timing_report(&t1, msg, 2);
+              free(msg);
+            }
         }
-      gal_timing_report(&t1, msg, 2);
-      free(msg);
     }
 
 
@@ -1251,8 +1260,8 @@ detection(struct noisechiselparams *p)
 
 
   /* p->binary was used to keep the initial pseudo-detection threshold. But
-     we don't need it any more, so we'll just free it and put the `workbin'
-     array in its place. Note that `workbin' has a map of all the detected
+     we don't need it any more, so we'll just free it and put the 'workbin'
+     array in its place. Note that 'workbin' has a map of all the detected
      objects, which is still necessary during NoiseChisel. */
   gal_data_free(p->binary);
   p->binary=workbin;
@@ -1265,7 +1274,7 @@ detection(struct noisechiselparams *p)
 
 
   /* If the user wanted to check the threshold and hasn't called
-     `continueaftercheck', then stop NoiseChisel. */
+     'continueaftercheck', then stop NoiseChisel. */
   if(p->detectionname && !p->continueaftercheck)
     ui_abort_after_check(p, p->detectionname, NULL,
                          "showing all detection steps");
diff --git a/bin/noisechisel/detection.h b/bin/noisechisel/detection.h
index 6613f75..c56d7e1 100644
--- a/bin/noisechisel/detection.h
+++ b/bin/noisechisel/detection.h
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/noisechisel/kernel-2d.h b/bin/noisechisel/kernel-2d.h
index 5afcf2a..d9ac583 100644
--- a/bin/noisechisel/kernel-2d.h
+++ b/bin/noisechisel/kernel-2d.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -48,7 +48,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    Convert it to C code
    --------------------
 
-   Put the following C program into a file called `kernel.c'.
+   Put the following C program into a file called 'kernel.c'.
 
      #include <stdio.h>
      #include <stdlib.h>
@@ -74,7 +74,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
                if(i % img->dsize[1] == 0 ) printf("\n\n");
              }
 
-           // We cannot use `\b' here, since we are writing directly
+           // We cannot use '\b' here, since we are writing directly
            // to the command-line, so we'll first write the number,
            // then decide if any subsequent character (a comma)
            // should be written.
@@ -96,7 +96,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    -----------------
 
    We can now compile and run that C program and put the outputs in
-   `kernel.c'. Once its created, copy the contents of `kernel-2d.h' after
+   'kernel.c'. Once its created, copy the contents of 'kernel-2d.h' after
    these comments.
 
      $ astbuildprog -q kernel.c > kernel-2d.h
diff --git a/bin/noisechisel/main.c b/bin/noisechisel/main.c
index 6161371..8c34330 100644
--- a/bin/noisechisel/main.c
+++ b/bin/noisechisel/main.c
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/noisechisel/main.h b/bin/noisechisel/main.h
index f0d62c5..63aba20 100644
--- a/bin/noisechisel/main.h
+++ b/bin/noisechisel/main.h
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -106,6 +106,7 @@ struct noisechiselparams
   gal_data_t          *olabel;  /* Labels of objects in the detection.    */
   gal_data_t   *expand_thresh;  /* Quantile threshold to expand per tile. */
   gal_data_t *exp_thresh_full;  /* Full array containing growth thresh.   */
+  gal_data_t      *noskytiles;  /* Tiles to not use for Sky.              */
   gal_data_t             *sky;  /* Mean of undetected pixels, per tile.   */
   gal_data_t             *std;  /* STD of undetected pixels, per tile.    */
   size_t           maxtcontig;  /* Maximum contiguous space for a tile.   */
diff --git a/bin/noisechisel/noisechisel.c b/bin/noisechisel/noisechisel.c
index 1227e06..1d347b2 100644
--- a/bin/noisechisel/noisechisel.c
+++ b/bin/noisechisel/noisechisel.c
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -160,11 +160,11 @@ noisechisel_output(struct noisechiselparams *p)
   /* Write the detected pixels and useful information into it's header. */
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "DETSN", 0, &p->detsnthresh,
                         0, "Minimum S/N of true pseudo-detections", 0,
-                        "ratio");
+                        "ratio", 0);
   if(p->label)
     gal_fits_key_list_add(&keys, GAL_TYPE_SIZE_T, "NUMLABS", 0,
                           &p->numdetections, 0, "Total number of labels "
-                          "(inclusive)", 0, "counter");
+                          "(inclusive)", 0, "counter", 0);
   gal_fits_key_list_reverse(&keys);
   if(p->label)
     {
@@ -193,13 +193,13 @@ noisechisel_output(struct noisechiselparams *p)
   p->std->name="SKY_STD";
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "MAXSTD", 0, &p->maxstd, 0,
                         "Maximum raw tile standard deviation", 0,
-                        p->input->unit);
+                        p->input->unit, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "MINSTD", 0, &p->minstd, 0,
                         "Minimum raw tile standard deviation", 0,
-                        p->input->unit);
+                        p->input->unit, 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "MEDSTD", 0, &p->medstd, 0,
                         "Median raw tile standard deviation", 0,
-                        p->input->unit);
+                        p->input->unit, 0);
   gal_tile_full_values_write(p->std, &p->cp.tl, !p->ignoreblankintiles,
                              p->cp.output, keys, PROGRAM_NAME);
   p->std->name=NULL;
@@ -213,7 +213,7 @@ noisechisel_output(struct noisechiselparams *p)
 
   /* Let the user know that the output is written. */
   if(!p->cp.quiet)
-    printf("  - Output written to `%s'.\n", p->cp.output);
+    printf("  - Output written to '%s'.\n", p->cp.output);
 }
 
 
@@ -248,28 +248,14 @@ noisechisel(struct noisechiselparams *p)
   /* Remove false detections. */
   detection(p);
 
-  /* If we have any detections, find the Sky value and subtract it from the
-     input and convolved images. */
-  if(p->numdetections)
-    {
-      /* Find the final Sky and Sky STD values. */
-      sky_and_std(p, p->skyname);
+  /* Find the final Sky and Sky STD values. */
+  sky_and_std(p, p->skyname);
 
-      /* Abort if the user only wanted to see until this point.*/
-      if(p->skyname && !p->continueaftercheck)
-        ui_abort_after_check(p, p->skyname, NULL,
-                             "derivation of final Sky (and its STD) value");
+  /* Abort if the user only wanted to see until this point.*/
+  if(p->skyname && !p->continueaftercheck)
+    ui_abort_after_check(p, p->skyname, NULL,
+                         "derivation of final Sky (and its STD) value");
 
-      /* Write the output. */
-      noisechisel_output(p);
-    }
-  else
-    {
-      if(p->cp.quiet)
-        error(0, 0, "no output file created: no detections could found "
-              "in `%s' with given parameters", p->inputname);
-      else
-        gal_timing_report(NULL, "NO OUTPUT FILE CREATED (try with "
-                          "`--checkdetection' to see why)", 1);
-    }
+  /* Write the output. */
+  noisechisel_output(p);
 }
diff --git a/bin/noisechisel/noisechisel.h b/bin/noisechisel/noisechisel.h
index c933f4e..b5a289e 100644
--- a/bin/noisechisel/noisechisel.h
+++ b/bin/noisechisel/noisechisel.h
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/noisechisel/sky.c b/bin/noisechisel/sky.c
index cd66b55..72bf3c5 100644
--- a/bin/noisechisel/sky.c
+++ b/bin/noisechisel/sky.c
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -59,6 +59,7 @@ sky_mean_std_undetected(void *in_prm)
   struct noisechiselparams *p=(struct noisechiselparams *)tprm->params;
 
   int setblank, type=GAL_TYPE_FLOAT32;
+  uint8_t *noskytiles=p->noskytiles->array;
   size_t i, tind, numsky, bdsize=2, ndim=p->sky->ndim;
   size_t refarea, twidth=gal_type_sizeof(GAL_TYPE_FLOAT32);
   gal_data_t *tile, *fusage, *busage, *bintile, *sigmaclip;
@@ -90,71 +91,80 @@ sky_mean_std_undetected(void *in_prm)
       tile = &p->cp.tl.tiles[tind];
       refarea = p->skyfracnoblank ? 0 : tile->size;
 
-      /* Correct the fake binary tile's properties to be the same as this
-         one, then count the number of zero valued elements in it. Note
-         that the `CHECK_BLANK' flag of `GAL_TILE_PARSE_OPERATE' is set to
-         1. So blank values in the input array are not counted. */
-      bintile->size=tile->size;
-      bintile->dsize=tile->dsize;
-      bintile->array=gal_tile_block_relative_to_other(tile, p->binary);
-      GAL_TILE_PARSE_OPERATE(tile, bintile, 1, 1, {
-          if(p->skyfracnoblank) ++refarea;
-          if(!*o)               ++numsky;
-        });
-
-      /* Only continue, if the fraction of Sky values is less than the
-         requested fraction. */
-      setblank=0;
-      if( (float)(numsky)/(float)(refarea) > p->minskyfrac)
+      /* If this tile is already known to have signal in it (from the
+         'qthresh' phase) it will have a value of '1' in the 'noskytiles'
+         array and should be set to blank here too. */
+      setblank=noskytiles[tind];
+      if(setblank==0)
         {
-          /* Re-initialize the usage array's size information (will be
-             corrected to this tile's size by
-             `gal_data_copy_to_allocated'). */
-          busage->ndim = fusage->ndim = ndim;
-          busage->size = fusage->size = p->maxtcontig;
-          gal_data_copy_to_allocated(tile,    fusage);
-          gal_data_copy_to_allocated(bintile, busage);
-
-
-          /* Set all the non-zero pixels in `busage' to NaN in `fusage'. */
-          busage->flag = fusage->flag = 0;
-          gal_blank_flag_apply(fusage, busage);
-
-
-          /* Do the sigma-clipping. */
-          sigmaclip=gal_statistics_sigma_clip(fusage, p->sigmaclip[0],
-                                              p->sigmaclip[1], 1, 1);
-
-          /* When there are zero-valued pixels on the edges of the dataset
-             (that have not been set to NaN/blank), given special
-             conditions, the whole zero-valued region can get a binary
-             value of 1 and so the Sky and its standard deviation can
-             become zero. So, we need ignore such tiles. */
-          if( ((float *)(sigmaclip->array))[3]==0.0 )
-            setblank=1;
-          else
+          /* Correct the fake binary tile's properties to be the same as
+             this one, then count the number of zero valued elements in
+             it. Note that the 'CHECK_BLANK' flag of
+             'GAL_TILE_PARSE_OPERATE' is set to 1. So blank values in the
+             input array are not counted. */
+          bintile->size=tile->size;
+          bintile->dsize=tile->dsize;
+          bintile->array=gal_tile_block_relative_to_other(tile, p->binary);
+          GAL_TILE_PARSE_OPERATE(tile, bintile, 1, 1, {
+              if(p->skyfracnoblank) ++refarea;
+              if(!*o)               ++numsky;
+            });
+
+          /* Only continue, if the fraction of Sky values is less than the
+             requested fraction. */
+          if( (float)(numsky)/(float)(refarea) > p->minskyfrac)
             {
-              /* Copy the sigma-clipped mean and STD to their respective
-                 places in the tile arrays. But first, make sure
-                 `sigmaclip' has the same type as the sky and std
-                 arrays. */
-              sigmaclip=gal_data_copy_to_new_type_free(sigmaclip, type);
-              memcpy(gal_pointer_increment(p->sky->array, tind, type),
-                     gal_pointer_increment(sigmaclip->array, 2, type),
-                     twidth);
-              memcpy(gal_pointer_increment(p->std->array, tind, type),
-                     gal_pointer_increment(sigmaclip->array, 3, type),
-                     twidth);
+              /* Re-initialize the usage array's size information (will be
+                 corrected to this tile's size by
+                 'gal_data_copy_to_allocated'). */
+              busage->ndim = fusage->ndim = ndim;
+              busage->size = fusage->size = p->maxtcontig;
+              gal_data_copy_to_allocated(tile,    fusage);
+              gal_data_copy_to_allocated(bintile, busage);
+
+
+              /* Set all the non-zero pixels in 'busage' to NaN in
+                 'fusage'. */
+              busage->flag = fusage->flag = 0;
+              gal_blank_flag_apply(fusage, busage);
+
+
+              /* Do the sigma-clipping. */
+              sigmaclip=gal_statistics_sigma_clip(fusage, p->sigmaclip[0],
+                                                  p->sigmaclip[1], 1, 1);
+
+
+              /* When there are zero-valued pixels on the edges of the
+                 dataset (that have not been set to NaN/blank), given
+                 special conditions, the whole zero-valued region can get a
+                 binary value of 1 and so the Sky and its standard
+                 deviation can become zero. So, we need ignore such
+                 tiles. */
+              if( ((float *)(sigmaclip->array))[3]==0.0 )
+                setblank=1;
+              else
+                {
+                  /* Copy the sigma-clipped mean and STD to their
+                     respective places in the tile arrays. But first, make
+                     sure 'sigmaclip' has the same type as the sky and std
+                     arrays. */
+                  sigmaclip=gal_data_copy_to_new_type_free(sigmaclip, type);
+                  memcpy(gal_pointer_increment(p->sky->array, tind, type),
+                         gal_pointer_increment(sigmaclip->array, 2, type),
+                         twidth);
+                  memcpy(gal_pointer_increment(p->std->array, tind, type),
+                         gal_pointer_increment(sigmaclip->array, 3, type),
+                         twidth);
+                }
+
+              /* Clean up. */
+              gal_data_free(sigmaclip);
             }
-
-          /* Clean up. */
-          gal_data_free(sigmaclip);
+          else
+            setblank=1;
         }
-      else
-        setblank=1;
 
-      /* If the tile is marked as being blank, write blank values into
-         it. */
+      /* If the tile is marked to be blank, write blank values into it. */
       if(setblank==1)
         {
           gal_blank_write(gal_pointer_increment(p->sky->array, tind, type),
@@ -197,17 +207,18 @@ sky_and_std(struct noisechiselparams *p, char *checkname)
 
 
   /* Allocate space for the mean and standard deviation. */
-  p->sky=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, p->input->ndim, tl->numtiles,
-                        NULL, 0, cp->minmapsize, p->cp.quietmmap, NULL,
-                        p->input->unit, NULL);
-  p->std=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, p->input->ndim, tl->numtiles,
-                        NULL, 0, cp->minmapsize, p->cp.quietmmap, NULL,
-                        p->input->unit, NULL);
+  p->sky=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, p->input->ndim,
+                        tl->numtiles, NULL, 0, cp->minmapsize,
+                        p->cp.quietmmap, NULL, p->input->unit, NULL);
+  p->std=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, p->input->ndim,
+                        tl->numtiles, NULL, 0, cp->minmapsize,
+                        p->cp.quietmmap, NULL, p->input->unit, NULL);
 
 
   /* Find the Sky and its STD on proper tiles. */
   gal_threads_spin_off(sky_mean_std_undetected, p, tl->tottiles,
-                       cp->numthreads);
+                       cp->numthreads, p->cp.minmapsize,
+                       p->cp.quietmmap);
   if(checkname)
     {
       p->sky->name="SKY";
@@ -291,8 +302,8 @@ sky_subtract(struct noisechiselparams *p)
 
   /* A small sanity check. */
   if(p->sky->type!=GAL_TYPE_FLOAT32)
-    error(EXIT_FAILURE, 0, "%s: only `float32' type is acceptable "
-          "for sky values. but `p->sky' has type `%s'", __func__,
+    error(EXIT_FAILURE, 0, "%s: only 'float32' type is acceptable "
+          "for sky values. but 'p->sky' has type '%s'", __func__,
           gal_type_name(p->sky->type, 1));
 
   /* Go over all the tiles. */
diff --git a/bin/noisechisel/sky.h b/bin/noisechisel/sky.h
index a7d308f..3c7be85 100644
--- a/bin/noisechisel/sky.h
+++ b/bin/noisechisel/sky.h
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/noisechisel/threshold.c b/bin/noisechisel/threshold.c
index 41f3e12..3ca3d03 100644
--- a/bin/noisechisel/threshold.c
+++ b/bin/noisechisel/threshold.c
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -33,6 +33,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/threads.h>
 #include <gnuastro/pointer.h>
 #include <gnuastro/statistics.h>
+#include <gnuastro/permutation.h>
 #include <gnuastro/interpolate.h>
 
 #include <gnuastro-internal/timing.h>
@@ -102,7 +103,7 @@ threshold_apply_on_thread(void *in_prm)
               tile->block=p->conv;
             }
 
-          /* Apply the threshold: When the `>' comparison fails, it can be
+          /* Apply the threshold: When the '>' comparison fails, it can be
              either because the pixel was actually smaller than the
              threshold, or that it was a NaN value. In the first case,
              return 0, in the second, return a blank value.
@@ -142,7 +143,7 @@ threshold_apply_on_thread(void *in_prm)
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we "
                 "can address the problem. A value of %d had for "
-                "`taprm->kind' is not valid", __func__, PACKAGE_BUGREPORT,
+                "'taprm->kind' is not valid", __func__, PACKAGE_BUGREPORT,
                 taprm->kind);
         }
     }
@@ -162,8 +163,9 @@ threshold_apply(struct noisechiselparams *p, float *value1,
                 float *value2, int kind)
 {
   struct threshold_apply_p taprm={value1, value2, kind, p};
-  gal_threads_spin_off(threshold_apply_on_thread, &taprm, p->cp.tl.tottiles,
-                       p->cp.numthreads);
+  gal_threads_spin_off(threshold_apply_on_thread, &taprm,
+                       p->cp.tl.tottiles, p->cp.numthreads,
+                       p->cp.minmapsize, p->cp.quietmmap);
 }
 
 
@@ -219,11 +221,12 @@ threshold_write_sn_table(struct noisechiselparams *p, 
gal_data_t *insn,
   gal_table_comments_add_intro(&comments, PROGRAM_STRING, &p->rawtime);
 
 
-  /* Write the table. Note that we'll set the `dontdelete' argument to 0
+  /* Write the table. Note that we'll set the 'dontdelete' argument to 0
      because when the output is a FITS table, we want all the tables in one
      FITS file. We have already deleted any existing file with the same
-     name in `ui_set_output_names'.*/
-  gal_table_write(cols, comments, p->cp.tableformat, filename, extname, 0);
+     name in 'ui_set_output_names'.*/
+  gal_table_write(cols, NULL, comments, p->cp.tableformat, filename,
+                  extname, 0);
 
 
   /* Clean up (if necessary). */
@@ -264,21 +267,22 @@ threshold_interp_smooth(struct noisechiselparams *p, 
gal_data_t **first,
 
   /* A small sanity check. */
   if( (*first)->next )
-    error(EXIT_FAILURE, 0, "%s: `first' must not have any `next' pointer.",
+    error(EXIT_FAILURE, 0, "%s: 'first' must not have any 'next' pointer.",
           __func__);
   if( (*second)->next )
-    error(EXIT_FAILURE, 0, "%s: `second' must not have any `next' pointer.",
+    error(EXIT_FAILURE, 0, "%s: 'second' must not have any 'next' pointer.",
           __func__);
   if( third && (*third)->next )
-    error(EXIT_FAILURE, 0, "%s: `third' must not have any `next' pointer.",
+    error(EXIT_FAILURE, 0, "%s: 'third' must not have any 'next' pointer.",
           __func__);
 
   /* Do the interpolation of both arrays. */
   (*first)->next = *second;
   if(third) (*second)->next = *third;
-  tmp=gal_interpolate_close_neighbors(*first, tl, cp->interpmetric,
-                                      cp->interpnumngb, cp->numthreads,
-                                      cp->interponlyblank, 1);
+  tmp=gal_interpolate_neighbors(*first, tl, cp->interpmetric,
+                                cp->interpnumngb, cp->numthreads,
+                                cp->interponlyblank, 1,
+                                GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN);
   gal_data_free(*first);
   gal_data_free(*second);
   if(third) gal_data_free(*third);
@@ -408,7 +412,7 @@ qthresh_on_tile(void *in_prm)
   for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
     {
       /* Re-initialize the usage array's space (will be changed in
-         `gal_data_copy_to_allocated' for each tile). */
+         'gal_data_copy_to_allocated' for each tile). */
       usage->ndim=ndim;
       usage->size=p->maxtcontig;
       memcpy(usage->dsize, p->maxtsize, ndim*sizeof *p->maxtsize);
@@ -421,7 +425,7 @@ qthresh_on_tile(void *in_prm)
 
       /* Temporarily change the tile's pointers so we can do the work on
          the convolved image, then copy the desired contents into the
-         already allocated `usage' array. */
+         already allocated 'usage' array. */
       tarray=tile->array; tblock=tile->block;
       tile->array=gal_tile_block_relative_to_other(tile, meanconv);
       tile->block=meanconv;
@@ -432,7 +436,7 @@ qthresh_on_tile(void *in_prm)
 
       /* Find the mean's quantile on this tile, note that we have already
          copied the tile's dataset to a newly allocated place. So we have
-         set the `inplace' flag to `1' to avoid extra allocation. */
+         set the 'inplace' flag to '1' to avoid extra allocation. */
       mean=gal_statistics_mean(usage);
       num=gal_statistics_number(usage);
       mean=gal_data_copy_to_new_type_free(mean, usage->type);
@@ -457,14 +461,14 @@ qthresh_on_tile(void *in_prm)
               tarray=tile->array; tblock=tile->block;
               tile->array=gal_tile_block_relative_to_other(tile, p->conv);
               tile->block=p->conv;
-              usage->ndim=ndim;             /* Since usage was modified in */
-              usage->size=p->maxtcontig;    /* place, it needs to be       */
-              gal_data_copy_to_allocated(tile, usage);  /* re-initialized. */
+              usage->ndim=ndim;           /* Since usage was modified in */
+              usage->size=p->maxtcontig;  /* place, it needs to be       */
+              gal_data_copy_to_allocated(tile, usage);/* re-initialized. */
               tile->array=tarray; tile->block=tblock;
             }
 
           /* Get the erosion quantile for this tile and save it. Note that
-             the type of `qvalue' is the same as the input dataset. */
+             the type of 'qvalue' is the same as the input dataset. */
           qvalue=gal_statistics_quantile(usage, p->qthresh, 1);
           memcpy(gal_pointer_increment(qprm->erode_th->array, tind, type),
                  qvalue->array, twidth);
@@ -529,9 +533,9 @@ threshold_good_error(size_t number, int before0_after1, 
size_t interpnumngb)
                   "(where the number may decrease even further).");
   char *in3 = ( before0_after1
                 ? "\n"
-                  "  - (slightly) Increase `--outliersclip' to reject less "
+                  "  - (slightly) Increase '--outliersclip' to reject less "
                   "as outliers.\n"
-                  "  - (slightly) Increase `--outliersigma' to reject less "
+                  "  - (slightly) Increase '--outliersigma' to reject less "
                   "as outliers.\n"
                 : "\n");
 
@@ -539,7 +543,7 @@ threshold_good_error(size_t number, int before0_after1, 
size_t interpnumngb)
   error(EXIT_FAILURE, 0, "%zu tiles usable %s!\n\n"
 
         "This is smaller than the requested minimum value of %zu (value to "
-        "the `--interpnumngb' option). %s\n\n"
+        "the '--interpnumngb' option). %s\n\n"
 
         "There are several ways to address the problem. The best and most "
         "highly recommended is to use a larger input if possible (when the "
@@ -548,23 +552,23 @@ threshold_good_error(size_t number, int before0_after1, 
size_t interpnumngb)
         "parameters mentioned below in the respective order (and therefore "
         "cause scatter/inaccuracy in the final result). Hence its best to "
         "not loosen them too much (recall that you can see all the option "
-        "values to Gnuastro's programs by appending `-P' to the end of your "
+        "values to Gnuastro's programs by appending '-P' to the end of your "
         "command).\n"
-        "  - (slightly) Decrease `--tilesize' so your tile-grid has more "
+        "  - (slightly) Decrease '--tilesize' so your tile-grid has more "
         "tiles.\n"
-        "  - (slightly) Increase `--meanmedqdiff' to accept more tiles.%s"
-        "  - (slightly) Decrease `--interpnumngb' to be less than %zu.\n\n"
+        "  - (slightly) Increase '--meanmedqdiff' to accept more tiles.%s"
+        "  - (slightly) Decrease '--interpnumngb' to be less than %zu.\n\n"
 
         "---- Tip ----\n"
-        "Append your command with `--checkqthresh' to see the "
+        "Append your command with '--checkqthresh' to see the "
         "successful tiles in relation with this dataset's contents "
         "before this crash. A visual inspection will greatly help in "
         "finding the cause/solution for this particular dataset (note "
-        "that the output of `--checkqthresh' is a multi-extension FITS "
+        "that the output of '--checkqthresh' is a multi-extension FITS "
         "file).\n\n"
         "To better understand this important step, please run the "
-        "following command (press `SPACE'/arrow-keys to navigate and "
-        "`Q' to return back to the command-line):\n\n"
+        "following command (press 'SPACE'/arrow-keys to navigate and "
+        "'Q' to return back to the command-line):\n\n"
         "    $ info gnuastro \"Quantifying signal in a tile\"\n", number,
         in1, interpnumngb, in2, in3, number);
 }
@@ -590,9 +594,9 @@ threshold_quantile_find_apply(struct noisechiselparams *p)
 
 
   /* Add image to check image if requested. If the user has asked for
-     `oneelempertile', then the size of values is not going to be the same
+     'oneelempertile', then the size of values is not going to be the same
      as the input, making it hard to inspect visually. So we'll only put
-     the full input when `oneelempertile' isn't requested. */
+     the full input when 'oneelempertile' isn't requested. */
   if(p->qthreshname && !tl->oneelempertile)
     {
       gal_fits_img_write(p->conv ? p->conv : p->input, p->qthreshname, NULL,
@@ -606,10 +610,12 @@ threshold_quantile_find_apply(struct noisechiselparams *p)
   /* Allocate space for the quantile threshold values. */
   qprm.erode_th=gal_data_alloc(NULL, p->input->type, p->input->ndim,
                                tl->numtiles, NULL, 0, cp->minmapsize,
-                               p->cp.quietmmap, NULL, p->input->unit, NULL);
+                               p->cp.quietmmap, NULL, p->input->unit,
+                               NULL);
   qprm.noerode_th=gal_data_alloc(NULL, p->input->type, p->input->ndim,
                                  tl->numtiles, NULL, 0, cp->minmapsize,
-                                 p->cp.quietmmap, NULL, p->input->unit, NULL);
+                                 p->cp.quietmmap, NULL, p->input->unit,
+                                 NULL);
   qprm.expand_th = ( p->detgrowquant!=1.0f
                      ? gal_data_alloc(NULL, p->input->type, p->input->ndim,
                                       tl->numtiles, NULL, 0, cp->minmapsize,
@@ -626,10 +632,12 @@ threshold_quantile_find_apply(struct noisechiselparams *p)
 
   /* Find the threshold on each tile, free the temporary processing space
      and set the blank flag on both. Since they have the same blank
-     elements, it is only necessary to check one (with the `updateflag'
+     elements, it is only necessary to check one (with the 'updateflag'
      value set to 1), then update the next. */
   qprm.p=p;
-  gal_threads_spin_off(qthresh_on_tile, &qprm, tl->tottiles, cp->numthreads);
+  gal_threads_spin_off(qthresh_on_tile, &qprm, tl->tottiles,
+                       cp->numthreads, cp->minmapsize,
+                       cp->quietmmap);
   free(qprm.usage);
   if( gal_blank_present(qprm.erode_th, 1) )
     {
@@ -661,11 +669,21 @@ threshold_quantile_find_apply(struct noisechiselparams *p)
     }
 
 
-  /* Remove outliers if requested. */
-  if(p->outliersigma!=0.0)
-    gal_tileinternal_no_outlier(qprm.erode_th, qprm.noerode_th,
-                                qprm.expand_th, &p->cp.tl, p->outliersclip,
-                                p->outliersigma, p->qthreshname);
+  /* Remove the outliers. */
+  gal_tileinternal_no_outlier_local(qprm.erode_th, qprm.noerode_th,
+                                    qprm.expand_th, &cp->tl,
+                                    cp->interpmetric, cp->interpnumngb,
+                                    cp->numthreads, p->outliersclip,
+                                    p->outliersigma, p->qthreshname);
+
+
+  /* Use the no-outlier grid as a basis for later estimating the sky. To
+     see this array on the image, use 'gal_tile_full_values_write'. */
+  p->noskytiles=gal_blank_flag(qprm.erode_th);
+  /* For a check:
+  gal_tile_full_values_write(p->noskytiles, &cp->tl, 1,
+                             "noskytiles.fits", NULL, NULL);
+  */
 
 
   /* Check if the number of acceptable tiles is more than the minimum
@@ -676,6 +694,7 @@ threshold_quantile_find_apply(struct noisechiselparams *p)
   nval=((size_t *)(num->array))[0];
   if( nval < cp->interpnumngb )
     threshold_good_error(nval, 1, cp->interpnumngb);
+  gal_data_free(num);
 
 
   /* Interpolate and smooth the derived values. */
@@ -716,7 +735,7 @@ threshold_quantile_find_apply(struct noisechiselparams *p)
 
 
   /* If the user wanted to check the threshold and hasn't called
-     `continueaftercheck', then stop NoiseChisel. */
+     'continueaftercheck', then stop NoiseChisel. */
   if(p->qthreshname && !p->continueaftercheck)
     ui_abort_after_check(p, p->qthreshname, NULL, "quantile threshold check");
 }
diff --git a/bin/noisechisel/threshold.h b/bin/noisechisel/threshold.h
index a4bc4bd..0c7abeb 100644
--- a/bin/noisechisel/threshold.h
+++ b/bin/noisechisel/threshold.h
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/noisechisel/ui.c b/bin/noisechisel/ui.c
index e930a0e..2f3e413 100644
--- a/bin/noisechisel/ui.c
+++ b/bin/noisechisel/ui.c
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -144,7 +144,7 @@ ui_initialize_options(struct noisechiselparams *p,
 
         case GAL_OPTIONS_KEY_TABLEFORMAT:
           cp->coptions[i].mandatory=GAL_OPTIONS_MANDATORY;
-          cp->coptions[i].doc="`txt', `fits-ascii', `fits-binary'.";
+          cp->coptions[i].doc="'txt', 'fits-ascii', 'fits-binary'.";
           break;
         }
     }
@@ -160,18 +160,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct noisechiselparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -219,34 +219,34 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct noisechiselparams *p)
 {
   /* If the convolved option is given, then the convolved HDU is also
      mandatory. */
   if(p->convolvedname && p->chdu==NULL)
-    error(EXIT_FAILURE, 0, "no value given to `--chdu'. When the "
-          "`--convolved' option is called (to specify a convolved image "
+    error(EXIT_FAILURE, 0, "no value given to '--chdu'. When the "
+          "'--convolved' option is called (to specify a convolved image "
           "and avoid convolution) it is mandatory to also specify a HDU "
           "for it");
 
   /* Make sure that the no-erode-quantile is not smaller or equal to
      qthresh. */
   if( p->noerodequant <= p->qthresh)
-    error(EXIT_FAILURE, 0, "the quantile for no erosion (`--noerodequant') "
-          "must be larger than the base quantile threshold (`--qthresh', "
-          "or `-t'). You have provided %.4f and %.4f for the former and "
+    error(EXIT_FAILURE, 0, "the quantile for no erosion ('--noerodequant') "
+          "must be larger than the base quantile threshold ('--qthresh', "
+          "or '-t'). You have provided %.4f and %.4f for the former and "
           "latter, respectively", p->noerodequant, p->qthresh);
 
   /* For the options that make tables, the table format option is
      mandatory. */
   if( p->checksn && p->cp.tableformat==0 )
-    error(EXIT_FAILURE, 0, "`--tableformat' is necessary with the "
-          "`--checksn' option.\n"
-          "Please see description for `--tableformat' after running the "
-          "following command for more information (use `SPACE' to go down "
-          "the page and `q' to return to the command-line):\n\n"
+    error(EXIT_FAILURE, 0, "'--tableformat' is necessary with the "
+          "'--checksn' option.\n"
+          "Please see description for '--tableformat' after running the "
+          "following command for more information (use 'SPACE' to go down "
+          "the page and 'q' to return to the command-line):\n\n"
           "    $ info gnuastro \"Input Output options\"");
 
   /* Kernel checks. */
@@ -259,7 +259,7 @@ ui_read_check_only_options(struct noisechiselparams *p)
       if( gal_fits_name_is_fits(p->kernelname) && p->khdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified for kernel. When the "
               "kernel is a FITS file, a HDU must also be specified. You "
-              "can use the `--khdu' option and give it the HDU number "
+              "can use the '--khdu' option and give it the HDU number "
               "(starting from zero), extension name, or anything "
               "acceptable by CFITSIO");
     }
@@ -273,8 +273,8 @@ ui_read_check_only_options(struct noisechiselparams *p)
       /* If its FITS, see if a HDU has been provided. */
       if( gal_fits_name_is_fits(p->widekernelname) && p->whdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified for the given wide kernel "
-              "(`%s'). When the wide kernel is a FITS file, a HDU must also "
-              "be specified. You can use the `--whdu' option and give it the "
+              "('%s'). When the wide kernel is a FITS file, a HDU must also "
+              "be specified. You can use the '--whdu' option and give it the "
               "HDU number (starting from zero), extension name, or any "
               "HDU identifier acceptable by CFITSIO", p->widekernelname);
     }
@@ -284,11 +284,11 @@ ui_read_check_only_options(struct noisechiselparams *p)
      (higher-is-better), not the contamination level
      (lower-is-better). This actually happened in a few cases: where we
      wanted a false detection rate of 0.0001 (a super-high value!), and
-     instead of inputing 0.9999, we mistakenly gave `--snquant' a value of
-     `0.0001'. We were thus fully confused with the output (an extremely
+     instead of inputing 0.9999, we mistakenly gave '--snquant' a value of
+     '0.0001'. We were thus fully confused with the output (an extremely
      low value) and thought its a bug, while it wasn't! */
   if(p->snquant<0.1)
-    fprintf(stderr, "\nWARNING: Value of `--snquant' (`-c') is %g. Note "
+    fprintf(stderr, "\nWARNING: Value of '--snquant' ('-c') is %g. Note "
             "that this is not a contamination rate (where lower is "
             "better), it is a purity rate (where higher is better). If you "
             "intentionally asked for such a low purity level, please "
@@ -312,7 +312,7 @@ ui_check_options_and_arguments(struct noisechiselparams *p)
       if( gal_fits_name_is_fits(p->inputname) && p->cp.hdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified for 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 "
+              "the '--hdu' ('-h') option and give it the HDU number "
               "(starting from zero), extension name, or anything "
               "acceptable by CFITSIO");
     }
@@ -531,7 +531,7 @@ ui_prepare_tiles(struct noisechiselparams *p)
       gal_fits_img_write(check, tl->tilecheckname, NULL, PROGRAM_NAME);
       gal_data_free(check);
 
-      /* If `continueaftercheck' hasn't been called, abort NoiseChisel. */
+      /* If 'continueaftercheck' hasn't been called, abort NoiseChisel. */
       if(!p->continueaftercheck)
         ui_abort_after_check(p, tl->tilecheckname, NULL,
                              "showing all tiles over the image");
@@ -553,7 +553,7 @@ ui_ngb_check(size_t value, char *optionname, size_t ndim)
     case 2:
       if(value!=4 && value!=8)
         error(EXIT_FAILURE, 0, "%zu is not an acceptable value for "
-              "`--%s'. Acceptable values for 2D inputs are 4 or 8",
+              "'--%s'. Acceptable values for 2D inputs are 4 or 8",
               value, optionname);
       break;
     case 3:
@@ -593,7 +593,7 @@ ui_preparations_read_input(struct noisechiselparams *p)
                                             p->input->dsize,
                                             p->input->wcs);
 
-  /* When the input doesn't have a name, use `INPUT'. */
+  /* When the input doesn't have a name, use 'INPUT'. */
   if(p->input->name==NULL)
     gal_checkset_allocate_copy("INPUT", &p->input->name);
 
@@ -662,8 +662,8 @@ ui_preparations(struct noisechiselparams *p)
 
       /* Make sure the convolved image is the same size as the input. */
       if( gal_dimension_is_different(p->input, p->conv) )
-        error(EXIT_FAILURE, 0, "%s (hdu %s), given to `--convolved' and "
-              "`--convolvehdu', is not the same size as NoiseChisel's "
+        error(EXIT_FAILURE, 0, "%s (hdu %s), given to '--convolved' and "
+              "'--convolvehdu', is not the same size as NoiseChisel's "
               "input: %s (hdu: %s)", p->convolvedname, p->chdu,
               p->inputname, p->cp.hdu);
     }
@@ -716,9 +716,9 @@ ui_read_check_inputs_setup(int argc, char *argv[],
   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
+  /* 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
@@ -831,12 +831,12 @@ ui_abort_after_check(struct noisechiselparams *p, char 
*filename,
 
   if(file2name)
     {
-      if( asprintf(&name, "`%s' and `%s'", filename, file2name)<0 )
+      if( asprintf(&name, "'%s' and '%s'", filename, file2name)<0 )
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
     }
   else
     {
-      if( asprintf(&name, "`%s'", filename)<0 )
+      if( asprintf(&name, "'%s'", filename)<0 )
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
     }
 
@@ -848,7 +848,7 @@ ui_abort_after_check(struct noisechiselparams *p, char 
*filename,
           "%s (%s) has been created.\n\n"
           "If you want %s to continue its processing AND save any "
           "requested check outputs, please run it again with "
-          "`--continueaftercheck'.\n"
+          "'--continueaftercheck'.\n"
           "------------------------------------------------\n",
           PROGRAM_NAME, name, description, PROGRAM_NAME);
 
@@ -872,12 +872,15 @@ ui_free_report(struct noisechiselparams *p, struct 
timeval *t1)
   free(p->maxtsize);
   free(p->maxltsize);
   free(p->cp.output);
-  if(p->skyname)          free(p->skyname);
-  if(p->detskyname)       free(p->detskyname);
-  if(p->qthreshname)      free(p->qthreshname);
-  if(p->detsn_s_name)     free(p->detsn_s_name);
-  if(p->detsn_d_name)     free(p->detsn_d_name);
-  if(p->detectionname)    free(p->detectionname);
+  if(p->khdu) free(p->khdu);
+  if(p->whdu) free(p->whdu);
+  if(p->chdu) free(p->chdu);
+  if(p->skyname) free(p->skyname);
+  if(p->detskyname) free(p->detskyname);
+  if(p->qthreshname) free(p->qthreshname);
+  if(p->detsn_s_name) free(p->detsn_s_name);
+  if(p->detsn_d_name) free(p->detsn_d_name);
+  if(p->detectionname) free(p->detectionname);
 
   /* Free the allocated datasets. */
   gal_data_free(p->sky);
@@ -887,6 +890,7 @@ ui_free_report(struct noisechiselparams *p, struct timeval 
*t1)
   gal_data_free(p->kernel);
   gal_data_free(p->binary);
   gal_data_free(p->olabel);
+  gal_data_free(p->noskytiles);
   gal_data_free(p->widekernel);
   if(p->conv!=p->input) gal_data_free(p->conv);
 
diff --git a/bin/noisechisel/ui.h b/bin/noisechisel/ui.h
index c42df7d..b08a0ab 100644
--- a/bin/noisechisel/ui.h
+++ b/bin/noisechisel/ui.h
@@ -5,7 +5,7 @@ NoiseChisel is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/convertt/Makefile.am b/bin/query/Makefile.am
similarity index 57%
copy from bin/convertt/Makefile.am
copy to bin/query/Makefile.am
index 1935ff1..a3f9a16 100644
--- a/bin/convertt/Makefile.am
+++ b/bin/query/Makefile.am
@@ -1,9 +1,9 @@
 ## Process this file with automake to produce Makefile.inx
 ##
 ## Original author:
-##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+##     Mohammad akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2016-2021, 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
@@ -20,27 +20,28 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
-bin_PROGRAMS = astconvertt
+bin_PROGRAMS = astquery
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astconvertt_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                    $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astquery_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                 -lgnuastro $(CONFIG_LDADD)
 
-astconvertt_SOURCES = main.c ui.c convertt.c color.c
+astquery_SOURCES = main.c ui.c query.c astron.c gaia.c ned.c tap.c \
+                   vizier.c
 
-EXTRA_DIST = main.h authors-cite.h args.h ui.h convertt.h color.h
+EXTRA_DIST = main.h authors-cite.h args.h ui.h query.h astron.h \
+             gaia.h ned.h tap.h vizier.h
 
 
 
 ## The configuration file (distribute and install).
 ## NOTE: the man page is created in doc/Makefile.am
-dist_sysconf_DATA = astconvertt.conf
+dist_sysconf_DATA = astquery.conf
diff --git a/bin/crop/args.h b/bin/query/args.h
similarity index 57%
copy from bin/crop/args.h
copy to bin/query/args.h
index 27ab92d..87db422 100644
--- a/bin/crop/args.h
+++ b/bin/query/args.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-Crop - Crop a given size from one or multiple images.
-Crop is part of GNU Astronomy Utilities (Gnuastro) package.
+Query - Retreive data from a remote data server.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
-     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2020-2021, 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
@@ -27,139 +27,145 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
+
 /* Array of acceptable options. */
 struct argp_option program_options[] =
   {
-    /* Input. */
+    /* Input options */
     {
-      "mode",
-      UI_KEY_MODE,
-      "STR",
+      "ccol",
+      UI_KEY_CCOL,
+      "STR,STR",
       0,
-      "Coordinate mode `img' or `wcs'.",
+      "Coordinate (RA, Dec) column names in dataset.",
       GAL_OPTIONS_GROUP_INPUT,
-      &p->mode,
-      GAL_TYPE_STRING,
+      &p->ccol,
+      GAL_TYPE_STRLL,
       GAL_OPTIONS_RANGE_ANY,
-      GAL_OPTIONS_MANDATORY,
-      GAL_OPTIONS_NOT_SET,
-      ui_parse_coordinate_mode
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
     },
+
+
+
+
+
+    /* Output related options. */
     {
-      "hstartwcs",
-      UI_KEY_HSTARTWCS,
-      "INT",
+      "keeprawdownload",
+      UI_KEY_KEEPRAWDOWNLOAD,
       0,
-      "Header keyword number to start reading WCS.",
-      GAL_OPTIONS_GROUP_INPUT,
-      &p->hstartwcs,
-      GAL_TYPE_SIZE_T,
-      GAL_OPTIONS_RANGE_GE_0,
+      0,
+      "Don't delete raw downloaded file.",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->keeprawdownload,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
     {
-      "hendwcs",
-      UI_KEY_HENDWCS,
-      "INT",
+      "information",
+      UI_KEY_INFORMATION,
       0,
-      "Header keyword number to stop reading WCS.",
-      GAL_OPTIONS_GROUP_INPUT,
-      &p->hendwcs,
-      GAL_TYPE_SIZE_T,
-      GAL_OPTIONS_RANGE_GE_0,
+      0,
+      "Print database or dataset information.",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->information,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
     {
-      "zeroisnotblank",
-      UI_KEY_ZEROISNOTBLANK,
-      0,
+      "limitinfo",
+      UI_KEY_LIMITINFO,
+      "STR",
       0,
-      "0.0 in float or double images are not blank.",
+      "Only retrieve dataset info. with this string.",
       GAL_OPTIONS_GROUP_INPUT,
-      &p->zeroisnotblank,
-      GAL_OPTIONS_NO_ARG_TYPE,
-      GAL_OPTIONS_RANGE_0_OR_1,
+      &p->limitinfo,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
-
-
-
-
-    /* Output. */
     {
-      "noblank",
-      UI_KEY_NOBLANK,
+      "dry-run",
+      UI_KEY_DRYRUN,
       0,
       0,
-      "Remove parts of the crop box out of input image.",
+      "Only print the download command, don't run it.",
       GAL_OPTIONS_GROUP_OUTPUT,
-      &p->noblank,
+      &p->dryrun,
       GAL_OPTIONS_NO_ARG_TYPE,
       GAL_OPTIONS_RANGE_0_OR_1,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+
+
+
+
+
+    /* Database and dataset. */
     {
-      "suffix",
-      UI_KEY_SUFFIX,
+      "query",
+      UI_KEY_QUERY,
       "STR",
       0,
-      "Suffix (postfix) of cropped images.",
-      GAL_OPTIONS_GROUP_OUTPUT,
-      &p->suffix,
+      "The raw query as a simple string.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->query,
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
-      GAL_OPTIONS_MANDATORY,
-      GAL_OPTIONS_NOT_SET
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
     },
 
 
 
 
 
+    /* Generate query automatically */
     {
       0, 0, 0, 0,
-      "Crop by center",
-      UI_GROUP_CENTER_GENERAL
+      "Generate query internally (not compatible with '--query'):",
+      UI_GROUP_GENQUERY,
     },
     {
-      "checkcenter",
-      UI_KEY_CHECKCENTER,
-      "FLT/INT",
+      "dataset",
+      UI_KEY_DATASET,
+      "STR",
       0,
-      "Width (in pixels) of box at center to check.",
-      UI_GROUP_CENTER_GENERAL,
-      &p->incheckcenter,
+      "Name of dataset in database.",
+      UI_GROUP_GENQUERY,
+      &p->datasetstr,
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      gal_options_parse_csv_float64
     },
     {
-      "width",
-      UI_KEY_WIDTH,
-      "FLT[,...]",
+      "overlapwith",
+      UI_KEY_OVERLAPWITH,
+      "FITS",
       0,
-      "Width when crop is defined by its center.",
-      UI_GROUP_CENTER_GENERAL,
-      &p->width,
+      "Set query region to overlap with this image.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->overlapwith,
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      gal_options_parse_csv_float64
     },
     {
       "center",
       UI_KEY_CENTER,
-      "FLT[,...]",
+      "FLT,FLT",
       0,
-      "Central coordinates of a single crop.",
-      UI_GROUP_CENTER_GENERAL,
+      "Center coords. to select by region in sky.",
+      UI_GROUP_GENQUERY,
       &p->center,
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
@@ -167,116 +173,97 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET,
       gal_options_parse_csv_float64
     },
-
-
-
-
-
-
-
-    {
-      0, 0, 0, 0,
-      "Crop by center (when a catalog is given)",
-      UI_GROUP_CENTER_CATALOG
-    },
     {
-      "catalog",
-      UI_KEY_CATALOG,
-      "STR",
+      "radius",
+      UI_KEY_RADIUS,
+      "FLT",
       0,
-      "Input catalog filename.",
-      UI_GROUP_CENTER_CATALOG,
-      &p->catname,
+      "Radius around --center to select targets.",
+      UI_GROUP_GENQUERY,
+      &p->radius,
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
-      GAL_OPTIONS_NOT_SET
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_float64
     },
     {
-      "cathdu",
-      UI_KEY_CATHDU,
-      "STR/INT",
+      "width",
+      UI_KEY_WIDTH,
+      "FLT[,FLT]",
       0,
-      "HDU of catalog, if it is a FITS table.",
-      UI_GROUP_CENTER_CATALOG,
-      &p->cathdu,
+      "Width of box around --center to select targets.",
+      UI_GROUP_GENQUERY,
+      &p->width,
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
-      GAL_OPTIONS_NOT_SET
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_float64
     },
     {
-      "namecol",
-      UI_KEY_NAMECOL,
-      "STR/INT",
+      "range",
+      UI_KEY_RANGE,
+      "STR,FLT:FLT",
       0,
-      "Column no./info of crop filename (no suffix).",
-      UI_GROUP_CENTER_CATALOG,
-      &p->namecol,
+      "Range of selected targets in given column.",
+      UI_GROUP_GENQUERY,
+      &p->range,
       GAL_TYPE_STRING,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
-      GAL_OPTIONS_NOT_SET
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_name_and_float64s
     },
     {
-      "coordcol",
-      UI_KEY_COORDCOL,
-      "STR/INT",
+      "noblank",
+      UI_KEY_NOBLANK,
+      "STR[,STR]",
       0,
-      "Column no./info containing coordinates.",
-      UI_GROUP_CENTER_CATALOG,
-      &p->coordcol,
+      "No rows with blank value in given column(s).",
+      UI_GROUP_GENQUERY,
+      &p->noblank,
       GAL_TYPE_STRLL,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
-
-
-
-
-
-    {
-      0, 0, 0, 0,
-      "Crop by region",
-      UI_GROUP_REGION
-    },
     {
-      "section",
-      UI_KEY_SECTION,
+      "column",
+      UI_KEY_COLUMN,
       "STR",
       0,
-      "Image section string specifying crop range.",
-      UI_GROUP_REGION,
-      &p->section,
-      GAL_TYPE_STRING,
+      "Column names to download from catalog.",
+      UI_GROUP_GENQUERY,
+      &p->columns,
+      GAL_TYPE_STRLL,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
     {
-      "polygon",
-      UI_KEY_POLYGON,
-      "STR",
+      "head",
+      UI_KEY_HEAD,
+      "INT",
       0,
-      "Polygon vertices of region to crop, keep inside.",
-      UI_GROUP_REGION,
-      &p->polygon,
-      GAL_TYPE_STRING,
+      "Only download given number of top rows.",
+      UI_GROUP_GENQUERY,
+      &p->head,
+      GAL_TYPE_SIZE_T,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
-      GAL_OPTIONS_NOT_SET
+      GAL_OPTIONS_NOT_SET,
     },
     {
-      "outpolygon",
-      UI_KEY_OUTPOLYGON,
-      0,
+      "sort",
+      UI_KEY_SORT,
+      "STR[,STR]",
       0,
-      "Keep the polygon's outside, mask the inside.",
-      UI_GROUP_REGION,
-      &p->outpolygon,
-      GAL_OPTIONS_NO_ARG_TYPE,
-      GAL_OPTIONS_RANGE_0_OR_1,
+      "Sort based on values of given columns.",
+      UI_GROUP_GENQUERY,
+      &p->sort,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
@@ -284,13 +271,6 @@ struct argp_option program_options[] =
 
 
 
-
-
-    /* Operating mode */
-
-
-
-
     {0}
   };
 
@@ -298,7 +278,10 @@ struct argp_option program_options[] =
 
 
 
-/* Define the child argp structure. */
+/* 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,
diff --git a/bin/fits/astfits.conf b/bin/query/astquery.conf
similarity index 58%
copy from bin/fits/astfits.conf
copy to bin/query/astquery.conf
index a69b5d9..d020099 100644
--- a/bin/fits/astfits.conf
+++ b/bin/query/astquery.conf
@@ -1,18 +1,18 @@
-# Default parameters (System) for Fits.
-# Fits is part of GNU Astronomy Utitlies.
+# Default parameters (System) for Query.
+# Query 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
-#  $ astfits --help                      # Full list of options, short doc.
-#  $ astfits -P                          # Print all options and used values.
-#  $ info astfits                        # All options and input/output.
+#  $ astquery --help                     # Full list of options, short doc.
+#  $ astquery -P                         # Print all options and used values.
+#  $ info astquery                       # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2020-2021 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/query/astron.c b/bin/query/astron.c
new file mode 100644
index 0000000..c3f41ad
--- /dev/null
+++ b/bin/query/astron.c
@@ -0,0 +1,77 @@
+/*********************************************************************
+Settings for Astron.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2021, 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 <string.h>
+
+#include <gnuastro-internal/checkset.h>
+
+#include "main.h"
+
+#include "ui.h"
+#include "tap.h"
+
+
+
+
+
+static void
+astron_sanity_checks(struct queryparams *p)
+{
+  /* Set the summarized names. */
+  if(p->datasetstr)
+    {
+      if( !strcmp(p->datasetstr, "tgssadr") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("tgssadr.main", &p->datasetstr);
+        }
+    }
+}
+
+
+
+
+
+void
+astron_prepare(struct queryparams *p)
+{
+  /* NED-specific. */
+  astron_sanity_checks(p);
+
+  /* Set the URLs, note that this is a simply-linked list, so we need to
+     reverse it in the end (with 'gal_list_str_reverse') to have the same
+     order here. */
+  gal_list_str_add(&p->urls,
+                   "https://vo.astron.nl/__system__/tap/run/tap/sync";, 0);
+
+  /* Name of default RA Dec columns. */
+  if(p->ra_name==NULL)  p->ra_name="ra";
+  if(p->dec_name==NULL) p->dec_name="dec";
+
+  /* Basic sanity checks. */
+  tap_sanity_checks(p);
+}
diff --git a/bin/TEMPLATE/TEMPLATE.h b/bin/query/astron.h
similarity index 76%
copy from bin/TEMPLATE/TEMPLATE.h
copy to bin/query/astron.h
index e75f578..eabcdc6 100644
--- a/bin/TEMPLATE/TEMPLATE.h
+++ b/bin/query/astron.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-TEMPLATE - A minimal set of files and functions to define a program.
-TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
+Settings for Astron.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2021, 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,10 +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 TEMPLATE_H
-#define TEMPLATE_H
+#ifndef ASTRON_H
+#define ASTRON_H
+
+#include "main.h"
 
 void
-TEMPLATE(struct TEMPLATEparams *p);
+astron_prepare(struct queryparams *p);
 
 #endif
diff --git a/bin/TEMPLATE/authors-cite.h b/bin/query/authors-cite.h
similarity index 75%
copy from bin/TEMPLATE/authors-cite.h
copy to bin/query/authors-cite.h
index 9090793..144e21e 100644
--- a/bin/TEMPLATE/authors-cite.h
+++ b/bin/query/authors-cite.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-TEMPLATE - A minimal set of files and functions to define a program.
-TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
+Query - Retreive data from a remote data server.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2020-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/query/gaia.c b/bin/query/gaia.c
new file mode 100644
index 0000000..135e3ca
--- /dev/null
+++ b/bin/query/gaia.c
@@ -0,0 +1,122 @@
+/*********************************************************************
+Settings for ESA's Gaia.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2020-2021, 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 <string.h>
+
+#include <gnuastro-internal/checkset.h>
+
+#include "main.h"
+
+#include "ui.h"
+#include "tap.h"
+
+
+
+
+
+/* Gaia-specific: use simpler names for the commonly used Gaia
+   datasets. This is relevant anytime that '--dataset' has been called. */
+static void
+gaia_sanity_checks(struct queryparams *p)
+{
+  /* FIRST CHECK (BEFORE SETTING DEFAULT DATASET): Gaia datasets are large
+     and it doesn't allow downloading of the full dataset anonymously! You
+     need to contact them for that. So if no constraints are given for the
+     rows, a warning will be printed. */
+  if(p->information==0
+     && p->datasetstr
+     && p->query==NULL
+     && p->center==NULL
+     && p->range==NULL
+     && p->overlapwith==NULL )
+    error(EXIT_FAILURE, 0, "no constraints specified! "
+          "In other words, you are asking for all the rows within this "
+          "dataset! Gaia datasets have billions of rows, therefore it "
+          "has a limit on the number of rows downloaded anonymously. "
+          "For bulk download access, you should contact "
+          "'gaia-helpdesk@cosmos.esa.int'. Alternatively, you can "
+          "constrain your search to a certain spatial region with "
+          "'--center=RA,DEC' (supplemented by '--radius' or '--width' in "
+          "degrees) or use '--overlapwith' to only download rows that "
+          "overlap with the provided image. See the documentation for "
+          "more with this command: 'info astquery' (press 'q' to return "
+          "to the command-line).");
+
+  /* Fix the summarized names. */
+  if(p->datasetstr)
+    {
+      if( !strcmp(p->datasetstr, "edr3") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("gaiaedr3.gaia_source", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "dr2") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("gaiadr2.gaia_source", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "dr1") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("gaiadr1.gaia_source", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "hipparcos") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("public.hipparcos", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "tycho2") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("public.tycho2", &p->datasetstr);
+        }
+    }
+}
+
+
+
+
+
+void
+gaia_prepare(struct queryparams *p)
+{
+  /* Gaia-specific settings. */
+  gaia_sanity_checks(p);
+
+  /* Set the URLs, note that this is a simply-linked list, so we need to
+     reverse it in the end (with 'gal_list_str_reverse') to have the same
+     order here. */
+  gal_list_str_add(&p->urls,
+                   "https://gea.esac.esa.int/tap-server/tap/sync";, 0);
+
+  /* Name of default RA Dec columns. */
+  if(p->ra_name==NULL)  p->ra_name="ra";
+  if(p->dec_name==NULL) p->dec_name="dec";
+
+  /* Basic sanity checks. */
+  tap_sanity_checks(p);
+}
diff --git a/bin/TEMPLATE/TEMPLATE.h b/bin/query/gaia.h
similarity index 76%
copy from bin/TEMPLATE/TEMPLATE.h
copy to bin/query/gaia.h
index e75f578..090c465 100644
--- a/bin/TEMPLATE/TEMPLATE.h
+++ b/bin/query/gaia.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-TEMPLATE - A minimal set of files and functions to define a program.
-TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
+Settings for ESA's Gaia.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2020-2021, 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,10 +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 TEMPLATE_H
-#define TEMPLATE_H
+#ifndef GAIA_H
+#define GAIA_H
+
+#include "main.h"
 
 void
-TEMPLATE(struct TEMPLATEparams *p);
+gaia_prepare(struct queryparams *p);
 
 #endif
diff --git a/bin/TEMPLATE/main.c b/bin/query/main.c
similarity index 82%
copy from bin/TEMPLATE/main.c
copy to bin/query/main.c
index 22757cb..fcc4bc4 100644
--- a/bin/TEMPLATE/main.c
+++ b/bin/query/main.c
@@ -1,11 +1,11 @@
 /*********************************************************************
-TEMPLATE - A minimal set of files and functions to define a program.
-TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
+Query - Retreive data from a remote data server.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2020-2021, 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
@@ -30,7 +30,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include "main.h"
 
 #include "ui.h"
-#include "TEMPLATE.h"
+#include "query.h"
 
 
 /* Main function */
@@ -38,7 +38,7 @@ int
 main (int argc, char *argv[])
 {
   struct timeval t1;
-  struct TEMPLATEparams p={{{0},0},0};
+  struct queryparams p={{{0},0},0};
 
   /* Set the starting time. */
   time(&p.rawtime);
@@ -47,8 +47,8 @@ main (int argc, char *argv[])
   /* Read the input parameters. */
   ui_read_check_inputs_setup(argc, argv, &p);
 
-  /* Run TEMPLATE */
-  TEMPLATE(&p);
+  /* Run Query */
+  query(&p);
 
   /* Free all non-freed allocations. */
   ui_free_report(&p, &t1);
diff --git a/bin/query/main.h b/bin/query/main.h
new file mode 100644
index 0000000..377d41c
--- /dev/null
+++ b/bin/query/main.h
@@ -0,0 +1,78 @@
+/*********************************************************************
+Query - Retreive data from a remote data server.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2020-2021, 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   "query"    /* Program full name.       */
+#define PROGRAM_EXEC   "astquery" /* Program executable name. */
+#define PROGRAM_STRING PROGRAM_NAME" (" PACKAGE_NAME ") " PACKAGE_VERSION
+
+
+
+
+
+
+
+/* Main program parameters structure */
+struct queryparams
+{
+  /* From command-line */
+  struct gal_options_common_params cp; /* Common parameters.           */
+  uint8_t      keeprawdownload;  /* Keep raw downloaded file.          */
+  uint8_t          information;  /* Print information on database.     */
+  uint8_t               dryrun;  /* Only print command, don't run it.  */
+  char              *limitinfo;  /* Limit retried dataset information. */
+  int                 database;  /* ID of database to use.             */
+  char             *datasetstr;  /* ID of dataset in database to use.  */
+  size_t                  head;  /* The number of top rows.            */
+  char            *overlapwith;  /* Image to use instead of center.    */
+  gal_list_str_t         *ccol;  /* Coordinate column names in dataset.*/
+  gal_data_t           *center;  /* Center position of query.          */
+  gal_data_t           *radius;  /* Radius around center.              */
+  gal_data_t            *range;  /* Range of magnitudes to query.      */
+  gal_list_str_t      *noblank;  /* Return rows that aren't blank.     */
+  gal_list_str_t         *sort;  /* Sort output by given column(s).    */
+  gal_data_t            *width;  /* Width of box around center.        */
+  char                  *query;  /* Raw query string.                  */
+  gal_list_str_t      *columns;  /* Columns to extract from database.  */
+
+  /* Internal variables. */
+  char            *databasestr;  /* Name of input database.            */
+  char           *downloadname;  /* Temporary output name.             */
+  size_t       outtableinfo[2];  /* To print in output.                */
+  gal_list_str_t         *urls;  /* List of URLs to use.               */
+  char                *ra_name;  /* Name of RA column.                 */
+  char               *dec_name;  /* Name of Dec columns.               */
+  char           *finalcommand;  /* The final command used.            */
+
+  /* Output: */
+  time_t               rawtime;  /* Starting time of the program.      */
+};
+
+#endif
diff --git a/bin/query/ned.c b/bin/query/ned.c
new file mode 100644
index 0000000..f2fd24f
--- /dev/null
+++ b/bin/query/ned.c
@@ -0,0 +1,88 @@
+/*********************************************************************
+Settings for NED.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2021, 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 <string.h>
+
+#include <gnuastro-internal/checkset.h>
+
+#include "main.h"
+
+#include "ui.h"
+#include "tap.h"
+
+
+
+
+
+static void
+ned_sanity_checks(struct queryparams *p)
+{
+  /* Set the summarized names. */
+  if(p->datasetstr)
+    {
+      if( !strcmp(p->datasetstr, "objdir") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("NEDTAP.objdir", &p->datasetstr);
+        }
+    }
+
+  /* Currently NED only has a single table for TAP access, so warn the
+     users about this if they ask for any other table. */
+  if( p->datasetstr==NULL || strcmp(p->datasetstr, "NEDTAP.objdir") )
+    error(EXIT_FAILURE, 0, "NED currently only supports a single "
+          "dataset with the TAP protocol called 'NEDTAP.objdir' "
+          "(which you can also call in Query with '--dataset=objdir'). "
+          "TAP access to more datasets/tables will be provided in "
+          "the future. To see all the column information and select "
+          "the columns you want for your work, please run this command:\n\n"
+          "    astquery %s --dataset=objdir --info", p->databasestr);
+}
+
+
+
+
+
+void
+ned_prepare(struct queryparams *p)
+{
+  /* NED-specific. */
+  ned_sanity_checks(p);
+
+  /* Set the URLs, note that this is a simply-linked list, so we need to
+     reverse it in the end (with 'gal_list_str_reverse') to have the same
+     order here. */
+  gal_list_str_add(&p->urls,
+                   "https://ned.ipac.caltech.edu/tap/sync";, 0);
+
+  /* Name of default RA Dec columns. */
+  if(p->ra_name==NULL)  p->ra_name="ra";
+  if(p->dec_name==NULL) p->dec_name="dec";
+
+  /* Basic sanity checks. */
+  tap_sanity_checks(p);
+}
diff --git a/bin/TEMPLATE/TEMPLATE.h b/bin/query/ned.h
similarity index 76%
copy from bin/TEMPLATE/TEMPLATE.h
copy to bin/query/ned.h
index e75f578..01effeb 100644
--- a/bin/TEMPLATE/TEMPLATE.h
+++ b/bin/query/ned.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-TEMPLATE - A minimal set of files and functions to define a program.
-TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
+Settings for NED.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2021, 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,10 +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 TEMPLATE_H
-#define TEMPLATE_H
+#ifndef NED_H
+#define NED_H
+
+#include "main.h"
 
 void
-TEMPLATE(struct TEMPLATEparams *p);
+ned_prepare(struct queryparams *p);
 
 #endif
diff --git a/bin/query/query.c b/bin/query/query.c
new file mode 100644
index 0000000..b8289b5
--- /dev/null
+++ b/bin/query/query.c
@@ -0,0 +1,392 @@
+/*********************************************************************
+Query - Retreive data from a remote data server.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2020-2021, 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 <string.h>
+
+#include <gnuastro/wcs.h>
+#include <gnuastro/pointer.h>
+
+#include <gnuastro-internal/checkset.h>
+
+#include "tap.h"
+#include "ned.h"
+#include "gaia.h"
+#include "query.h"
+#include "astron.h"
+#include "vizier.h"
+
+
+
+
+
+/* Go over the columns and see if a given column exists. */
+static int
+query_output_meta_col(gal_list_str_t **cols, gal_data_t *allcols,
+                      size_t numcols, char *string)
+{
+  size_t i;
+  for(i=0; i<numcols; ++i)
+    if( !strcmp(allcols[i].name, string) )
+      {
+        gal_list_str_add(cols, string, 0);
+        return 1;
+      }
+  return 0;
+}
+
+
+
+
+
+/* Read the downloaded metadata for all the tables (datasets) within a
+   database and print them in an easy to read format. */
+static void
+query_output_meta_database(struct queryparams *p)
+{
+  int tableformat;
+  size_t i, *size=NULL;
+  size_t numcols, numrows;
+  gal_list_str_t *cols=NULL;
+  char **name, **type, **desc;
+  gal_data_t *table, *allcols, *scol=NULL;
+
+  /* Get the downloaded metadata column information so we can ask for the
+     proper columns. */
+  allcols=gal_table_info(p->downloadname, "1", NULL, &numcols,
+                         &numrows, &tableformat);
+
+  /* Parse the column information to set the necessary columns. */
+  if( query_output_meta_col(&cols, allcols, numcols, "table_name") == 0 )
+    { error(EXIT_SUCCESS, 0, "no 'table_name' found, but this "
+            "is required by the IVOA TAP standard"); return; }
+  if( query_output_meta_col(&cols, allcols, numcols, "description") == 0 )
+    { error(EXIT_SUCCESS, 0, "no 'description' found, but this "
+            "is required by the IVOA TAP standard"); return; }
+  if( query_output_meta_col(&cols, allcols, numcols, "table_type") == 0 )
+    { error(EXIT_SUCCESS, 0, "no 'table_type' found, but this "
+            "is required by the IVOA TAP standard"); return; }
+  query_output_meta_col(&cols, allcols, numcols, "size");
+
+  /* Read the necessary columns in the desired order, we just need to
+     reverse the list first since it is last-in-first-out. */
+  gal_list_str_reverse(&cols);
+  table=gal_table_read(p->downloadname, "1", NULL, cols,
+                       GAL_TABLE_SEARCH_NAME, 1, p->cp.minmapsize,
+                       p->cp.quietmmap, NULL);
+
+  /* Set the basic columns for easy reading. */
+  name=table->array;
+  desc=table->next->array;
+  type=table->next->next->array;
+  if(table->next->next->next)
+    {
+      scol=gal_data_copy_to_new_type_free(table->next->next->next,
+                                          GAL_TYPE_SIZE_T);
+      table->next->next->next=scol;
+      size=scol->array;
+    }
+
+  /* Print all the information for those tables that have a type of
+     'table'. If I understood the TAP standard properly, the 'view' ones
+     aren't relevant for non-webpage users. */
+  printf("\nRETRIEVED DATASET INFORMATION\n"
+         "=============================\n");
+  printf("Database: %s (URL: %s)\n", p->databasestr, p->urls->v);
+  if(p->limitinfo)
+    printf("Only datasets containing string below in description "
+           "(case sensitive): '%s'\n", p->limitinfo);
+  if(table->size)
+    {
+      for(i=0;i<table->size;++i)
+        if( !strcmp(type[i],"table") )
+          {
+            printf("\n%zu of %zu\n"
+                   "==================\n"
+                   "DATASET NAME: %s\n"
+                   "------------------\n", i+1, table->size, name[i]);
+            if(size)
+              printf("DATASET SIZE (number of rows): %zu\n"
+                     "------------------\n", size[i]);
+            printf("DATASET DESCRIPTION:\n%s\n"
+                   "==================\n", desc[i]);
+          }
+    }
+  else printf("\nNO DATASET FOUND!\n");
+
+  /* Clean up and return. */
+  gal_list_data_free(table);
+  gal_data_array_free(allcols, numcols, 0);
+}
+
+
+
+
+
+static uint8_t
+query_type_from_tap(char *typestr)
+{
+  uint8_t type;
+  if(      !strcmp(typestr,"BOOLEAN")  ) type=GAL_TYPE_UINT8;
+  else if( !strcmp(typestr,"BIGINT")   ) type=GAL_TYPE_INT64;
+  else if( !strcmp(typestr,"REAL")     ) type=GAL_TYPE_FLOAT32;
+  else if( !strcmp(typestr,"DOUBLE")   ) type=GAL_TYPE_FLOAT64;
+  else if( !strcmp(typestr,"SMALLINT")
+           || !strcmp(typestr,"INTEGER") ) type=GAL_TYPE_INT32;
+  else if( !strcmp(typestr,"VARCHAR")
+           || !strcmp(typestr,"STRING")
+           || !strncmp(typestr, "CHAR", 4) ) type=GAL_TYPE_STRING;
+  else type=GAL_TYPE_INVALID;
+  return type;
+}
+
+
+
+
+/* Read the downloaded metadata for all the columns within a table
+   (dataset) and print them in an easy to read format. */
+static void
+query_output_meta_dataset(struct queryparams *p)
+{
+  size_t i;
+  int tableformat;
+  size_t numcols, numrows;
+  gal_list_str_t *cols=NULL;
+  gal_data_t *table, *allcols;
+  char **name, **type, **unit, **desc;
+
+  /* Get the downloaded metadata column information so we can ask for the
+     proper columns. */
+  allcols=gal_table_info(p->downloadname, "1", NULL, &numcols,
+                         &numrows, &tableformat);
+
+  /* Parse the column information to set the necessary columns. */
+  if( query_output_meta_col(&cols, allcols, numcols, "column_name") == 0 )
+    { error(EXIT_SUCCESS, 0, "no 'column_name' found, but this "
+            "is required by the IVOA TAP standard"); return; }
+  if( query_output_meta_col(&cols, allcols, numcols, "datatype") == 0 )
+    { error(EXIT_SUCCESS, 0, "no 'datatype' found, but this "
+            "is required by the IVOA TAP standard"); return; }
+  if( query_output_meta_col(&cols, allcols, numcols, "description") == 0 )
+    { error(EXIT_SUCCESS, 0, "no 'description' found, but this "
+            "is required by the IVOA TAP standard"); return; }
+  if( query_output_meta_col(&cols, allcols, numcols, "unit") == 0 )
+    { error(EXIT_SUCCESS, 0, "no 'unit' found, but this "
+            "is required by the IVOA TAP standard"); return; }
+
+  /* Read the necessary columns in the desired order, we just need to
+     reverse the list first since it is last-in-first-out. */
+  gal_list_str_reverse(&cols);
+  table=gal_table_read(p->downloadname, "1", NULL, cols,
+                       GAL_TABLE_SEARCH_NAME, 1, p->cp.minmapsize,
+                       p->cp.quietmmap, NULL);
+
+  /* It may happen that the required dataset name isn't recognized by the
+     database. In this case, 'table' will have 0 rows. */
+  if(table->size)
+    {
+      /* Free the initial 'allcols' and set the array pointers.  */
+      name=table->array;
+      type=table->next->array;
+      desc=table->next->next->array;
+      unit=table->next->next->next->array;
+      gal_data_array_free(allcols, numcols, 0);
+
+      /* Allocate the new 'allcols' and fill it with the metadata. */
+      numcols=table->size;
+      allcols=gal_data_array_calloc(numcols);
+      for(i=0;i<numcols;++i)
+        {
+          allcols[i].type=query_type_from_tap(type[i]);
+          gal_checkset_allocate_copy(name[i], &allcols[i].name);
+          gal_checkset_allocate_copy(desc[i], &allcols[i].comment);
+          if( !strcmp(unit[i]," ") ) allcols[i].unit=NULL;
+          else gal_checkset_allocate_copy(unit[i], &allcols[i].unit);
+        }
+
+      /* Print the basic information. */
+      printf("\n--------\ndatabase: %s (URL: %s)\ndataset: %s\n",
+             p->databasestr, p->urls->v, p->datasetstr);
+      gal_table_print_info(allcols, numcols, GAL_BLANK_SIZE_T);
+    }
+  else
+    {
+      /* We are using 'error' to have the progam name at the start, and so
+         it goes to 'stderr'. But we aren't exiting with 'EXIT_FAILURE',
+         because Query still has work to do (for example deleting the
+         temporarily downloaded file). */
+      printf("\n");
+      error(EXIT_SUCCESS, 0, "no '%s' dataset found in the '%s' database. "
+            "For the list of datasets within this database, please run the "
+            "command below (put any search word or phrase in 'SEARCH' to "
+            "find your dataset more easily):\n\n"
+            "   astquery %s --information --limitinfo=\"SEARCH\"\n\n",
+            p->datasetstr, p->databasestr, p->databasestr);
+    }
+
+  /* Clean up and return. */
+  gal_list_data_free(table);
+  gal_data_array_free(allcols, numcols, 0);
+}
+
+
+
+
+
+/* Read the raw download data, and write them into the output file with
+   Gnuastro's own library. */
+static void
+query_output_data(struct queryparams *p)
+{
+  gal_data_t *table;
+
+  /* Read the table and write it into a clean output (in case the
+     downloaded table is compressed in any special FITS way). */
+  table=gal_table_read(p->downloadname, "1", NULL, NULL,
+                       GAL_TABLE_SEARCH_NAME, 1, p->cp.minmapsize,
+                       p->cp.quietmmap, NULL);
+  gal_table_write(table, NULL, NULL, p->cp.tableformat,
+                  p->cp.output ? p->cp.output : p->cp.output,
+                  "QUERY", 0);
+
+  /* Get basic information about the table and free it. */
+  p->outtableinfo[0]=table->size;
+  p->outtableinfo[1]=gal_list_data_number(table);
+  gal_list_data_free(table);
+}
+
+
+
+
+
+void
+query_check_download(struct queryparams *p)
+{
+  size_t len;
+  int status=0;
+  char *logname;
+  fitsfile *fptr;
+
+  /* Open the FITS file and if the status value is still zero, it means
+     everything worked properly. */
+  fits_open_file(&fptr, p->downloadname, READONLY, &status);
+  if(status==0)
+    {
+      /* Close the FITS file pointer. */
+      fits_close_file(fptr, &status);
+
+      /* Prepare the output dataset. */
+      if(p->information)
+        {
+          if(p->datasetstr)  query_output_meta_dataset(p);
+          else               query_output_meta_database(p);
+        }
+      else               query_output_data(p);
+
+      /* Delete the raw downloaded file if necessary. */
+      if(p->keeprawdownload==0) remove(p->downloadname);
+    }
+  else
+    {
+      /* Add a '.log' suffix to the output filename. */
+      len=strlen(p->downloadname);
+      logname=gal_pointer_allocate(GAL_TYPE_STRING, len+10, 1,
+                                   __func__, "logname");
+      sprintf(logname, "%s.log", p->downloadname);
+
+      /* Rename the output file to the logname file and let the user
+         know. */
+      rename(p->downloadname, logname);
+      if(p->cp.quiet==0) printf("\n");
+      error(EXIT_FAILURE, 0, "the requested dataset could not be "
+            "retrieved! For more, please see '%s'", logname);
+    }
+
+  /* Add the query keywords to the first extension (if the output was a
+     FITS file). */
+  if( p->information==0 && gal_fits_name_is_fits(p->cp.output) )
+    {
+      gal_fits_key_list_title_add_end(&p->cp.okeys,
+                                      "Constructed query command", 0);
+      gal_fits_key_list_fullcomment_add_end(&p->cp.okeys,
+                                            p->finalcommand, 1);
+      gal_fits_key_write_config(&p->cp.okeys, "Query settings",
+                                "QUERY-CONFIG", p->cp.output, "0");
+    }
+}
+
+
+
+
+
+void
+query(struct queryparams *p)
+{
+  /* Download the dataset. */
+  switch(p->database)
+    {
+    case QUERY_DATABASE_ASTRON: astron_prepare(p); break;
+    case QUERY_DATABASE_GAIA:   gaia_prepare(p);   break;
+    case QUERY_DATABASE_NED:    ned_prepare(p);    break;
+    case QUERY_DATABASE_VIZIER: vizier_prepare(p); break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+            "address the problem. '%d' is not a recognized database "
+            "code", __func__, PACKAGE_BUGREPORT, p->database);
+    }
+
+  /* Download the requested query. */
+  tap_download(p);
+
+  /* Make sure that the result is a readable FITS file, otherwise, abort
+     with an error. */
+  if(p->dryrun==0)
+    query_check_download(p);
+
+  /* Let the user know that things went well. */
+  if(p->dryrun==0 && p->cp.quiet==0)
+    {
+      if(p->information==0)
+        printf("\nQuery resulted in %zu rows and %zu columns.\n",
+               p->outtableinfo[0], p->outtableinfo[1]);
+      if(p->keeprawdownload)
+        {
+          if(p->information) printf("\n");
+          printf("Query's raw downloaded file: %s\n", p->downloadname);
+        }
+      if(p->information==0)
+        {
+          printf("Query's final output: %s\n", p->cp.output);
+          printf("TIP: use the command below for more on the "
+                 "downloaded table:\n"
+                 "   asttable %s --info\n", p->cp.output);
+        }
+    }
+
+  /* Clean up. */
+  gal_list_str_free(p->urls, 0);
+}
diff --git a/bin/TEMPLATE/TEMPLATE.h b/bin/query/query.h
similarity index 68%
copy from bin/TEMPLATE/TEMPLATE.h
copy to bin/query/query.h
index e75f578..81fed39 100644
--- a/bin/TEMPLATE/TEMPLATE.h
+++ b/bin/query/query.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-TEMPLATE - A minimal set of files and functions to define a program.
-TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
+Query - Retreive data from a remote data server.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2020-2021, 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,10 +20,22 @@ 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 TEMPLATE_H
-#define TEMPLATE_H
+#ifndef QUERY_H
+#define QUERY_H
+
+#include "main.h"
+
+enum query_databases
+{
+  QUERY_DATABASE_INVALID,
+  QUERY_DATABASE_ASTRON,
+  QUERY_DATABASE_GAIA,
+  QUERY_DATABASE_NED,
+  QUERY_DATABASE_VIZIER,
+};
+
 
 void
-TEMPLATE(struct TEMPLATEparams *p);
+query(struct queryparams *p);
 
 #endif
diff --git a/bin/query/tap.c b/bin/query/tap.c
new file mode 100644
index 0000000..f934644
--- /dev/null
+++ b/bin/query/tap.c
@@ -0,0 +1,440 @@
+/*********************************************************************
+Table Access Protocol (TAP) based download of given query.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2021, 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 <gnuastro/wcs.h>
+
+#include <gnuastro-internal/checkset.h>
+
+#include "main.h"
+
+#include "ui.h"
+
+
+
+
+
+/* Basic sanity checks necessary in all TAP-based databases. */
+void
+tap_sanity_checks(struct queryparams *p)
+{
+  /* Checks in case a raw query isn't given. */
+  if(p->query==NULL)
+    {
+      /* If '--center' is given, '--radius' is also necessary. */
+      if(p->center || p->overlapwith)
+        {
+          /* Make sure the radius is given, and that it isn't zero. */
+          if(p->center && p->radius==NULL && p->width==NULL)
+            error(EXIT_FAILURE, 0, "the '--radius' ('-r') or "
+                  "'--width' ('-w') options are necessary with "
+                  "the '--center' ('-C') option");
+        }
+
+      /* If no dataset is explicitly given, let the user know that a
+         catalog reference is necessary. */
+      if( p->information==0 && p->datasetstr==NULL )
+        error(EXIT_FAILURE, 0, "no '--dataset' specified! To get the "
+              "list of available datasets (tables) in this database, "
+              "please run with '--information' (or '-i'). Note that some "
+              "databases (like VizieR) have (tens of) thousands of "
+              "datasets. Hence, for a fast result, its best to limit the "
+              "downloaded and displayed information list by also adding "
+              "--limitinfo=\"SEARCH\" (where 'SEARCH' can be any string "
+              "in the description of the dataset, usually project or "
+              "author names) for example:\n\n"
+              "    astquery %s -i --limitinfo=\"SEARCH_STRING\"\n\n"
+              "For more, see the documentation of 'astquery' and the "
+              "\"Available databases\" section of the book for more:\n\n"
+              "    info astquery\n"
+              "    info gnuastro \"Available databases\"\n",
+              p->databasestr);
+    }
+}
+
+
+
+
+
+/* The database string needs to be quoted if it contains a slash. */
+static char *
+tap_dataset_quote_if_necessary(struct queryparams *p)
+{
+  int quote=0;
+  char *c, *quoted;
+
+  /* Parse the string for bad characters */
+  for(c=p->datasetstr; *c!='\0'; ++c)
+    if(*c=='/') { quote=1; break; }
+
+  /* Add quotes around the database string. */
+  if(quote)
+    {
+      /* Allocate the new string with quotes. */
+      if( asprintf(&quoted, "\"%s\"", p->datasetstr)<0 )
+        error(EXIT_FAILURE, 0, "%s: asprintf allocation ('quoted')",
+              __func__);
+    }
+  else quoted=p->datasetstr;
+
+  /* Return the possibly quoted string. */
+  return quoted;
+}
+
+
+
+
+
+/* Construct the query for metadata download. */
+static char *
+tap_query_construct_meta(struct queryparams *p)
+{
+  char *querystr;
+
+  /* If a dataset is given, build the query to download the metadata of
+     that dataset. Otherwise, get the metadata of the full database. */
+  if(p->datasetstr)
+    {
+      if( asprintf(&querystr,  "\"SELECT * FROM TAP_SCHEMA.columns "
+                   "WHERE table_name = '%s'\"", p->datasetstr)<0 )
+        error(EXIT_FAILURE, 0, "%s: asprintf allocation ('querystr')",
+              __func__);
+    }
+  else
+    {
+      if(p->limitinfo)
+        {
+          if( asprintf(&querystr,  "\"SELECT * FROM TAP_SCHEMA.tables "
+                       "WHERE description LIKE '%%%s%%'\"", p->limitinfo)<0 )
+            error(EXIT_FAILURE, 0, "%s: asprintf allocation ('querystr')",
+                  __func__);
+        }
+      else
+        gal_checkset_allocate_copy("\"SELECT * FROM TAP_SCHEMA.tables\"",
+                                   &querystr);
+    }
+
+  /* Clean up and return. */
+  return querystr;
+}
+
+
+
+
+
+/* Construct the spatial-constraints criteria if necessary. */
+static char *
+tap_query_construct_spatial(struct queryparams *p)
+{
+  size_t ndim;
+  double width2, *center, *darray;
+  char *regionstr, *spatialstr=NULL;
+  double *ocenter=NULL, *owidth=NULL, *omin=NULL, *omax=NULL;
+
+  /* If the user wanted an overlap with an image, then calculate it. */
+  if(p->overlapwith)
+    {
+      /* Calculate the Sky coverage of the overlap dataset. */
+      gal_wcs_coverage(p->overlapwith, p->cp.hdu, &ndim, &ocenter,
+                       &owidth, &omin, &omax);
+
+      /* Make sure a WCS existed in the file. */
+      if(owidth==NULL)
+        error(EXIT_FAILURE, 0, "%s (hdu %s): contains no WCS to "
+              "derive the sky coverage", p->overlapwith, p->cp.hdu);
+    }
+
+  /* For easy reading. */
+  center = p->overlapwith ? ocenter : p->center->array;
+
+  /* Write the region. */
+  if(p->radius)
+    {
+      darray=p->radius->array;
+      if( asprintf(&regionstr, "CIRCLE('ICRS', %.8f, %.8f, %g)",
+                   center[0], center[1], darray[0])<0 )
+        error(EXIT_FAILURE, 0, "%s: asprintf allocation ('regionstr')",
+              __func__);
+    }
+  else if(p->width || p->overlapwith)
+    {
+      darray = p->overlapwith ? owidth : p->width->array;
+      width2 = ( (p->overlapwith || p->width->size==2)
+                 ? darray[1] : darray[0] );
+      if( asprintf( &regionstr, "BOX('ICRS', %.8f, %.8f, %.8f, %.8f)",
+                    center[0], center[1], darray[0], width2 )<0 )
+        error(EXIT_FAILURE, 0, "%s: asprintf allocation ('regionstr')",
+              __func__);
+    }
+
+  /* Build the final spatial constraint query string. Note on the
+     quotations: the final query is surrounded by single-quotes
+     ('). However, we need the single quotes around 'ICRS' in this command
+     (both in the final string below and the ones above). So just before
+     the first 'ICRS', we end the single-quote and start a double quote and
+     keep it until the end. Finally, we add a single quote again so all
+     other components of the query can assume that the single-quote
+     environment is active.*/
+  if( asprintf(&spatialstr,
+               "1=CONTAINS( POINT('\"'ICRS', %s, %s), %s )\"'",
+               p->ra_name, p->dec_name, regionstr)<0 )
+    error(EXIT_FAILURE, 0, "%s: asprintf allocation ('querystr')",
+          __func__);
+
+
+  /* Clean up and return. */
+  free(regionstr);
+  if(p->overlapwith)
+    { free(ocenter); free(owidth); free(omin); free(omax); }
+  return spatialstr;
+}
+
+
+
+
+
+static void
+tap_query_construct_noblank(struct queryparams *p, char **outstr)
+{
+  gal_list_str_t *tmp;
+  char *noblankstr=NULL, *prevstr=*outstr;
+
+  for(tmp=p->noblank; tmp!=NULL; tmp=tmp->next)
+    {
+      /* Write 'rangestr'. */
+      if(prevstr)
+        {
+          if( asprintf(&noblankstr, "%s AND %s IS NOT NULL",
+                       prevstr, tmp->v) < 0 )
+            error(EXIT_FAILURE, 0,
+                  "%s: asprintf allocation ('noblankstr', 1)", __func__);
+          free(prevstr);
+        }
+      else
+        if( asprintf(&noblankstr, "%s IS NOT NULL", tmp->v) < 0 )
+          error(EXIT_FAILURE, 0,
+                "%s: asprintf allocation ('noblankstr', 2)", __func__);
+
+      /* Put the 'rangestr' in previous-range string for the next
+         round.*/
+      prevstr=noblankstr;
+    }
+
+  /* Set the final output pointer. */
+  *outstr=noblankstr;
+}
+
+
+
+
+
+static void
+tap_query_construct_range(struct queryparams *p, char **outstr)
+{
+  double *darray;
+  gal_data_t *tmp;
+  char *rangestr=NULL, *prevstr=*outstr;
+
+  for(tmp=p->range; tmp!=NULL; tmp=tmp->next)
+    {
+      /* Write 'rangestr'. */
+      darray=tmp->array;
+      if(prevstr)
+        {
+          if( asprintf(&rangestr, "%s AND %s>=%g AND %s<=%g", prevstr,
+                       tmp->name, darray[0], tmp->name, darray[1]) < 0 )
+            error(EXIT_FAILURE, 0,
+                  "%s: asprintf allocation ('rangestr', 1)", __func__);
+          free(prevstr);
+        }
+      else
+        if( asprintf(&rangestr, "%s>=%g AND %s<=%g",
+                     tmp->name, darray[0], tmp->name, darray[1]) < 0 )
+          error(EXIT_FAILURE, 0,
+                "%s: asprintf allocation ('rangestr', 2)", __func__);
+
+      /* Put the 'rangestr' in previous-range string for the next
+         round.*/
+      prevstr=rangestr;
+    }
+
+  /* Set the final output pointer. */
+  *outstr=rangestr;
+}
+
+
+
+
+
+static char *
+tap_query_construct_sort(struct queryparams *p)
+{
+  gal_list_str_t *tmp;
+  char *sortstr=NULL, *prevstr=NULL;
+
+  for(tmp=p->sort; tmp!=NULL; tmp=tmp->next)
+    {
+      /* Write 'rangestr'. */
+      if(prevstr)
+        {
+          if( asprintf(&sortstr, "%s,%s", prevstr, tmp->v) < 0 )
+            error(EXIT_FAILURE, 0,
+                  "%s: asprintf allocation ('sortstr', 1)", __func__);
+          free(prevstr);
+        }
+      else
+        if( asprintf(&sortstr, "ORDER BY %s", tmp->v) < 0 )
+          error(EXIT_FAILURE, 0,
+                "%s: asprintf allocation ('sortstr', 2)", __func__);
+
+      /* Put the 'rangestr' in previous-range string for the next
+         round.*/
+      prevstr=sortstr;
+    }
+
+  /* Set the final output pointer. */
+  return sortstr;
+}
+
+
+
+
+
+/* Construct the query for data download. */
+static char *
+tap_query_construct_data(struct queryparams *p)
+{
+  char *sortstr=NULL;
+  char *headstr=NULL, allcols[]="*";
+  char *datasetstr, *valuelimitstr=NULL;
+  char *querystr, *columns, *spatialstr=NULL;
+
+  /* If the dataset has special characters (like a slash) it needs to
+     be quoted. */
+  datasetstr=tap_dataset_quote_if_necessary(p);
+
+  /* If certain columns have been requested use them, otherwise
+     download all existing columns.*/
+  columns = p->columns ? ui_strlist_to_str(p->columns) : allcols;
+
+  /* If the user only wants the top few rows, only return them. */
+  if(p->head!=GAL_BLANK_SIZE_T)
+    if( asprintf(&headstr, "TOP %zu", p->head)<0 )
+      error(EXIT_FAILURE, 0, "%s: asprintf allocation ('head')",
+            __func__);
+
+  /* Build the 'noblank' and 'range' criteria. No blank goes first because
+     it is easier to check (for the server), thus the more time-consuming
+     range check can be done on fewer rows. */
+  if(p->noblank) tap_query_construct_noblank(p, &valuelimitstr);
+  if(p->range) tap_query_construct_range(p, &valuelimitstr);
+
+  /* If the user has asked for a spatial constraint. */
+  if(p->overlapwith || p->center)
+    spatialstr=tap_query_construct_spatial(p);
+
+  /* If the user has asked to sort the columns. */
+  if(p->sort) sortstr=tap_query_construct_sort(p);
+
+  /* Write the automatically generated query string.  */
+  if( asprintf(&querystr,  "'SELECT %s %s FROM %s %s %s %s %s %s'",
+               headstr ? headstr : "",
+               columns,
+               datasetstr,
+               ( valuelimitstr || spatialstr ? "WHERE" : ""),
+               valuelimitstr ? valuelimitstr : "",
+               ( valuelimitstr && spatialstr ? "AND"   : "" ),
+               spatialstr ? spatialstr : "",
+               sortstr ? sortstr : "")<0 )
+    error(EXIT_FAILURE, 0, "%s: asprintf allocation ('querystr')",
+          __func__);
+
+  /* Clean up and return. */
+  if(datasetstr!=p->datasetstr) free(datasetstr);
+  if(columns!=allcols) free(columns);
+  return querystr;
+}
+
+
+
+
+void
+tap_download(struct queryparams *p)
+{
+  gal_list_str_t *url;
+  char *command, *querystr;
+
+  /* If the raw query has been given, use it. */
+  querystr = ( p->query
+               ? p->query
+               : ( p->information
+                   ? tap_query_construct_meta(p)
+                   : tap_query_construct_data(p) ) );
+
+  /* Go over the given URLs for this server. */
+  for(url=p->urls; url!=NULL; url=url->next)
+    {
+      /* Build the calling command. Note that the query quotes are
+         included by the function building it. */
+      if( asprintf(&command, "curl%s -o%s --form LANG=ADQL "
+                   "--form FORMAT=fits --form REQUEST=doQuery "
+                   "--form QUERY=%s %s", p->cp.quiet ? " -s" : "",
+                   p->downloadname, querystr, url->v)<0 )
+        error(EXIT_FAILURE, 0, "%s: asprintf allocation ('command')",
+              __func__);
+
+      /* Print the calling command for the user to know. */
+      if(p->dryrun==1 || p->cp.quiet==0)
+        {
+          if(p->dryrun==0) printf("\n");
+          error(EXIT_SUCCESS, 0, "%s: %s",
+                p->dryrun ? "would run" : "running", command);
+          if(p->dryrun==0) printf("\nDownload status:\n");
+        }
+
+      /* Run the command: if it succeeds ('system' returns zero), then stop
+         parsing with a break. If it fails, then see if this is the last
+         URL or not. If its the last one ('url->next' is NULL), then exit
+         with a failure, otherwise, just let the user know that this
+         download failed, but continue with the next URLs. */
+      if(p->dryrun) break;
+      else
+        {
+          if(system(command)==EXIT_SUCCESS) break;
+          else
+            error(url->next ? EXIT_SUCCESS : EXIT_FAILURE, 0,
+                  "the query download command %sfailed%s\n",
+                  p->cp.quiet==0 ? "printed above " : "",
+                  p->cp.quiet==0 ? "" : " (the command can be printed "
+                  "if you don't use the option '--quiet', or '-q')");
+        }
+    }
+
+  /* Keep the executed command (to put in the final file's meta-data). */
+  p->finalcommand=command;
+}
diff --git a/bin/TEMPLATE/TEMPLATE.h b/bin/query/tap.h
similarity index 73%
copy from bin/TEMPLATE/TEMPLATE.h
copy to bin/query/tap.h
index e75f578..02b7c9c 100644
--- a/bin/TEMPLATE/TEMPLATE.h
+++ b/bin/query/tap.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-TEMPLATE - A minimal set of files and functions to define a program.
-TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
+Table Access Protocol (TAP) based download of given query.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2021, 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,10 +20,15 @@ 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 TEMPLATE_H
-#define TEMPLATE_H
+#ifndef TAP_H
+#define TAP_H
+
+#include "main.h"
+
+void
+tap_sanity_checks(struct queryparams *p);
 
 void
-TEMPLATE(struct TEMPLATEparams *p);
+tap_download(struct queryparams *p);
 
 #endif
diff --git a/bin/query/ui.c b/bin/query/ui.c
new file mode 100644
index 0000000..ed37313
--- /dev/null
+++ b/bin/query/ui.c
@@ -0,0 +1,578 @@
+/*********************************************************************
+Query - Retreive data from a remote data server.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2020-2021, 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 <string.h>
+
+#include <gnuastro/fits.h>
+#include <gnuastro/pointer.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 "query.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[] = "DATABASE";
+
+const char
+doc[] = GAL_STRINGS_TOP_HELP_INFO PROGRAM_NAME" is just a place holder "
+  "used as a minimal set of files and functions necessary for a program in "
+  "Gnuastro. It can be used for learning or as a template to build new "
+  "programs.\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 queryparams *p,
+                      struct argp_option *program_options,
+                      struct argp_option *gal_commonopts_options)
+{
+  size_t i;
+  struct gal_options_common_params *cp=&p->cp;
+
+
+  /* Set the necessary common parameters structure. */
+  cp->program_struct     = p;
+  cp->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;
+
+  /* Program-specific initializations. */
+  p->head                = GAL_BLANK_SIZE_T;
+
+  /* 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_LOG:
+        case GAL_OPTIONS_KEY_TYPE:
+        case GAL_OPTIONS_KEY_SEARCHIN:
+        case GAL_OPTIONS_KEY_QUIETMMAP:
+        case GAL_OPTIONS_KEY_IGNORECASE:
+        case GAL_OPTIONS_KEY_NUMTHREADS:
+        case GAL_OPTIONS_KEY_MINMAPSIZE:
+        case GAL_OPTIONS_KEY_STDINTIMEOUT:
+          cp->coptions[i].flags=OPTION_HIDDEN;
+          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;
+        }
+    }
+}
+
+
+
+
+
+/* Fixed string */
+#define UI_NODATABASE "Please use the '--database' ('-d') option to "   \
+  "specify your desired database, see manual ('info gnuastro "          \
+  "astquery' command) for the current databases, here is the list "     \
+  "of acceptable values (with their web-based search URLs):\n\n"        \
+  "    astron     https://vo.astron.nl/\n";                              \
+  "    gaia       https://gea.esac.esa.int/archive\n";                   \
+  "    ned        https://ned.ipac.caltech.edu/tap/sync\n";              \
+  "    vizier     http://vizier.u-strasbg.fr/viz-bin/VizieR\n";
+
+
+
+
+/* Parse a single option: */
+error_t
+parse_opt(int key, char *arg, struct argp_state *state)
+{
+  struct queryparams *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:
+      p->databasestr=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;
+}
+
+
+
+
+
+char *
+ui_strlist_to_str(gal_list_str_t *input)
+{
+  char *out=NULL;
+  gal_list_str_t *node;
+  size_t n, nn, nnodes=0, alllen=0;
+
+  /* First calculate the full length of all nodes. */
+  for(node=input; node!=NULL; node=node->next)
+    {
+      /* We'll add two extra for each. One for the ',' that must come in
+         between it and the next one. One just for a buffer, incase we
+         haven't accounted for something. */
+      alllen += strlen(node->v) + 2;
+      ++nnodes;
+    }
+
+  /* Allocate the output string. */
+  out=gal_pointer_allocate(GAL_TYPE_STRING, alllen, 1, "out", __func__);
+
+  /* Write all the strings into the allocated space. */
+  n=nn=0;
+  for(node=input; node!=NULL; node=node->next)
+    {
+      if(nn++==nnodes-1)
+        sprintf(out+n, "%s", node->v);
+      else
+        n += sprintf(out+n, "%s,", node->v);
+    }
+
+  /* Return the merged string. */
+  return out;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/***************       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 queryparams *p)
+{
+  size_t i;
+  double *darray;
+  char *basename;
+  gal_data_t *tmp;
+  int keepinputdir;
+
+  /* See if database has been specified. */
+  if(p->databasestr==NULL)
+    error(EXIT_FAILURE, 0, "no input database! " UI_NODATABASE);
+
+  /* Convert the given string into a code. */
+  if(      !strcmp(p->databasestr, "astron") ) 
p->database=QUERY_DATABASE_ASTRON;
+  else if( !strcmp(p->databasestr, "gaia") )   p->database=QUERY_DATABASE_GAIA;
+  else if( !strcmp(p->databasestr, "ned") )    p->database=QUERY_DATABASE_NED;
+  else if( !strcmp(p->databasestr, "vizier") ) 
p->database=QUERY_DATABASE_VIZIER;
+  else
+    error(EXIT_FAILURE, 0, "'%s' is not a recognized database.\n\n"
+          "For the full list of recognized databases, please see the "
+          "documentation (with the command 'info astquery')", p->databasestr);
+
+  /* If '--limitinfo' is given, but the string is empty (possibly due to a
+     shell variable that wasn't set), remove it. */
+  if(p->limitinfo && p->limitinfo[0]=='\0')
+    {
+      free(p->limitinfo);
+      p->limitinfo=NULL;
+      for(i=0; !gal_options_is_last(&p->cp.poptions[i]); ++i)
+        if( p->cp.poptions[i].key == UI_KEY_LIMITINFO )
+          p->cp.poptions[i].set=GAL_OPTIONS_NOT_SET;
+    }
+
+  /* If '--ccol' is given, first merge all possible calls to it, confirm
+     that there are only two values and put them into the 'ra_name' and
+     'dec_name' variables. */
+  if(p->ccol)
+    {
+      gal_options_merge_list_of_csv(&p->ccol);
+      if(gal_list_str_number(p->ccol)!=2)
+        error(EXIT_FAILURE, 0, "2 values should be given to '--ccol', "
+              "but you have given %zu values (possibly in multiple calls "
+              "to '--ccols'). Recall that '--ccol' is the coordinate "
+              "column (usually RA and Dec). You can either put them in "
+              "one call (for example '--ccol=ra,dec') or in two (for "
+              "example '--ccol=ra --ccol=dec')",
+              gal_list_str_number(p->ccol));
+      p->ra_name=p->ccol->v;
+      p->dec_name=p->ccol->next->v;
+    }
+
+  /* If '--noblank' or '--sort' are given (possibly multiple times, each
+     with multiple column names) break it up into individual names. */
+  if(p->sort)    gal_options_merge_list_of_csv(&p->sort);
+  if(p->noblank) gal_options_merge_list_of_csv(&p->noblank);
+
+  /* Make sure that '--query' and '--center' are not called together. */
+  if(p->query && (p->center || p->overlapwith) )
+    error(EXIT_FAILURE, 0, "the '--query' option cannot be called together "
+          "together with '--center' or '--overlapwith'");
+
+  /* Overlapwith cannot be called with the manual query. */
+  if( p->overlapwith && (p->center || p->width || p->radius) )
+    error(EXIT_FAILURE, 0, "the '--overlapwith' option cannot be called "
+          "with the manual region specifiers ('--center', '--width' or "
+          "'--radius')");
+
+  /* The radius and width cannot be called together. */
+  if(p->radius && p->width)
+    error(EXIT_FAILURE, 0, "the '--radius' and '--width' options cannot be "
+          "called together");
+
+  /* If radius is given, it should be one value and positive. */
+  if(p->radius)
+    {
+      if(p->radius->size>1)
+        error(EXIT_FAILURE, 0, "only one value can be given to '--radius' "
+              "('-r') option");
+
+      if( ((double *)(p->radius->array))[0]<0 )
+        error(EXIT_FAILURE, 0, "the '--radius' option value cannot be 
negative");
+    }
+
+  /* Make sure the range values are reasonable. */
+  i=0;
+  if(p->range)
+    for(tmp=p->range; tmp!=NULL; tmp=tmp->next)
+      {
+        /* Basic preparations. */
+        ++i;
+        darray=tmp->array;
+
+        /* Make sure only two values are given. */
+        if(tmp->size!=2)
+          error(EXIT_FAILURE, 0, "two values (separated by ',' or ':') "
+                "should be given to '--range'. But %zu values were given "
+                "to the %zu%s call of this option (recall that the first "
+                "value should be the column name in the given dataset)",
+                tmp->size, i,
+                i==1 ? "st" : i==2 ? "nd" : i==3 ? "rd" : "th");
+
+        /* Make sure the first value is large than the second. */
+        if(darray[0]>darray[1])
+          error(EXIT_FAILURE, 0, "the first value of '--range' "
+                "should be smaller than, or equal to, the second, "
+                "but %g>%g", darray[0], darray[1]);
+
+        /* None of the values should be 'nan'. */
+        if( isnan(darray[0]) || isnan(darray[1]) )
+          error(EXIT_FAILURE, 0, "values to '--range' cannot be NaN");
+
+        /* ADQL doesn't recognize 'inf', so if the user gives '-inf' or
+           'inf', change it to the smallest/largest possible floating point
+           number. */
+        if( isinf(darray[0]) == -1 ) darray[0] = -FLT_MAX;
+        if( isinf(darray[1]) ==  1 ) darray[1] =  FLT_MAX;
+      }
+
+  /* Make sure the widths are reasonable. */
+  if(p->width && p->center)
+    {
+      /* Width should have the same number of elements as the center
+         coordinates */
+      if( p->width->size > 1 && p->width->size != p->center->size )
+        error(EXIT_FAILURE, 0, "'--width' should either have a single "
+              "value (used for all dimensions), or one value for each "
+              "dimension. However, you have provided %zu coordinate "
+              "values, and %zu width values", p->center->size,
+              p->width->size);
+
+      /* All values must be positive. */
+      for(i=0;i<p->width->size;++i)
+        if( ((double *)(p->width->array))[i]<0 )
+          error(EXIT_FAILURE, 0, "the '--width' option value(s) cannot "
+                "be negative");
+    }
+
+  /* Make sure that the output name is in a writable location and that it
+     doesn't exist. If it exists, and the user hasn't called
+     '--dontdelete', then delete the existing file. */
+  gal_checkset_writable_remove(p->cp.output, p->cp.keep,
+                               p->cp.dontdelete);
+
+  /* Set the name for the downloaded and final output name. These are due
+     to an internal low-level processing that will be done on the raw
+     downloaded file. */
+  if(p->cp.output==NULL)
+    {
+      basename=gal_checkset_malloc_cat(p->databasestr, ".fits");
+      p->cp.output=gal_checkset_make_unique_suffix(basename, ".fits");
+      free(basename);
+    }
+
+  /* Make sure the output name doesn't exist (and report an error if
+     '--dontdelete' is called. Just note that for the automatic output, we
+     are basing that on the output, not the input. So we are temporarily
+     activating 'keepinputdir'. */
+  keepinputdir=p->cp.keepinputdir;
+  p->cp.keepinputdir=1;
+  gal_checkset_writable_remove(p->cp.output, 0, p->cp.dontdelete);
+  p->downloadname=gal_checkset_automatic_output(&p->cp, p->cp.output,
+                                                "-raw-download.fits");
+  p->cp.keepinputdir=keepinputdir;
+}
+
+
+
+
+
+static void
+ui_check_options_and_arguments(struct queryparams *p)
+{
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/***************       Preparations         *******************/
+/**************************************************************/
+static void
+ui_preparations(struct queryparams *p)
+{
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/************         Set the parameters          *************/
+/**************************************************************/
+void
+ui_read_check_inputs_setup(int argc, char *argv[], struct queryparams *p)
+{
+  struct gal_options_common_params *cp=&p->cp;
+
+
+  /* Just to avoid warning on no minmapsize. It is irrelevant here. */
+  p->cp.minmapsize=-1;
+
+
+  /* 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);
+
+
+  /* Prepare all the options as FITS keywords to write in output later. */
+  gal_options_as_fits_keywords(&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 queryparams *p, struct timeval *t1)
+{
+  /* Free the allocated arrays: */
+  free(p->cp.hdu);
+  free(p->cp.output);
+  free(p->downloadname);
+}
diff --git a/bin/table/ui.h b/bin/query/ui.h
similarity index 59%
copy from bin/table/ui.h
copy to bin/query/ui.h
index 7af1d1c..f72a6a4 100644
--- a/bin/table/ui.h
+++ b/bin/query/ui.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-Table - View and manipulate a FITS table structures.
-Table is part of GNU Astronomy Utilities (Gnuastro) package.
+Query - Retreive data from a remote data server.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
-     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2020-2021, 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
@@ -33,48 +33,52 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Option groups particular to this program. */
 enum program_args_groups
 {
-  UI_GROUP_OUTROWS = GAL_OPTIONS_GROUP_AFTER_COMMON,
+  UI_GROUP_GENQUERY = GAL_OPTIONS_GROUP_AFTER_COMMON,
 };
 
 
 
 
+
 /* Available letters for short options:
 
-   a b d f g j k l m p t u v x y z
-   A B C E G H J L O Q R X Y
+   a e f j m n p t u x y z
+   A B E G J R W X Y
 */
 enum option_keys_enum
 {
   /* With short-option version. */
-  UI_KEY_WCSFILE         = 'w',
-  UI_KEY_WCSHDU          = 'W',
-  UI_KEY_COLUMN          = 'c',
+  UI_KEY_KEEPRAWDOWNLOAD = 'k',
   UI_KEY_INFORMATION     = 'i',
-  UI_KEY_COLINFOINSTDOUT = 'O',
-  UI_KEY_RANGE           = 'r',
-  UI_KEY_EQUAL           = 'e',
-  UI_KEY_NOTEQUAL        = 'n',
-  UI_KEY_SORT            = 's',
-  UI_KEY_DESCENDING      = 'd',
+  UI_KEY_LIMITINFO       = 'L',
+  UI_KEY_DATABASE        = 'd',
+  UI_KEY_QUERY           = 'Q',
+  UI_KEY_DATASET         = 's',
+  UI_KEY_CENTER          = 'C',
+  UI_KEY_OVERLAPWITH     = 'v',
+  UI_KEY_RADIUS          = 'r',
+  UI_KEY_RANGE           = 'g',
+  UI_KEY_NOBLANK         = 'b',
+  UI_KEY_COLUMN          = 'c',
+  UI_KEY_WIDTH           = 'w',
   UI_KEY_HEAD            = 'H',
-  UI_KEY_TAIL            = 't',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
+  UI_KEY_CCOL            = 1000,
+  UI_KEY_SORT,
+  UI_KEY_DRYRUN,
 };
 
 
 
-
-
-void
-ui_read_check_inputs_setup(int argc, char *argv[], struct tableparams *p);
+char *
+ui_strlist_to_str(gal_list_str_t *input);
 
 void
-ui_list_select_free(struct list_select *list, int freevalue);
+ui_read_check_inputs_setup(int argc, char *argv[], struct queryparams *p);
 
 void
-ui_free_report(struct tableparams *p);
+ui_free_report(struct queryparams *p, struct timeval *t1);
 
 #endif
diff --git a/bin/query/vizier.c b/bin/query/vizier.c
new file mode 100644
index 0000000..d7513a0
--- /dev/null
+++ b/bin/query/vizier.c
@@ -0,0 +1,179 @@
+/*********************************************************************
+Settings for VizieR.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2021, 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 <string.h>
+
+#include <gnuastro-internal/checkset.h>
+
+#include "main.h"
+
+#include "ui.h"
+#include "tap.h"
+
+
+static void
+vizier_sanity_checks(struct queryparams *p)
+{
+  /* VizieR specific: if the user has asked for '--information', but
+     without '--limitinfo', print a notice to introduce 'limitinfo'. */
+  if(p->datasetstr==NULL && p->information && p->limitinfo==NULL)
+    {
+      fprintf(stderr, "\n--------------------\n");
+      error(EXIT_SUCCESS, 0, "WARNING: The full VizieR metadata "
+            "(information) is more than 20Mb, and contains tens of "
+            "thousands entries. You can use '--limitinfo=XXXX' to "
+            "constrain the downloaded and displayed metadata to "
+            "those that have 'XXXX' in the description (for example "
+            "a certain author, or a certain project name). This will "
+            "greatly improve the speed of your search");
+      fprintf(stderr, "--------------------\n");
+    }
+
+  /* Set the summarized names. */
+  if(p->datasetstr)
+    {
+      if( !strcmp(p->datasetstr, "2mass") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("II/246/out", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "akarifis") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("II/298/fis", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "allwise") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("II/328/allwise", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "apass9") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("II/336/apass9", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "catwise") )
+        {
+          free(p->datasetstr);
+          if(p->ra_name==NULL) p->ra_name="RA_ICRS";
+          if(p->dec_name==NULL) p->dec_name="DE_ICRS";
+          gal_checkset_allocate_copy("II/365/catwise", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "des1") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("II/357/des_dr1", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "gaiadr2") )
+        {
+          free(p->datasetstr);
+          if(p->ra_name==NULL) p->ra_name="ra_epoch2000";
+          if(p->dec_name==NULL) p->dec_name="dec_epoch2000";
+          gal_checkset_allocate_copy("I/345/gaia2", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "gaiaedr3") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("I/350/gaiaedr3", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "galex5") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("II/312/ais", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "nomad") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("I/297/out", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "panstarrs1") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("II/349/ps1", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "pmx1") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("I/317/sample", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "sdss12") )
+        {
+          free(p->datasetstr);
+          if(p->ra_name==NULL) p->ra_name="RA_ICRS";
+          if(p->dec_name==NULL) p->dec_name="DE_ICRS";
+          gal_checkset_allocate_copy("V/147/sdss12", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "usnob1") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("I/284/out", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "ucac5") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("I/340/ucac5", &p->datasetstr);
+        }
+      else if( !strcmp(p->datasetstr, "unwise") )
+        {
+          free(p->datasetstr);
+          gal_checkset_allocate_copy("II/363/unwise", &p->datasetstr);
+        }
+    }
+}
+
+
+
+
+
+void
+vizier_prepare(struct queryparams *p)
+{
+  /* VizieR-specific. */
+  vizier_sanity_checks(p);
+
+  /* Set the URLs, note that this is a simply-linked list, so we need to
+     reverse it in the end (with 'gal_list_str_reverse') to have the same
+     order here.
+
+     Other possible VizieR TAP servers that don't seem to be working now
+     (extracted from the 'visquery' script):
+        http://vizier.cfa.harvard.edu/TAPVizieR/tap/sync
+        http://vizier.nao.ac.jp/TAPVizieR/tap/sync
+        http://data.bao.ac.cn/TAPVizieR/tap/sync
+        http://vizier.ast.cam.ac.uk/TAPVizieR/tap/sync
+        http://www.ukirt.jach.hawaii.edu/TAPVizieR/tap/sync
+        http://vizier.inasan.ru/TAPVizieR/tap/sync */
+  gal_list_str_add(&p->urls,
+                   "http://tapvizier.u-strasbg.fr/TAPVizieR/tap/sync";, 0);
+
+  /* Name of default RA Dec columns. */
+  if(p->ra_name==NULL)  p->ra_name="RAJ2000";
+  if(p->dec_name==NULL) p->dec_name="DEJ2000";
+
+  /* Basic sanity checks. */
+  tap_sanity_checks(p);
+}
diff --git a/bin/TEMPLATE/TEMPLATE.h b/bin/query/vizier.h
similarity index 76%
copy from bin/TEMPLATE/TEMPLATE.h
copy to bin/query/vizier.h
index e75f578..c4e0432 100644
--- a/bin/TEMPLATE/TEMPLATE.h
+++ b/bin/query/vizier.h
@@ -1,11 +1,11 @@
 /*********************************************************************
-TEMPLATE - A minimal set of files and functions to define a program.
-TEMPLATE is part of GNU Astronomy Utilities (Gnuastro) package.
+Settings for VizieR.
+Query is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
      Mohammad akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2021, 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,10 +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 TEMPLATE_H
-#define TEMPLATE_H
+#ifndef VIZIER_H
+#define VIZIER_H
+
+#include "main.h"
 
 void
-TEMPLATE(struct TEMPLATEparams *p);
+vizier_prepare(struct queryparams *p);
 
 #endif
diff --git a/bin/script/Makefile.am b/bin/script/Makefile.am
index 485c7fa..3bbedcf 100644
--- a/bin/script/Makefile.am
+++ b/bin/script/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2019, Free Software Foundation, Inc.
+## Copyright (C) 2019-2021, 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
@@ -23,12 +23,13 @@
 
 
 ## List of programs (scripts in this directory) to install under the
-## `prefix/bin' directory (`bin_SCRIPTS'), files necessary to distribute
-## with the tarball (`EXTRA_DIST') and output files (to be cleaned with
-## `make clean).
-bin_SCRIPTS = astscript-sort-by-night
+## 'prefix/bin' directory ('bin_SCRIPTS'), files necessary to distribute
+## with the tarball ('EXTRA_DIST') and output files (to be cleaned with
+## 'make clean').
+bin_SCRIPTS = astscript-make-ds9-reg \
+              astscript-sort-by-night
 
-EXTRA_DIST = sort-by-night.in
+EXTRA_DIST = make-ds9-reg.in sort-by-night.in
 
 CLEANFILES = $(bin_SCRIPTS)
 
@@ -36,7 +37,7 @@ CLEANFILES = $(bin_SCRIPTS)
 
 
 
-## Command to do basic substitutions (anything surrounded by an `@').
+## Command to do basic substitutions (anything surrounded by an '@').
 do_subst = sed -e 's,[@]VERSION[@],$(VERSION),g' \
                -e 's,[@]SCRIPT_NAME[@],$@,g'
 
@@ -48,3 +49,7 @@ do_subst = sed -e 's,[@]VERSION[@],$(VERSION),g' \
 astscript-sort-by-night: sort-by-night.in Makefile
        $(do_subst) < $(srcdir)/sort-by-night.in > $@
        chmod +x $@
+
+astscript-make-ds9-reg: make-ds9-reg.in Makefile
+       $(do_subst) < $(srcdir)/make-ds9-reg.in > $@
+       chmod +x $@
diff --git a/bin/script/make-ds9-reg.in b/bin/script/make-ds9-reg.in
new file mode 100755
index 0000000..15d115e
--- /dev/null
+++ b/bin/script/make-ds9-reg.in
@@ -0,0 +1,326 @@
+#!/bin/sh
+
+# Convert the given two columns into a ds9 region file.
+#
+# Original author:
+#     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+# Contributing author(s):
+# Copyright (C) 2021 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/>.
+
+
+# Exit the script in the case of failure
+set -e
+
+
+
+
+
+# Default option values (can be changed with options on the
+# command-line).
+hdu=1
+col=""
+name=""
+width=1
+mode=wcs
+radius=""
+command=""
+out=ds9.reg
+color=green
+dontdelete=0
+version=@VERSION@
+scriptname=@SCRIPT_NAME@
+
+
+
+
+
+# Output of '--usage' and '--help':
+print_usage() {
+    cat <<EOF
+$scriptname: run with '--help' for list of options
+EOF
+}
+
+print_help() {
+    cat <<EOF
+Usage: $scriptname [OPTION] FITS-files
+
+This script is part of GNU Astronomy Utilities $version.
+
+This script will take two column names (or numbers) and return a "region
+file" (with one region over each entry) for easy inspection of catalog
+entries in this SAO DS9.
+
+For more information, please run any of the following commands. In
+particular the first contains a very comprehensive explanation of this
+script's invocation: expected input(s), output(s), and a full description
+of all the options.
+
+     Inputs/Outputs and options:           $ info $scriptname
+     Full Gnuastro manual/book:            $ info gnuastro
+
+If you couldn't find your answer in the manual, you can get direct help from
+experienced Gnuastro users and developers. For more information, please run:
+
+     $ info help-gnuastro
+
+$scriptname options:
+ Input:
+  -h, --hdu=STR           HDU/extension of all input FITS files.
+  -c, --column=STR,STR    Columns to use as coordinates (name or number).
+  -m, --mode=wcs|img      Coordinates in WCS or image (default: $mode)
+
+ Output:
+  -C, --color             Color for the regions (read by DS9).
+  -w, --width             Line thickness of the regions (in DS9).
+  -r, --radius            Radius of each region (arcseconds if in WCS mode).
+  -D, --dontdelete        Don't delete output if it exists.
+  -o, --output=STR        Name of output file.
+      --command="STR"     DS9 command to run after making region.
+
+ Operating mode:
+  -h, --help              Print this help list.
+      --cite              BibTeX citation for this program.
+  -V, --version           Print program version.
+
+Mandatory or optional arguments to long options are also mandatory or optional
+for any corresponding short options.
+
+GNU Astronomy Utilities home page: http://www.gnu.org/software/gnuastro/
+
+Report bugs to bug-gnuastro@gnu.org.
+EOF
+}
+
+
+
+
+
+# Output of '--version':
+print_version() {
+    cat <<EOF
+$scriptname (GNU Astronomy Utilities) $version
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
+License GPLv3+: GNU General public license version 3 or later.
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+Written/developed by Mohammad Akhlaghi
+EOF
+}
+
+
+
+
+
+# Functions to check option values and complain if necessary.
+on_off_option_error() {
+    if [ "x$2" = x ]; then
+        echo "$scriptname: '$1' doesn't take any values."
+    else
+        echo "$scriptname: '$1' (or '$2') doesn't take any values."
+    fi
+    exit 1
+}
+
+check_v() {
+    if [ x"$2" = x ]; then
+        echo "$scriptname: option '$1' requires an argument."
+        echo "Try '$scriptname --help' for more information."
+        exit 1;
+    fi
+}
+
+
+
+
+
+# Separate command-line arguments from options. Then put the option
+# value into the respective variable.
+#
+# OPTIONS WITH A VALUE:
+#
+#   Each option has three lines because we want to all common formats: for
+#   long option names: '--longname value' and '--longname=value'. For short
+#   option names we want '-l value', '-l=value' and '-lvalue' (where '-l'
+#   is the short version of the hypothetical '--longname' option).
+#
+#   The first case (with a space between the name and value) is two
+#   command-line arguments. So, we'll need to shift it two times. The
+#   latter two cases are a single command-line argument, so we just need to
+#   "shift" the counter by one. IMPORTANT NOTE: the ORDER OF THE LATTER TWO
+#   cases matters: '-h*' should be checked only when we are sure that its
+#   not '-h=*').
+#
+# OPTIONS WITH NO VALUE (ON-OFF OPTIONS)
+#
+#   For these, we just want the two forms of '--longname' or '-l'. Nothing
+#   else. So if an equal sign is given we should definitely crash and also,
+#   if a value is appended to the short format it should crash. So in the
+#   second test for these ('-l*') will account for both the case where we
+#   have an equal sign and where we don't.
+while [ $# -gt 0 ]
+do
+    # Initialize 'tcol':
+    tcol=""
+
+    # Put the values in the proper variable.
+    case "$1" in
+        # Input parameters.
+        -h|--hdu)           hdu="$2";                            check_v "$1" 
"$hdu";   shift;shift;;
+        -h=*|--hdu=*)       hdu="${1#*=}";                       check_v "$1" 
"$hdu";   shift;;
+        -h*)                hdu=$(echo "$1"  | sed -e's/-h//');  check_v "$1" 
"$hdu";   shift;;
+        -c|--column)        tcol="$2";                           check_v "$1" 
"$tcol";  shift;shift;;
+        -c=*|--column=*)    tcol="${1#*=}";                      check_v "$1" 
"$tcol";  shift;;
+        -c*)                tcol=$(echo "$1"  | sed -e's/-c//'); check_v "$1" 
"$tcol";  shift;;
+        -m|--mode)          mode="$2";                           check_v "$1" 
"$mode";  shift;shift;;
+        -m=*|--mode=*)      mode="${1#*=}";                      check_v "$1" 
"$mode";  shift;;
+        -m*)                mode=$(echo "$1"  | sed -e's/-m//'); check_v "$1" 
"$mode";  shift;;
+
+        # Output parameters
+        -C|--color)         color="$2";                            check_v 
"$1" "$color";   shift;shift;;
+        -C=*|--color=*)     color="${1#*=}";                       check_v 
"$1" "$color";   shift;;
+        -C*)                color=$(echo "$1"  | sed -e's/-C//');  check_v 
"$1" "$color";   shift;;
+        -w|--width)         width="$2";                            check_v 
"$1" "$width";   shift;shift;;
+        -w=*|--width=*)     width="${1#*=}";                       check_v 
"$1" "$width";   shift;;
+        -w*)                width=$(echo "$1"  | sed -e's/-w//');  check_v 
"$1" "$width";   shift;;
+        -r|--radius)        radius="$2";                           check_v 
"$1" "$radius";  shift;shift;;
+        -r=*|--radius=*)    radius="${1#*=}";                      check_v 
"$1" "$radius";  shift;;
+        -r*)                radius=$(echo "$1"  | sed -e's/-r//'); check_v 
"$1" "$radius";  shift;;
+        -D|--dontdelete)    dontdelete=1; shift;;
+        -D*|--dontdelete=*) on_off_option_error --dontdelete -D;;
+        -o|--output)        out="$2";                              check_v 
"$1" "$out";     shift;shift;;
+        -o=*|--output=*)    out="${1#*=}";                         check_v 
"$1" "$out";     shift;;
+        -o*)                out=$(echo "$1"  | sed -e's/-o//');    check_v 
"$1" "$out";     shift;;
+        --command)          command="$2";                          check_v 
"$1" "$command"; shift;shift;;
+        --command=*)        command="${1#*=}";                     check_v 
"$1" "$command"; shift;;
+
+        # Non-operating options.
+        -?|--help)        print_help; exit 0;;
+        -'?'*|--help=*)   on_off_option_error --help -?;;
+        -V|--version)     print_version; exit 0;;
+        -V*|--version=*)  on_off_option_error --version -V;;
+        --cite)           astfits --cite; exit 0;;
+        --cite=*)         on_off_option_error --cite;;
+
+        # Unrecognized option:
+        -*) echo "$scriptname: unknown option '$1'"; exit 1;;
+
+        # Not an option (not starting with a '-'): assumed to be input FITS
+        # file name.
+        *) input="$1 $input"; shift;;
+    esac
+
+    # If a column was given, add it to possibly existing previous columns
+    # into a comma-separate list.
+    if [ x"$tcol" != x ]; then
+        if [ x"$col" != x ]; then col="$col,$tcol"; else col=$tcol; fi
+    fi
+done
+
+
+
+
+# Basic sanity checks
+# ===================
+
+# Make sure only two columns are given, then set the columns.
+if [ x$col = x ]; then
+    echo "$scriptname: no columns specified, you can use '--column' (or '-c')"
+    exit 1
+else
+    ncols=$(echo $col | awk 'BEGIN{FS=","}END{print NF}')
+    if [ x$ncols != x2 ]; then
+        echo "$scriptname: only two columns should be given, but $ncols were 
given"
+        exit 1
+    fi
+fi
+
+# Make sure the value to '--mode' is either 'wcs' or 'img'.
+if [ $mode = "wcs" ] || [ $mode = "img" ]; then
+    junk=1
+else
+    echo "$scriptname: value to '--mode' ('-m') should be 'wcs' or 'img'"
+    exit 1
+fi
+
+# If the output already exists and the user doesn't want to overwrite, then
+# abort with an error.
+if [ -f $out ]; then
+    if [ $dontdelete = 1 ]; then
+        echo "$scriptname: '$out' already exists! Aborting due to 
'--dontdelete'"
+        exit 1
+    fi
+fi
+
+
+
+
+
+# Initalize the radius value if not given. If we are in WCS mode, select a
+# 3 arcsecond radius and if we are in image mode, select a 5 pixel radius.
+if [ x"$radius" = x ]; then
+    if [ $mode = "wcs" ]; then radius=1
+    else                       radius=3
+    fi
+fi
+
+# Set the units of the radius.
+if [ x$mode = x"wcs" ]; then unit="\""; else unit=""; fi
+
+
+
+
+
+# Write the metadata in the output.
+printf "# Region file format: DS9 version 4.1\n" > $out
+printf "# Created by $scriptname (GNU Astronomy Utilities) $version\n" >> $out
+printf "# Input: $input (hdu $hdu)\n" >> $out
+printf "# Columns: $col\n" >> $out
+printf "global color=%s width=%d\n" $color $width >> $out
+if [ $mode = "wcs" ]; then  printf "fk5\n" >> $out
+else                        printf "image\n" >> $out;   fi
+
+
+
+
+
+# Write each region's results (when no input file is given, read from the
+# standard input).
+if [ x"$input" = x ]; then
+    cat /dev/stdin \
+        | asttable $input --column=$col \
+        | while read a b; do \
+            printf "circle(%g,%g,%g%s)\n" $a $b $radius $unit >> $out; \
+          done
+else
+    asttable $input --column=$col \
+        | while read a b; do \
+            printf "circle(%g,%g,%g%s)\n" $a $b $radius $unit >> $out; \
+    done
+fi
+
+
+
+
+
+# Run ds9 with the desired region over-plotted.
+if [ x"$command" = x ]; then
+    junk=1
+else
+    $command -regions $out
+    if [ $dontdelete = 0 ]; then rm $out; fi
+fi
diff --git a/bin/script/sort-by-night.in b/bin/script/sort-by-night.in
index 5a882e4..5e8cade 100644
--- a/bin/script/sort-by-night.in
+++ b/bin/script/sort-by-night.in
@@ -1,12 +1,12 @@
 #!/bin/sh
 
-# Separate input datasets into multiple nights, run with `--help', or see
-# description under `print_help' (below) for more.
+# Separate input datasets into multiple nights, run with '--help', or see
+# description under 'print_help' (below) for more.
 #
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2019, Free Software Foundation, Inc.
+# Copyright (C) 2019-2021, 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
@@ -32,9 +32,9 @@ set -e
 # Default option values (can be changed with options on the
 # command-line).
 hdu=1
-hour=9
 copy=0
 link=0
+hour=11
 quiet=0
 key=DATE
 prefix=./
@@ -45,7 +45,7 @@ scriptname=@SCRIPT_NAME@
 
 
 
-# Output of `--usage' and `--help':
+# Output of '--usage' and '--help':
 print_usage() {
     cat <<EOF
 $scriptname: run with '--help' for list of options
@@ -112,11 +112,11 @@ EOF
 
 
 
-# Output of `--version':
+# Output of '--version':
 print_version() {
     cat <<EOF
 $scriptname (GNU Astronomy Utilities) $version
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 License GPLv3+: GNU General public license version 3 or later.
 This is free software: you are free to change and redistribute it.
 There is NO WARRANTY, to the extent permitted by law.
@@ -157,23 +157,23 @@ check_v() {
 # OPTIONS WITH A VALUE:
 #
 #   Each option has three lines because we want to all common formats: for
-#   long option names: `--longname value' and `--longname=value'. For short
-#   option names we want `-l value', `-l=value' and `-lvalue' (where `-l'
-#   is the short version of the hypothetical `--longname' option).
+#   long option names: '--longname value' and '--longname=value'. For short
+#   option names we want '-l value', '-l=value' and '-lvalue' (where '-l'
+#   is the short version of the hypothetical '--longname' option).
 #
 #   The first case (with a space between the name and value) is two
 #   command-line arguments. So, we'll need to shift it two times. The
 #   latter two cases are a single command-line argument, so we just need to
 #   "shift" the counter by one. IMPORTANT NOTE: the ORDER OF THE LATTER TWO
-#   cases matters: `-h*' should be checked only when we are sure that its
-#   not `-h=*').
+#   cases matters: '-h*' should be checked only when we are sure that its
+#   not '-h=*').
 #
 # OPTIONS WITH NO VALUE (ON-OFF OPTIONS)
 #
-#   For these, we just want the two forms of `--longname' or `-l'. Nothing
+#   For these, we just want the two forms of '--longname' or '-l'. Nothing
 #   else. So if an equal sign is given we should definitely crash and also,
 #   if a value is appended to the short format it should crash. So in the
-#   second test for these (`-l*') will account for both the case where we
+#   second test for these ('-l*') will account for both the case where we
 #   have an equal sign and where we don't.
 while [ $# -gt 0 ]
 do
@@ -211,7 +211,7 @@ do
         # Unrecognized option:
         -*) echo "$scriptname: unknown option '$1'"; exit 1;;
 
-        # Not an option (not starting with a `-'): assumed to be input FITS
+        # Not an option (not starting with a '-'): assumed to be input FITS
         # file name.
         *) inputs="$1 $inputs"; shift;;
     esac
@@ -242,12 +242,30 @@ fi
 #
 # To do this, we'll convert the date into Unix epoch time (seconds
 # since 1970-01-01,00:00:00) and keep that with the filename.
-list=$(for f in $inputs; do
-           astfits $f --datetosec=$key --hdu=$hdu -q \
-               | awk '{h='$hour'; d=int($1/86400); \
-                       if(int($1)%86400<(h*3600)) n=d-1; else n=d; \
-                       print "'$f'", $1, n }'
-       done)
+#
+# A simple AWK expression for what we are doing here inside of Table
+# (where the first input column is the filename and the second is the
+# date in seconds):
+#
+# awk '{h='$hour'; d=int($2/86400); \
+#       if(int($2)%86400<(h*3600)) n=d-1; else n=d; \
+#       print $1, $2, n }'
+#
+# Finally, we are sorting all the images with the unix-second column
+# to make sure that they are ordered in observation order later.
+list=$(astfits --keyvalue=$key --hdu=$hdu $inputs --colinfoinstdout \
+               | asttable -cFILENAME \
+                          -c'arith '$key' date-to-sec' \
+                          -c'arith '$key' date-to-sec set-sec \
+                                   sec 86400 / int32 set-day \
+                                   day \
+                                     sec int32 86400 % '$hour' 3600  x lt \
+                                     day 1 - \
+                                   where' \
+                          --colmetadata=ARITH_8,NIGHT,counter,"Observing 
night." \
+                          --colinfoinstdout \
+               | asttable --sort=UNIXSEC --colinfoinstdout)
+
 
 
 
@@ -263,44 +281,42 @@ list=$(for f in $inputs; do
 
 
 
-# Get the uniqe nights from the previous step.
-unique=$(echo "$list" | awk '{print $3}' | sort | uniq | cat -n)
-
+# Get the uniqe nights from the previous step and give each night a
+# counter starting from 1. The output of this step will be two
+# columns: a counter, and the night number.
+#
+# We are using 'asttable' here to avoid issues with spaces in
+# directory names in the first line.
+unique=$(echo "$list" | asttable -cNIGHT | sort | uniq | cat -n)
+#echo "check: $unique"; exit 1
 
 
 
 
 # Find the FITS files of every unique day and sort them by observing
 # time within that day. We'll also initialize the night-counter to 1.
-counter=1
 echo "$unique" | while read l; do
 
     # Find all input files (and their Unix epoch time).
-    daynum_to=$(echo $l | awk '{print $1}')
-    daynum_from=$(echo $l | awk '{print $2}')
-    in_this_day=$(echo "$list" \
-                       | awk '$3=='$daynum_from' {print $1, $2}' \
-                       | sort -nk2 \
-                       | cat -n \
-                       | awk '{print $2,'$counter',$1}')
-
-    # Now that we know this night's files, we can take the proper action.
-    echo "$in_this_day" | while read L; do
-
-        # Set the necessary numbers.
-        infile=$(echo $L | awk '{print $1}')
-        night_num=$(echo $L | awk '{print $2}')
-        exposure_num=$(echo $L | awk '{print $3}')
+    night_to=$(echo $l | awk '{print $1}')
+    night_from=$(echo $l | awk '{print $2}')
+    in_this_night=$(echo "$list" \
+                         | asttable -cFILENAME --equal=NIGHT,$night_from)
+
+    # Now that we know this night's files, sorted by time, we can take
+    # the proper action (simply list, or copy or make links).
+    exposure_num=1
+    echo "$in_this_night" | while read infile; do
 
         # Make the outputs
-        outfile=$prefix"n"$night_num-$exposure_num.fits
-        if   [ $copy = 1 ]; then   cp $infile $outfile
+        outfile=$prefix"n"$night_to-$exposure_num.fits
+        if   [ $copy = 1 ]; then   cp     $infile $outfile
         elif [ $link = 1 ]; then   ln -fs $infile $outfile
-        else                       echo "$infile $night_num $exposure_num"
+        else                       echo "$infile $night_to $exposure_num"
         fi
 
-    done
+        # Increment the exposure number
+        exposure_num=$((exposure_num+1))
 
-    # Increment the night-counter.
-    counter=$((counter+1))
+    done
 done
diff --git a/bin/segment/Makefile.am b/bin/segment/Makefile.am
index 56bb770..aa81bcd 100644
--- a/bin/segment/Makefile.am
+++ b/bin/segment/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2018-2019, Free Software Foundation, Inc.
+## Copyright (C) 2018-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astsegment
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astsegment_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                   $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astsegment_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                   -lgnuastro $(CONFIG_LDADD)
 
 astsegment_SOURCES = main.c ui.c segment.c clumps.c
 
diff --git a/bin/segment/args.h b/bin/segment/args.h
index c3400b1..0fedc8d 100644
--- a/bin/segment/args.h
+++ b/bin/segment/args.h
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -100,7 +100,7 @@ struct argp_option program_options[] =
     {
       "detection",
       UI_KEY_DETECTION,
-      "STR",
+      "FITS",
       0,
       "Filename of detection image (to segment).",
       GAL_OPTIONS_GROUP_INPUT,
@@ -126,7 +126,7 @@ struct argp_option program_options[] =
     {
       "kernel",
       UI_KEY_KERNEL,
-      "STR",
+      "FITS",
       0,
       "Filename of kernel to convolve with input.",
       GAL_OPTIONS_GROUP_INPUT,
@@ -152,7 +152,7 @@ struct argp_option program_options[] =
     {
       "convolved",
       UI_KEY_CONVOLVED,
-      "STR",
+      "FITS",
       0,
       "Convolved image file to avoid convolution.",
       GAL_OPTIONS_GROUP_INPUT,
diff --git a/bin/segment/astsegment.conf b/bin/segment/astsegment.conf
index caf71bb..cba31ba 100644
--- a/bin/segment/astsegment.conf
+++ b/bin/segment/astsegment.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astsegment                     # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/segment/authors-cite.h b/bin/segment/authors-cite.h
index 7e54e8b..3649b09 100644
--- a/bin/segment/authors-cite.h
+++ b/bin/segment/authors-cite.h
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -26,12 +26,31 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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 ""
+   put a row of '-' with the same length and then put the BibTeX.
+
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
+
+#define PROGRAM_BIBTEX ""                                               \
+  "Paper on why Segment was spinned-off from NoiseChisel\n"             \
+  "-----------------------------------------------------\n"             \
+  "@ARTICLE{noisechisel_segment_2019,\n"                                \
+  "        author = {{Akhlaghi}, Mohammad},\n"                          \
+  "         title = \"{Carving out the low surface brightness universe with 
NoiseChisel}\",\n" \
+  "       journal = {arXiv e-prints},\n"                                \
+  "      keywords = {Astrophysics - Instrumentation and Methods for 
Astrophysics,\n" \
+  "                  Astrophysics - Astrophysics of Galaxies,\n"        \
+  "                  Computer Science - Computer Vision and Pattern 
Recognition},\n" \
+  "          year = \"2019\",\n"                                        \
+  "         month = \"Sep\",\n"                                         \
+  "           eid = {arXiv:1909.11230},\n"                              \
+  "         pages = {arXiv:1909.11230},\n"                              \
+  " archivePrefix = {arXiv},\n"                                         \
+  "        eprint = {1909.11230},\n"                                    \
+  "  primaryClass = {astro-ph.IM},\n"                                   \
+  "        adsurl = 
{https://ui.adsabs.harvard.edu/abs/2019arXiv190911230A},\n"; \
+  "       adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n" \
+  "}\n"
 
 #define PROGRAM_AUTHORS "Mohammad Akhlaghi"
 
diff --git a/bin/segment/clumps.c b/bin/segment/clumps.c
index 116c5bb..02ffab7 100644
--- a/bin/segment/clumps.c
+++ b/bin/segment/clumps.c
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -57,7 +57,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /**********************************************************************/
 /* Make the preparations for the intiial growing the clumps to identify
    objects: a single standard deviation for the whole object and preparing
-   the labels (because the growth is going to happen on the `olabel'
+   the labels (because the growth is going to happen on the 'olabel'
    image. */
 void
 clumps_grow_prepare_initial(struct clumps_thread_params *cltprm)
@@ -135,11 +135,11 @@ clumps_grow_prepare_initial(struct clumps_thread_params 
*cltprm)
      object at this stage: we are also going to re-label the pixels to
      grow. For most astronomical objects, the major part of the detection
      area is going to be diffuse flux, so we will just allocate the same
-     size as `indexs' array (the `dsize' will be corrected after getting
+     size as 'indexs' array (the 'dsize' will be corrected after getting
      the exact number.
 
-     Also note that since `indexs' is already sorted, therefore
-     `diffuseindexs' will also be already sorted. */
+     Also note that since 'indexs' is already sorted, therefore
+     'diffuseindexs' will also be already sorted. */
   cltprm->diffuseindexs=gal_data_alloc(NULL, GAL_TYPE_SIZE_T, 1,
                                        cltprm->indexs->dsize, NULL, 0,
                                        p->cp.minmapsize, p->cp.quietmmap,
@@ -155,7 +155,7 @@ clumps_grow_prepare_initial(struct clumps_thread_params 
*cltprm)
   while(++s<sf);
 
 
-  /* Correct the sizes of the `diffuseindexs' data structure. */
+  /* Correct the sizes of the 'diffuseindexs' data structure. */
   cltprm->diffuseindexs->size = cltprm->diffuseindexs->dsize[0] = ndiffuse;
 }
 
@@ -165,12 +165,12 @@ clumps_grow_prepare_initial(struct clumps_thread_params 
*cltprm)
 
 /* Add all the remaining pixels in the detection (below the growth
    threshold, or those that were not touching). Note that initially
-   `diffuseindexs' was filled with the pixels that are above the growth
+   'diffuseindexs' was filled with the pixels that are above the growth
    threshold. That was necessary for identifying the objects. Now that we
    have identified the objects and labeled them, we want to add the
    remaining diffuse pixels to it too before doing the final growth.
 
-   Note that the most efficient way is just to re-fill the `diffuseindexs'
+   Note that the most efficient way is just to re-fill the 'diffuseindexs'
    array instead of adding the pixels below the threshold and sorting them
    afterwards.*/
 void
@@ -181,7 +181,7 @@ clumps_grow_prepare_final(struct clumps_thread_params 
*cltprm)
   int32_t *olabel=cltprm->clprm->p->olabel->array;
   size_t *s=cltprm->indexs->array, *sf=s+cltprm->indexs->size;
 
-  /* Recall that we initially allocated `diffuseindexs' to have the same
+  /* Recall that we initially allocated 'diffuseindexs' to have the same
      size as the indexs. So there is no problem if there are more pixels in
      this final round compared to the initial round. */
   do
@@ -189,7 +189,7 @@ clumps_grow_prepare_final(struct clumps_thread_params 
*cltprm)
       dindexs[ ndiffuse++ ] = *s;
   while(++s<sf);
 
-  /* Correct the sizes of the `diffuseindexs' data structure. */
+  /* Correct the sizes of the 'diffuseindexs' data structure. */
   cltprm->diffuseindexs->size = cltprm->diffuseindexs->dsize[0] = ndiffuse;
 }
 
@@ -230,15 +230,15 @@ clumps_correct_sky_labels_for_check(struct 
clumps_thread_params *cltprm,
   size_t len=cltprm->numinitclumps+1;
   struct segmentparams *p=cltprm->clprm->p;
 
-  /* If any of the clumps must be kept (`cltprm->snind->size!=0'), then
+  /* If any of the clumps must be kept ('cltprm->snind->size!=0'), then
      re-label them for the check image. Otherwise, remove all clumps. */
   if(cltprm->snind->size)
     {
       /* A small sanity check. */
       if(gal_tile_block(tile)!=p->clabel)
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
-              "address the problem. `tile->block' must point to the "
-              "`clabel' dataset", __func__, PACKAGE_BUGREPORT);
+              "address the problem. 'tile->block' must point to the "
+              "'clabel' dataset", __func__, PACKAGE_BUGREPORT);
 
 
       /* Allocate a dataset with the new indexs, note that it will need to
@@ -269,8 +269,8 @@ clumps_correct_sky_labels_for_check(struct 
clumps_thread_params *cltprm,
 
 
       /* The new indexs array has been initialized to zero. So we just need
-         to go over the labels in `cltprm->sninds' and give them a value of
-         `curlab++'. */
+         to go over the labels in 'cltprm->sninds' and give them a value of
+         'curlab++'. */
       ninds=newinds->array;
       lf = (l=cltprm->snind->array) + cltprm->snind->size;
       do { ninds[*l]=curlab++; *l=ninds[*l]; } while(++l<lf);
@@ -285,7 +285,7 @@ clumps_correct_sky_labels_for_check(struct 
clumps_thread_params *cltprm,
     }
   else
     /* There were no usable clumps in this tile, so just set all the pixels
-       larger than zero (a clump) to `GAL_LABEL_INIT'. */
+       larger than zero (a clump) to 'GAL_LABEL_INIT'. */
     GAL_TILE_PARSE_OPERATE( tile, NULL, 0, 1, {*i=*i>0?GAL_LABEL_INIT:*i;} );
 }
 
@@ -336,7 +336,7 @@ clumps_find_make_sn_table(void *in_prm)
 
 
       /* Get the number of usable elements in this tile (note that tiles
-         can have blank pixels), so we can't simply use `tile->size'. */
+         can have blank pixels), so we can't simply use 'tile->size'. */
       if(p->input->flag & GAL_DATA_FLAG_HASBLANK)
         {
           tmp=gal_statistics_number(tile);
@@ -349,9 +349,9 @@ clumps_find_make_sn_table(void *in_prm)
       /* Find the number of detected pixels over this tile. Since this is
          the binary image, this is just the sum of all the pixels.
 
-         Note that `numdet' can be `nan' when the whole tile is blank and
+         Note that 'numdet' can be 'nan' when the whole tile is blank and
          so there was no values to sum. Recall that in summing, when there
-         is not input, the output is `nan'. */
+         is not input, the output is 'nan'. */
       tmp=gal_statistics_sum(tile);
       numdet=*((double *)(tmp->array));
       gal_data_free(tmp);
@@ -389,10 +389,10 @@ clumps_find_make_sn_table(void *in_prm)
 
           /* Add the index of every sky element to the array of
              indexs. Note that since we know the array is always of type
-             `int32_t', we can call the `GAL_TILE_PO_OISET' macro to avoid
+             'int32_t', we can call the 'GAL_TILE_PO_OISET' macro to avoid
              having to deal with multiple possible types in
-             `GAL_TILE_PARSE_OPERATE'. Since the OUT macro-variable is
-             NULL, the `int' is just a place-holder, it will not be
+             'GAL_TILE_PARSE_OPERATE'. Since the OUT macro-variable is
+             NULL, the 'int' is just a place-holder, it will not be
              used. */
           c=0;
           indarr=cltprm.indexs->array;
@@ -417,7 +417,7 @@ clumps_find_make_sn_table(void *in_prm)
                 *(int32_t *)i=GAL_LABEL_RIVER;
 
               /* This pixel is not on the edge, check if it had a value of
-                 `0' in the binary image (is not detected) then add it to
+                 '0' in the binary image (is not detected) then add it to
                  the list of indexs (note that the binary image also
                  contains the blank pixels, so only sky regions have a
                  value of 0 in the binary image). */
@@ -481,7 +481,7 @@ clumps_find_make_sn_table(void *in_prm)
 
 
           /* If the user wanted to check the steps, remove the clumps that
-             weren't used from the `clabel' image (they have been already
+             weren't used from the 'clabel' image (they have been already
              excluded from the table). */
           if(cltprm.snind)
             clumps_correct_sky_labels_for_check(&cltprm, tile);
@@ -489,7 +489,7 @@ clumps_find_make_sn_table(void *in_prm)
 
           /* If there were no clumps, then just set the S/N table to
              NULL. This must be done after the check image creation (if
-             necessary), because we use `cltprm.snind' as a proxy for the
+             necessary), because we use 'cltprm.snind' as a proxy for the
              check image.*/
           if( cltprm.clprm->sn[ cltprm.id ].size==0 )
             cltprm.snind=cltprm.sn=NULL;
@@ -538,8 +538,8 @@ clumps_write_sn_table(struct segmentparams *p, gal_data_t 
*insn,
       /* A small sanity check. */
       if(ind->size==0 || sn->size==0)
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-              "the problem. For some reason, all the elements in `ind' or "
-              "`sn' are blank", __func__, PACKAGE_BUGREPORT);
+              "the problem. For some reason, all the elements in 'ind' or "
+              "'sn' are blank", __func__, PACKAGE_BUGREPORT);
     }
   else
     {
@@ -555,7 +555,7 @@ clumps_write_sn_table(struct segmentparams *p, gal_data_t 
*insn,
   gal_table_comments_add_intro(&comments, PROGRAM_STRING, &p->rawtime);
 
   /* write the table. */
-  gal_table_write(cols, comments, p->cp.tableformat, filename,
+  gal_table_write(cols, NULL, comments, p->cp.tableformat, filename,
                   "SKY_CLUMP_SN", 0);
 
   /* Clean up (if necessary). */
@@ -609,7 +609,7 @@ clumps_true_find_sn_thresh(struct segmentparams *p)
   /* If the user wants to check the steps of get an S/N table, then we need
      a unique label for each clump. But in each region, the labels start
      from 1. So we need a central place to keep the next available
-     label. Since `p->numclumps' is not used yet, we will use it here. When
+     label. Since 'p->numclumps' is not used yet, we will use it here. When
      multiple threads are used, we will need a mutex to make sure that only
      one thread can change this central variable at every one moment. */
   if(p->checksegmentation || p->checksn)
@@ -642,7 +642,8 @@ clumps_true_find_sn_thresh(struct segmentparams *p)
 
           /* Do this step. */
           gal_threads_spin_off(clumps_find_make_sn_table, &clprm,
-                               p->ltl.tottiles, p->cp.numthreads);
+                               p->ltl.tottiles, p->cp.numthreads,
+                               p->cp.minmapsize, p->cp.quietmmap);
 
           /* Set the extension name. */
           switch(clprm.step)
@@ -674,7 +675,8 @@ clumps_true_find_sn_thresh(struct segmentparams *p)
     {
       clprm.step=0;
       gal_threads_spin_off(clumps_find_make_sn_table, &clprm,
-                           p->ltl.tottiles, p->cp.numthreads);
+                           p->ltl.tottiles, p->cp.numthreads,
+                           p->cp.minmapsize, p->cp.quietmmap);
     }
 
 
@@ -690,7 +692,7 @@ clumps_true_find_sn_thresh(struct segmentparams *p)
   if( numsn < p->minnumfalse )
     error(EXIT_FAILURE, 0, "%zu usable clumps found in the undetected "
           "regions. This is smaller than the requested minimum number of "
-          "false/reference clumps (%zu, value to the `--minnumfalse' "
+          "false/reference clumps (%zu, value to the '--minnumfalse' "
           "option).\n\n"
           "There are several ways to address the problem. The best and most "
           "highly recommended is to use a larger input if possible (when the "
@@ -699,22 +701,22 @@ clumps_true_find_sn_thresh(struct segmentparams *p)
           "parameters (and therefore cause more scatter/bias in the final "
           "result). Thus don't loosen them too much. Recall that you can "
           "see all the option values to Gnuastro's programs by appending "
-          "`-P' to the end of your command.\n\n"
-          "  * Slightly decrease `--largetilesize' to have more tiles.\n"
-          "  * Decrease `--minskyfrac' (currently %g) to look into more "
+          "'-P' to the end of your command.\n\n"
+          "  * Slightly decrease '--largetilesize' to have more tiles.\n"
+          "  * Decrease '--minskyfrac' (currently %g) to look into more "
           "tiles.\n"
-          "  * Slightly decrease `--snminarea' (currently %zu) to "
+          "  * Slightly decrease '--snminarea' (currently %zu) to "
           "measure more clumps.\n"
           "  * If Segment already works on a dataset with similar noise "
           "properties, you can directly pass the 'true' clump "
-          "signal-to-noise ratio found there to `--clumpsnthresh' and "
+          "signal-to-noise ratio found there to '--clumpsnthresh' and "
           "avoid having to study the undetected regions any more.\n\n"
-          "Append your previous command with `--checksegmentation' to see "
+          "Append your previous command with '--checksegmentation' to see "
           "the steps and get a better feeling of the cause/solution. Note "
           "that the output is a multi-extension FITS file).\n\n"
           "To better understand the segmentation process and options, "
-          "please run the following command (press `SPACE'/arrow-keys to "
-          "navigate and `Q' to return back to the command-line):\n\n"
+          "please run the following command (press 'SPACE'/arrow-keys to "
+          "navigate and 'Q' to return back to the command-line):\n\n"
           "    $ info gnuastro \"Segmentation options\"\n",
           numsn, p->minnumfalse, p->minskyfrac, p->snminarea);
 
@@ -757,8 +759,8 @@ clumps_true_find_sn_thresh(struct segmentparams *p)
       if(p->cp.numthreads>1)
         gal_list_str_add(&comments, "NOTE: In multi-threaded mode, clump "
                          "IDs differ in each run and are not sorted.", 1);
-      gal_list_str_add(&comments, "See also: `SKY_CLUMPS_FOR_SN' HDU of "
-                       "output with `--checksegmentation'.", 1);
+      gal_list_str_add(&comments, "See also: 'SKY_CLUMPS_FOR_SN' HDU of "
+                       "output with '--checksegmentation'.", 1);
       gal_list_str_add(&comments, "S/N of clumps over undetected regions.",
                        1);
       clumps_write_sn_table(p, sn, snind, p->clumpsn_s_name, comments);
diff --git a/bin/segment/clumps.h b/bin/segment/clumps.h
index 8b22863..9d6392b 100644
--- a/bin/segment/clumps.h
+++ b/bin/segment/clumps.h
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -39,7 +39,7 @@ struct clumps_params
   gal_data_t            *snind; /* Array of clump S/N index (for check).   */
 
   /* For detections. */
-  gal_data_t        *labindexs; /* Array of `gal_data_t' with obj indexs.  */
+  gal_data_t        *labindexs; /* Array of 'gal_data_t' with obj indexs.  */
   size_t            totobjects; /* Total number of objects at any point.   */
   size_t             totclumps; /* Total number of clumps at any point.    */
 };
diff --git a/bin/segment/kernel-2d.h b/bin/segment/kernel-2d.h
index e6c0944..7c52bdd 100644
--- a/bin/segment/kernel-2d.h
+++ b/bin/segment/kernel-2d.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -48,7 +48,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    Convert it to C code
    --------------------
 
-   Put the following C program into a file called `kernel.c'.
+   Put the following C program into a file called 'kernel.c'.
 
      #include <stdio.h>
      #include <stdlib.h>
@@ -74,7 +74,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
                if(i % img->dsize[1] == 0 ) printf("\n\n");
              }
 
-           // We cannot use `\b' here, since we are writing directly
+           // We cannot use '\b' here, since we are writing directly
            // to the command-line, so we'll first write the number,
            // then decide if any subsequent character (a comma)
            // should be written.
@@ -96,7 +96,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    -----------------
 
    We can now compile and run that C program and put the outputs in
-   `kernel.c'. Once its created, copy the contents of `kernel-2d.h' after
+   'kernel.c'. Once its created, copy the contents of 'kernel-2d.h' after
    these comments.
 
      $ astbuildprog -q kernel.c > kernel-2d.h
diff --git a/bin/segment/main.c b/bin/segment/main.c
index ceb3274..91c53d6 100644
--- a/bin/segment/main.c
+++ b/bin/segment/main.c
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
diff --git a/bin/segment/main.h b/bin/segment/main.h
index 3391202..889484b 100644
--- a/bin/segment/main.h
+++ b/bin/segment/main.h
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
diff --git a/bin/segment/segment.c b/bin/segment/segment.c
index eaa13fe..2cbcfcd 100644
--- a/bin/segment/segment.c
+++ b/bin/segment/segment.c
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -128,7 +128,7 @@ segment_initialize(struct segmentparams *p)
   p->clabel->wcs=gal_wcs_copy(p->input->wcs);
 
 
-  /* Prepare the `binary', `clabel' and `olabel' arrays. */
+  /* Prepare the 'binary', 'clabel' and 'olabel' arrays. */
   b=p->binary->array;
   o=p->olabel->array;
   f=p->input->array; cf=(c=p->clabel->array)+p->clabel->size;
@@ -141,7 +141,7 @@ segment_initialize(struct segmentparams *p)
           *b = *o > 0;
 
           /* A small sanity check. */
-          if(*o<0)
+          if(*o<0 && *o!=GAL_BLANK_INT32)
             error(EXIT_FAILURE, 0, "%s (hdu: %s) has negative value(s). "
                   "Each non-zero pixel in this image must be positive (a "
                   "counter, counting from 1).", p->useddetectionname,
@@ -210,12 +210,12 @@ segment_relab_noseg(struct clumps_thread_params *cltprm)
    have to be allocated prior to entering this function.
 
    The way to find connected objects is through an adjacency matrix. It is
-   a square matrix with a side equal to numobjs. So to see if regions `a`
-   and `b` are connected. All we have to do is to look at element
+   a square matrix with a side equal to numobjs. So to see if regions 'a'
+   and 'b' are connected. All we have to do is to look at element
    a*numobjs+b or b*numobjs+a and get the answer. Since the number of
    objects in a given region will not be too high, this is efficient. */
 static void
-segment_relab_to_objects(struct clumps_thread_params *cltprm)
+segment_relab_to_objects_array(struct clumps_thread_params *cltprm)
 {
   size_t amwidth=cltprm->numtrueclumps+1;
   struct segmentparams *p=cltprm->clprm->p;
@@ -231,11 +231,12 @@ segment_relab_to_objects(struct clumps_thread_params 
*cltprm)
   gal_data_t *adjacency_d=gal_data_alloc(NULL, GAL_TYPE_UINT8, 2, mdsize,
                                          NULL, 1, p->cp.minmapsize,
                                          p->cp.quietmmap, NULL, NULL, NULL);
+
   float *imgss=p->input->array;
+  int32_t *olabel=p->olabel->array;
   double var=cltprm->std*cltprm->std;
   uint8_t *adjacency=adjacency_d->array;
   size_t nngb=gal_dimension_num_neighbors(ndim);
-  int32_t *clumptoobj, *olabel=p->olabel->array;
   size_t *dinc=gal_dimension_increment(ndim, dsize);
   size_t *s, *sf, i, j, ii, rpnum, *nums=nums_d->array;
   double ave, rpsum, c=sqrt(1/p->cpscorr), *sums=sums_d->array;
@@ -249,146 +250,413 @@ segment_relab_to_objects(struct clumps_thread_params 
*cltprm)
      matrix. Note that at this point, the rivers are also part of the
      "diffuse" regions. So we don't need to go over all the indexs of this
      object, only its diffuse indexs. */
-  if(cltprm->diffuseindexs->size)
-    {
-      sf=(s=cltprm->diffuseindexs->array)+cltprm->diffuseindexs->size;
-      do
-        /* We only want to work on pixels that have already been identified
-           as touching more than one label: river pixels. */
-        if( olabel[ *s ]==GAL_LABEL_RIVER )
-          {
-            /* Initialize the values. */
-            i=ii=0;
-            rpnum=1;              /* River-pixel number of points used. */
-            rpsum=imgss[*s];      /* River-pixel sum of values used.    */
-            memset(ngblabs, 0, nngb*sizeof *ngblabs);
-
-            /* Check all the fully-connected neighbors of this pixel and
-               see if it touches a label or not */
-            GAL_DIMENSION_NEIGHBOR_OP(*s, ndim, dsize, ndim, dinc, {
-                if( olabel[nind] > 0 )
-                  {
-                    /* Add this neighbor's value and increment the number. */
-                    if( !isnan(imgss[nind]) ) { ++rpnum; rpsum+=imgss[nind]; }
+  sf=(s=cltprm->diffuseindexs->array)+cltprm->diffuseindexs->size;
+  do
+    /* We only want to work on pixels that have already been identified as
+       touching more than one label: river pixels. */
+    if( olabel[ *s ]==GAL_LABEL_RIVER )
+      {
+        /* Initialize the values. */
+        i=ii=0;
+        rpnum=1;              /* River-pixel number of points used. */
+        rpsum=imgss[*s];      /* River-pixel sum of values used.    */
+        memset(ngblabs, 0, nngb*sizeof *ngblabs);
+
+        /* Check all the fully-connected neighbors of this pixel and
+           see if it touches a label or not */
+        GAL_DIMENSION_NEIGHBOR_OP(*s, ndim, dsize, ndim, dinc, {
+            if( olabel[nind] > 0 )
+              {
+                /* Add this neighbor's value and increment the number. */
+                if( !isnan(imgss[nind]) ) { ++rpnum; rpsum+=imgss[nind]; }
 
-                    /* Go over the already found neighbors and see if this
-                       grown clump has already been considered or not. */
-                    for(i=0;i<ii;++i) if(ngblabs[i]==olabel[nind]) break;
+                /* Go over the already found neighbors and see if this
+                   grown clump has already been considered or not. */
+                for(i=0;i<ii;++i) if(ngblabs[i]==olabel[nind]) break;
 
-                    /* This is the first time we are getting to this
-                       neighbor: */
-                    if(i==ii) ngblabs[ ii++ ] = olabel[nind];
-                  }
-              } );
+                /* This is the first time we are getting to this
+                   neighbor: */
+                if(i==ii) ngblabs[ ii++ ] = olabel[nind];
+              }
+          } );
+
+        /* For a check:
+        if(ii>0)
+          {
+            printf("%zu, %zu:\n", *s%dsize[1]+1, *s/dsize[1]+1);
+            for(i=0;i<ii;++i) printf("\t%u\n", ngblabs[i]);
+          }
+        */
+
+        /* If more than one neighboring label was found, fill in the
+           'sums' and 'nums' adjacency matrixs with the values for this
+           pixel. Recall that ii is the number of neighboring labels to
+           this river pixel. */
+        if(ii>i)
+          for(i=0;i<ii;++i)
+            for(j=0;j<ii;++j)
+              if(i!=j)
+                {
+                  /* For safety, we will fill both sides of the
+                     diagonal. */
+                  ++nums[ ngblabs[i] * amwidth + ngblabs[j] ];
+                  ++nums[ ngblabs[j] * amwidth + ngblabs[i] ];
+                  sums[ ngblabs[i] * amwidth + ngblabs[j] ] +=
+                    rpsum/rpnum;
+                  sums[ ngblabs[j] * amwidth + ngblabs[i] ] +=
+                    rpsum/rpnum;
+                }
+      }
+  while(++s<sf);
 
-            /* For a check:
-            if(ii>0)
+  /* We now have the average values and number of all rivers between
+     the grown clumps. We now want to finalize their connection (given
+     the user's criteria). */
+  for(i=1;i<amwidth;++i)
+    for(j=1;j<i;++j)
+      {
+        ii = i * amwidth + j;
+        if(nums[ii]>p->minriverlength)       /* There is a connection. */
+          {
+            /* For easy reading. */
+            ave=sums[ii]/nums[ii];
+
+            /* In case the average is negative (only possible if 'sums'
+               is negative), don't change the adjacency: it is already
+               initialized to zero. Note that even an area of 1 is
+               acceptable, and we put no area criteria here, because
+               the fact that a river exists between two clumps is
+               important. */
+            if( ave>0.0f && ( c * ave / sqrt(ave+var) ) > p->objbordersn )
               {
-                printf("%zu, %zu:\n", *s%dsize[1]+1, *s/dsize[1]+1);
-                for(i=0;i<ii;++i) printf("\t%u\n", ngblabs[i]);
+                adjacency[ii]=1;   /* We want to set both sides of the */
+                adjacency[ j * amwidth + i ] = 1; /* Symmetric matrix. */
               }
-            */
-
-            /* If more than one neighboring label was found, fill in the
-               'sums' and 'nums' adjacency matrixs with the values for this
-               pixel. Recall that ii is the number of neighboring labels to
-               this river pixel. */
-            if(ii>i)
-              for(i=0;i<ii;++i)
-                for(j=0;j<ii;++j)
-                  if(i!=j)
-                    {
-                      /* For safety, we will fill both sides of the
-                         diagonal. */
-                      ++nums[ ngblabs[i] * amwidth + ngblabs[j] ];
-                      ++nums[ ngblabs[j] * amwidth + ngblabs[i] ];
-                      sums[ ngblabs[i] * amwidth + ngblabs[j] ] +=
-                        rpsum/rpnum;
-                      sums[ ngblabs[j] * amwidth + ngblabs[i] ] +=
-                        rpsum/rpnum;
-                    }
           }
-      while(++s<sf);
-
+      }
 
-      /* We now have the average values and number of all rivers between
-         the grown clumps. We now want to finalize their connection (given
-         the user's criteria). */
+  /* For a check:
+  if(cltprm->id==XXX)
+    {
+      printf("=====================\n");
+      printf("%zu:\n--------\n", cltprm->id);
       for(i=1;i<amwidth;++i)
-        for(j=1;j<i;++j)
+        {
+          printf(" %zu...\n", i);
+          for(j=1;j<amwidth;++j)
+            {
+              ii=i*amwidth+j;
+              if(nums[ii])
+                {
+                  ave=sums[ii]/nums[ii];
+                  printf("    ...%zu: N:%-4zu S:%-10.2f S/N: %-10.2f "
+                         "--> %u\n", j, nums[ii], sums[ii],
+                         c*ave/sqrt(ave+var), adjacency[ii]);
+                }
+            }
+          printf("\n");
+        }
+    }
+  */
+
+  /* Calculate the new labels for each grown clump. */
+  cltprm->clumptoobj = gal_binary_connected_adjacency_matrix(adjacency_d,
+                                                     &cltprm->numobjects);
+
+  /* Clean up and return. */
+  free(dinc);
+  free(ngblabs);
+  gal_data_free(nums_d);
+  gal_data_free(sums_d);
+  gal_data_free(adjacency_d);
+}
+
+
+
+
+
+/* For a large number of clumps, 'segment_relab_to_objects_array' will
+   consume too much memory and can completely fill the memory. So we need
+   to use a list-based adjacency solution. */
+typedef struct segment_relab_list_t
+{
+  size_t num;
+  double sum;
+  size_t ngbid;
+  struct segment_relab_list_t *next;
+} segment_relab_list_t;
+
+
+
+
+
+static void
+segment_relab_list_add(struct segment_relab_list_t **list, size_t ngbid,
+                       double value)
+{
+  int done=0;
+  struct segment_relab_list_t *tmp=NULL;
+
+  /* Check if the desired index has already been found or not. Note that if
+     'list' is empty, then it will never enter the loop.*/
+  for(tmp=*list; tmp!=NULL; tmp=tmp->next)
+    if( tmp->ngbid == ngbid )
+      {
+        tmp->sum += value;
+        ++tmp->num;
+        done=1;
+        break;
+      }
+
+  /* Either the list was empty, or the desired index didn't exist in it. So
+     we need to allocate a new node and add it to the list. */
+  if(done==0)
+    {
+      /* Allocate a new node. */
+      errno=0;
+      tmp=malloc(sizeof *tmp);
+      if(tmp==NULL)
+        error(EXIT_FAILURE, errno, "%s: couldn't allocate %zu bytes "
+              "for 'tmp'", __func__, sizeof tmp);
+
+      /* Fill in the node. */
+      tmp->num=1;
+      tmp->sum=value;
+      tmp->ngbid=ngbid;
+
+      /* Put the node at the top of the list. */
+      tmp->next = *list ? *list : NULL;
+      *list=tmp;
+    }
+}
+
+
+
+
+
+static void
+segment_relab_to_objects_list(struct clumps_thread_params *cltprm)
+{
+  size_t amwidth=cltprm->numtrueclumps+1;
+  struct segmentparams *p=cltprm->clprm->p;
+  size_t ndim=p->input->ndim, *dsize=p->input->dsize;
+
+  int addadj;
+  float *imgss=p->input->array;
+  size_t *s, *sf, i, j, ii, rpnum;
+  int32_t *olabel=p->olabel->array;
+  double var=cltprm->std*cltprm->std;
+  gal_list_sizet_t **adjacency, *atmp;
+  segment_relab_list_t **rlist, *rtmp;
+  double ave, rpsum, c=sqrt(1/p->cpscorr);
+  size_t nngb=gal_dimension_num_neighbors(ndim);
+  size_t *dinc=gal_dimension_increment(ndim, dsize);
+  int32_t *ngblabs=gal_pointer_allocate(GAL_TYPE_UINT32, nngb, 0, __func__,
+                                         "ngblabs");
+
+  /* Allocate the two lists to keep the number and sum, as well as the
+     final adjacency matrix. */
+  errno=0;
+  rlist=calloc(amwidth, sizeof *rlist);
+  if(rlist==NULL)
+    error(EXIT_FAILURE, errno, "%s: couldn't allocate %zu bytes for 'rlist'",
+          __func__, amwidth * (sizeof *rlist));
+  errno=0;
+  adjacency=calloc(amwidth, sizeof *adjacency);
+  if(adjacency==NULL)
+    error(EXIT_FAILURE, errno, "%s: couldn't allocate %zu bytes for 
'adjacency'",
+          __func__, amwidth * (sizeof *adjacency));
+
+  /* Go over all the still-unlabeled pixels (if they exist) and see which
+     labels they touch. In the process, get the average value of the
+     river-pixel values and put them in the respective adjacency
+     matrix. Note that at this point, the rivers are also part of the
+     "diffuse" regions. So we don't need to go over all the indexs of this
+     object, only its diffuse indexs. */
+  sf=(s=cltprm->diffuseindexs->array)+cltprm->diffuseindexs->size;
+  do
+    /* We only want to work on pixels that have already been identified as
+       touching more than one label: river pixels. */
+    if( olabel[ *s ]==GAL_LABEL_RIVER )
+      {
+        /* Initialize the values. */
+        i=ii=0;
+        rpnum=1;              /* River-pixel number of points used. */
+        rpsum=imgss[*s];      /* River-pixel sum of values used.    */
+        memset(ngblabs, 0, nngb*sizeof *ngblabs);
+
+        /* Check all the fully-connected neighbors of this pixel and
+           see if it touches a label or not */
+        GAL_DIMENSION_NEIGHBOR_OP(*s, ndim, dsize, ndim, dinc, {
+            if( olabel[nind] > 0 )
+              {
+                /* Add this neighbor's value and increment the number. */
+                if( !isnan(imgss[nind]) ) { ++rpnum; rpsum+=imgss[nind]; }
+
+                /* Go over the already found neighbors and see if this
+                   grown clump has already been considered or not. */
+                for(i=0;i<ii;++i) if(ngblabs[i]==olabel[nind]) break;
+
+                /* This is the first time we are getting to this
+                   neighbor: */
+                if(i==ii) ngblabs[ ii++ ] = olabel[nind];
+              }
+          } );
+
+        /* For a check:
+        if(ii>0)
+          {
+            printf("%zu, %zu:\n", *s%dsize[1]+1, *s/dsize[1]+1);
+            for(i=0;i<ii;++i) printf("\t%u\n", ngblabs[i]);
+          }
+        */
+
+        /* If more than one neighboring label was found, fill in the
+           'sums' and 'nums' adjacency matrixs with the values for this
+           pixel. Recall that ii is the number of neighboring labels to
+           this river pixel. */
+        if(ii>i)
+          for(i=0;i<ii;++i)
+            for(j=0;j<ii;++j)
+              if(i!=j)
+                {
+                  /* For safety and ease of processing, we will fill
+                     both sides of the diagonal. */
+                  segment_relab_list_add(&rlist[ ngblabs[i] ], ngblabs[j],
+                                         rpsum/rpnum);
+                  segment_relab_list_add(&rlist[ ngblabs[j] ], ngblabs[i],
+                                         rpsum/rpnum);
+                }
+      }
+  while(++s<sf);
+
+  /* We now have the average values and number of all rivers between
+     the grown clumps. We now want to finalize their connection (given
+     the user's criteria). */
+  for(i=1; i<amwidth; ++i)
+    for(rtmp=rlist[i]; rtmp!=NULL; rtmp=rtmp->next)
+      {
+        if(rtmp->num > p->minriverlength)  /* There is a connection. */
           {
-            ii = i * amwidth + j;
-            if(nums[ii]>p->minriverlength)       /* There is a connection. */
+            /* For easy reading. */
+            ave = rtmp->sum / rtmp->num;
+
+            /* In case the average is negative (only possible if 'sums'
+               is negative), don't change the adjacency: it is already
+               initialized to zero. Note that even an area of 1 is
+               acceptable, and we put no area criteria here, because
+               the fact that a river exists between two clumps is
+               important. */
+            if( ave>0.0f && ( c * ave / sqrt(ave+var) ) > p->objbordersn )
               {
-                /* For easy reading. */
-                ave=sums[ii]/nums[ii];
-
-                /* In case the average is negative (only possible if `sums'
-                   is negative), don't change the adjacency: it is already
-                   initialized to zero. Note that even an area of 1 is
-                   acceptable, and we put no area criteria here, because
-                   the fact that a river exists between two clumps is
-                   important. */
-                if( ave>0.0f && ( c * ave / sqrt(ave+var) ) > p->objbordersn )
+                addadj=1;
+                for(atmp=adjacency[i]; atmp!=NULL; atmp=atmp->next)
+                  if(atmp->v==rtmp->ngbid)
+                    { addadj=0; break; }
+                if(addadj)
                   {
-                    adjacency[ii]=1;   /* We want to set both sides of the */
-                    adjacency[ j * amwidth + i ] = 1; /* Symmetric matrix. */
+                    gal_list_sizet_add(&adjacency[i], rtmp->ngbid);
+                    gal_list_sizet_add(&adjacency[rtmp->ngbid], i);
                   }
               }
           }
+      }
 
-
-      /* For a check:
-      if(cltprm->id==XXX)
+  /* For a check:
+  if(cltprm->id==2)
+    {
+      printf("=====================\n");
+      printf("%zu:\n--------\n", cltprm->id);
+      for(i=1; i<amwidth; ++i)
         {
-          printf("=====================\n");
-          printf("%zu:\n--------\n", cltprm->id);
-          for(i=1;i<amwidth;++i)
+          printf(" %zu...\n", i);
+          for(rtmp=rlist[i]; rtmp!=NULL; rtmp=rtmp->next)
             {
-              printf(" %zu...\n", i);
-              for(j=1;j<amwidth;++j)
+              if(rtmp->num)
                 {
-                  ii=i*amwidth+j;
-                  if(nums[ii])
-                    {
-                      ave=sums[ii]/nums[ii];
-                      printf("    ...%zu: N:%-4zu S:%-10.2f S/N: %-10.2f "
-                             "--> %u\n", j, nums[ii], sums[ii],
-                             c*ave/sqrt(ave+var), adjacency[ii]);
-                    }
+                  ave=rtmp->sum/rtmp->num;
+                  printf("    ...%zu: N:%-4zu S:%-10.2f S/N: %-10.2f\n",
+                         rtmp->ngbid, rtmp->num, rtmp->sum,
+                         c*ave/sqrt(ave+var));
                 }
-              printf("\n");
             }
+          printf("FINAL: ");
+          for(atmp=adjacency[i]; atmp!=NULL; atmp=atmp->next)
+            printf("%zu ", atmp->v);
+          printf("\n\n");
         }
-      */
+      exit(0);
+    }
+  */
+
+  /* Calculate the new labels for each grown clump. */
+  cltprm->clumptoobj=gal_binary_connected_adjacency_list(adjacency,
+                                               amwidth, p->cp.minmapsize,
+                                               p->cp.quietmmap,
+                                               &cltprm->numobjects);
+
+  /* Clean up. */
+  for(i=1; i<amwidth; ++i)
+    while(rlist[i]!=NULL)
+      { rtmp=rlist[i]->next; free(rlist[i]); rlist[i]=rtmp; }
+  for(i=1; i<amwidth; ++i) gal_list_sizet_free(adjacency[i]);
+  free(adjacency);
+  free(ngblabs);
+  free(rlist);
+  free(dinc);
+}
 
 
-      /* Calculate the new labels for each grown clump. */
-      cltprm->clumptoobj = gal_binary_connected_adjacency_matrix(adjacency_d,
-                                                         &cltprm->numobjects);
+
+
+
+/* Relabel objects. */
+static void
+segment_relab_to_objects(struct clumps_thread_params *cltprm)
+{
+  struct segmentparams *p=cltprm->clprm->p;
+
+  size_t *s, *sf;
+  size_t i, amwidth=cltprm->numtrueclumps+1;
+  int32_t *clumptoobj, *olabel=p->olabel->array;
+
+  /* Find the final object IDs if there is any list of diffuse pixels. It
+     can happen that we don't have a list of diffuse pixels when the user
+     sets a very high 'gthresh' threshold and wants to make sure that each
+     clump is a separate object. So we need to define the number of objects
+     and 'clumptoobj' manually.*/
+  if(cltprm->diffuseindexs->size)
+    {
+      /* See if we should use a matrix-based adjacent finding (good for
+         small numbers) or a list-based method (necessary for large
+         numbers). Here we'll set the limit to 1000, because of this: the
+         adjacency array will be 1e6 pixels in three types (size_t, double
+         and uint8_t), so it will consume (8+8+1)*1e6 bytes which is 17
+         megabytes and reasonable. But the same argument for a 10000 limit
+         would be 17*e8 bytes or 1.7GB which is not reasonable. Note that
+         cases with +600000 have also been encountered (wide images of
+         dense fields near the Milky way disk). */
+      if( amwidth>1000 )
+        segment_relab_to_objects_list(cltprm);
+      else
+        segment_relab_to_objects_array(cltprm);
       clumptoobj = cltprm->clumptoobj->array;
     }
-
-  /* There was no list of diffuse pixels, this happens when the user sets a
-     very high `gthresh' threshold and wants to make sure that each clump
-     is a separate object. So we need to define the number of objects and
-     `clumptoobj' manually. */
   else
     {
-      /* Allocate the `clumptoobj' array. */
+      /* Allocate the 'clumptoobj' array. */
       cltprm->clumptoobj = gal_data_alloc(NULL, GAL_TYPE_INT32, 1, &amwidth,
                                           NULL, 1, p->cp.minmapsize,
                                           p->cp.quietmmap, NULL, NULL, NULL);
       clumptoobj = cltprm->clumptoobj->array;
 
-      /* Fill in the `clumptoobj' array with the indexs of the objects. */
+      /* Fill in the 'clumptoobj' array with the indexs of the objects. */
       for(i=0;i<amwidth;++i) clumptoobj[i]=i;
 
       /* Set the number of objects. */
       cltprm->numobjects = cltprm->numtrueclumps;
     }
 
-
   /* For a check
   if(cltprm->id==XXXX)
     {
@@ -400,26 +668,18 @@ segment_relab_to_objects(struct clumps_thread_params 
*cltprm)
     }
   */
 
-
   /* Correct all the labels. */
   sf=(s=cltprm->indexs->array)+cltprm->indexs->size;
   do
     if( olabel[*s] > 0 )
       olabel[*s] = clumptoobj[ olabel[*s] ];
   while(++s<sf);
-
-
-  /* Clean up and return. */
-  free(dinc);
-  free(ngblabs);
-  gal_data_free(nums_d);
-  gal_data_free(sums_d);
-  gal_data_free(adjacency_d);
 }
 
 
 
 
+
 /* The correspondance between the clumps and objects has been found. With
    this function, we want to correct the clump labels so the clump IDs in
    each object start from 1 and are contiguous. */
@@ -487,7 +747,7 @@ segment_relab_overall(struct clumps_thread_params *cltprm)
   if(clprm->p->cp.numthreads>1)
     pthread_mutex_unlock(&clprm->labmutex);
 
-  /* Increase all the object labels by `startinglab'. */
+  /* Increase all the object labels by 'startinglab'. */
   if( onlyclumps )
     {
       if(cltprm->numtrueclumps>0)
@@ -551,7 +811,7 @@ segment_on_threads(void *in_prm)
       cltprm.numinitclumps = cltprm.numtrueclumps = cltprm.numobjects = 0;
 
 
-      /* The `topinds' array is only necessary when the user wants to
+      /* The 'topinds' array is only necessary when the user wants to
          ignore true clumps with a peak touching a river. */
       if(p->keepmaxnearriver==0)
         {
@@ -587,7 +847,7 @@ segment_on_threads(void *in_prm)
          the user has also asked for a check image, we can break out of the
          loop at that point.
 
-         Note that the array of `gal_data_t' that keeps the S/N table for
+         Note that the array of 'gal_data_t' that keeps the S/N table for
          each detection is allocated before threading starts. However, when
          the user wants to inspect the steps, this function is called
          multiple times. So we need to avoid over-writing the allocations. */
@@ -648,7 +908,7 @@ segment_on_threads(void *in_prm)
 
               /* If the user has asked for grown clumps in the clumps image
                  instead of the raw clumps, then replace the indexs in the
-                 `clabel' array is well. In this case, there will always be
+                 'clabel' array is well. In this case, there will always be
                  one "clump". */
               if(p->grownclumps)
                 {
@@ -678,7 +938,10 @@ segment_on_threads(void *in_prm)
 
               /* Identify the objects in this detection using the grown
                  clumps and correct the grown clump labels into new object
-                 labels. */
+                 labels. When the number of clumps are large the
+                 array-based adjacency finding will consume too much
+                 memory. So we should switch to a list-based adjacency
+                 process instead. */
               segment_relab_to_objects(&cltprm);
               if(clprm->step==4)
                 {
@@ -752,7 +1015,7 @@ segment_save_sn_table(struct clumps_params *clprm)
 
 
   /* Find the total number of clumps in all the initial detections. Recall
-     that the `size' values were one more than the actual number because
+     that the 'size' values were one more than the actual number because
      the labelings start from 1. */
   for(i=1;i<p->numdetections+1;++i)
     if( clprm->sn[i].size > 1 )
@@ -788,10 +1051,10 @@ segment_save_sn_table(struct clumps_params *clprm)
 
 
   /* Write the comments. */
-  gal_list_str_add(&comments, "See also: `CLUMPS_ALL_DET' HDU of "
-                   "output with `--checksegmentation'.", 1);
-  if( asprintf(&msg, "S/N values of `nan': clumps smaller than "
-               "`--snminarea' of %zu.", p->snminarea)<0 )
+  gal_list_str_add(&comments, "See also: 'CLUMPS_ALL_DET' HDU of "
+                   "output with '--checksegmentation'.", 1);
+  if( asprintf(&msg, "S/N values of 'nan': clumps smaller than "
+               "'--snminarea' of %zu.", p->snminarea)<0 )
     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
   gal_list_str_add(&comments, msg, 0);
   gal_list_str_add(&comments, "S/N of clumps over detected regions.", 1);
@@ -801,7 +1064,7 @@ segment_save_sn_table(struct clumps_params *clprm)
   /* Set the column pointers and write them into a table.. */
   clumpinobj->next=sn;
   objind->next=clumpinobj;
-  gal_table_write(objind, comments, p->cp.tableformat, p->clumpsn_d_name,
+  gal_table_write(objind, NULL, comments, p->cp.tableformat, p->clumpsn_d_name,
                   "DET_CLUMP_SN", 0);
 
 
@@ -824,6 +1087,54 @@ segment_save_sn_table(struct clumps_params *clprm)
 
 
 
+/* Avoid non-reproducible labels (when built on multiple threads). Note
+   that when working with objects, the clump labels don't need to be
+   re-labeled (they always start from 1 within each object and are thus
+   already thread-safe).*/
+static void
+segment_reproducible_labels(struct segmentparams *p)
+{
+  size_t i;
+  gal_data_t *new;
+  int32_t currentlab=0, *oldarr, *newarr, *newlabs;
+  gal_data_t *old = p->onlyclumps ? p->clabel : p->olabel;
+  size_t numlabsplus1 = (p->onlyclumps ? p->numclumps : p->numobjects) + 1;
+
+  /* Allocate the necessary datasets. */
+  new=gal_data_alloc(NULL, old->type, old->ndim, old->dsize, old->wcs, 0,
+                     p->cp.minmapsize, p->cp.quietmmap, old->name, old->unit,
+                     old->comment);
+  newlabs=gal_pointer_allocate(old->type, numlabsplus1, 0, __func__,
+                               "newlabs");
+
+  /* Initialize the newlabs array to blank (so we don't relabel
+     things). */
+  for(i=0;i<numlabsplus1;++i) newlabs[i]=GAL_BLANK_INT32;
+
+  /* Parse the old dataset and set the new labels. */
+  oldarr=old->array;
+  for(i=0;i<old->size;++i)
+    if( oldarr[i] > 0 && newlabs[ oldarr[i] ]==GAL_BLANK_INT32 )
+      newlabs[ oldarr[i] ] = ++currentlab;
+
+  /* For a check.
+  for(i=0;i<numlabsplus1;++i) printf("%zu --> %d\n", i, newlabs[i]);
+  */
+
+  /* Fill the newly labeled dataset. */
+  newarr=new->array;
+  for(i=0;i<old->size;++i)
+    newarr[i] = oldarr[i]>0 ? newlabs[ oldarr[i] ] : oldarr[i];
+
+  /* Clean up. */
+  free(newlabs);
+  if(p->onlyclumps) { gal_data_free(p->clabel); p->clabel=new; }
+  else              { gal_data_free(p->olabel); p->olabel=new; }
+}
+
+
+
+
 /* Find true clumps over the detected regions. */
 static void
 segment_detections(struct segmentparams *p)
@@ -839,7 +1150,7 @@ segment_detections(struct segmentparams *p)
 
 
   /* Initialize the necessary thread parameters. Note that since the object
-     labels begin from one, the `sn' array will have one extra element.*/
+     labels begin from one, the 'sn' array will have one extra element.*/
   clprm.p=p;
   clprm.sky0_det1=1;
   clprm.totclumps=0;
@@ -886,7 +1197,8 @@ segment_detections(struct segmentparams *p)
 
           /* (Re-)do everything until this step. */
           gal_threads_spin_off(segment_on_threads, &clprm,
-                               p->numdetections, p->cp.numthreads);
+                               p->numdetections, p->cp.numthreads,
+                               p->cp.minmapsize, p->cp.quietmmap);
 
           /* Set the extension name. */
           switch(clprm.step)
@@ -897,7 +1209,7 @@ segment_detections(struct segmentparams *p)
               if(!p->cp.quiet)
                 {
                   if( asprintf(&msg, "Identified clumps over detections  "
-                               "(HDU: `%s').", demo->name)<0 )
+                               "(HDU: '%s').", demo->name)<0 )
                     error(EXIT_FAILURE, 0, "%s: asprintf allocation",
                           __func__);
                   gal_timing_report(NULL, msg, 2);
@@ -911,7 +1223,7 @@ segment_detections(struct segmentparams *p)
               if(!p->cp.quiet)
                 {
                   if( asprintf(&msg, "True clumps found                  "
-                               "(HDU: `%s').", demo->name)<0 )
+                               "(HDU: '%s').", demo->name)<0 )
                     error(EXIT_FAILURE, 0, "%s: asprintf allocation",
                           __func__);
                   gal_timing_report(NULL, msg, 2);
@@ -927,7 +1239,7 @@ segment_detections(struct segmentparams *p)
                   gal_timing_report(NULL, "Identify objects...",
                                     1);
                   if( asprintf(&msg, "True clumps grown                  "
-                               "(HDU: `%s').", demo->name)<0 )
+                               "(HDU: '%s').", demo->name)<0 )
                     error(EXIT_FAILURE, 0, "%s: asprintf allocation",
                           __func__);
                   gal_timing_report(NULL, msg, 2);
@@ -941,7 +1253,7 @@ segment_detections(struct segmentparams *p)
               if(!p->cp.quiet)
                 {
                   if( asprintf(&msg, "Identified objects over detections "
-                               "(HDU: `%s').", demo->name)<0 )
+                               "(HDU: '%s').", demo->name)<0 )
                     error(EXIT_FAILURE, 0, "%s: asprintf allocation",
                           __func__);
                   gal_timing_report(NULL, msg, 2);
@@ -955,7 +1267,7 @@ segment_detections(struct segmentparams *p)
               if(!p->cp.quiet)
                 {
                   if( asprintf(&msg, "Objects grown to cover full area   "
-                               "(HDU: `%s').", demo->name)<0 )
+                               "(HDU: '%s').", demo->name)<0 )
                     error(EXIT_FAILURE, 0, "%s: asprintf allocation",
                           __func__);
                   gal_timing_report(NULL, msg, 2);
@@ -969,7 +1281,7 @@ segment_detections(struct segmentparams *p)
               if(!p->cp.quiet)
                 {
                   if( asprintf(&msg, "Clumps given their final label     "
-                               "(HDU: `%s').", demo->name)<0 )
+                               "(HDU: '%s').", demo->name)<0 )
                     error(EXIT_FAILURE, 0, "%s: asprintf allocation",
                           __func__);
                   gal_timing_report(NULL, msg, 2);
@@ -983,7 +1295,7 @@ segment_detections(struct segmentparams *p)
               if(!p->cp.quiet)
                 {
                   if( asprintf(&msg, "Objects given their final label    "
-                               "(HDU: `%s').", demo->name)<0 )
+                               "(HDU: '%s').", demo->name)<0 )
                     error(EXIT_FAILURE, 0, "%s: asprintf allocation",
                           __func__);
                   gal_timing_report(NULL, msg, 2);
@@ -1013,7 +1325,8 @@ segment_detections(struct segmentparams *p)
     {
       clprm.step=0;
       gal_threads_spin_off(segment_on_threads, &clprm, p->numdetections,
-                           p->cp.numthreads);
+                           p->cp.numthreads, p->cp.minmapsize,
+                           p->cp.quietmmap);
     }
 
 
@@ -1028,6 +1341,13 @@ segment_detections(struct segmentparams *p)
   p->numobjects=clprm.totobjects;
 
 
+  /* Correct the final object labels to start from the bottom of the
+     image. This is necessary because we define objects on multiple
+     threads, so every time a program is run, an object can have a
+     different label! */
+  segment_reproducible_labels(p);
+
+
   /* Clean up allocated structures and destroy the mutex. */
   gal_data_array_free(clprm.sn, p->numdetections+1, 1);
   gal_data_array_free(labindexs, p->numdetections+1, 1);
@@ -1070,10 +1390,10 @@ segment_output(struct segmentparams *p)
   /* The clump labels. */
   gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "CLUMPSN", 0,
                         &p->clumpsnthresh, 0, "Minimum S/N of true clumps",
-                        0, "ratio");
+                        0, "ratio", 0);
   gal_fits_key_list_add(&keys, GAL_TYPE_SIZE_T, "NUMLABS", 0,
                         &p->numclumps, 0, "Total number of clumps", 0,
-                        "counter");
+                        "counter", 0);
   p->clabel->name="CLUMPS";
   gal_fits_img_write(p->clabel, p->cp.output, keys, PROGRAM_NAME);
   p->clabel->name=NULL;
@@ -1085,7 +1405,7 @@ segment_output(struct segmentparams *p)
     {
       gal_fits_key_list_add(&keys, GAL_TYPE_SIZE_T, "NUMLABS", 0,
                             &p->numobjects, 0, "Total number of objects", 0,
-                            "counter");
+                            "counter", 0);
       p->olabel->name="OBJECTS";
       gal_fits_img_write(p->olabel, p->cp.output, keys, PROGRAM_NAME);
       p->olabel->name=NULL;
@@ -1101,17 +1421,17 @@ segment_output(struct segmentparams *p)
         gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "MAXSTD", 0,
                               &p->maxstd, 0,
                               "Maximum raw tile standard deviation", 0,
-                              p->input->unit);
+                              p->input->unit, 0);
       if( !isnan(p->minstd) )
         gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "MINSTD", 0,
                               &p->minstd, 0,
                               "Minimum raw tile standard deviation", 0,
-                              p->input->unit);
+                              p->input->unit, 0);
       if( !isnan(p->medstd) )
         gal_fits_key_list_add(&keys, GAL_TYPE_FLOAT32, "MEDSTD", 0,
                               &p->medstd, 0,
                               "Median raw tile standard deviation", 0,
-                              p->input->unit);
+                              p->input->unit, 0);
 
       /* If the input was actually a variance dataset, we'll need to take
          its square root before writing it. We want this output to be a
@@ -1136,7 +1456,7 @@ segment_output(struct segmentparams *p)
 
   /* Let the user know that the output is written. */
   if(!p->cp.quiet)
-    printf("  - Output written to `%s'.\n", p->cp.output);
+    printf("  - Output written to '%s'.\n", p->cp.output);
 }
 
 
@@ -1241,7 +1561,7 @@ segment(struct segmentparams *p)
 
 
   /* If the user wanted to check the segmentation and hasn't called
-     `continueaftercheck', then stop Segment. */
+     'continueaftercheck', then stop Segment. */
   if(p->segmentationname && !p->continueaftercheck)
     ui_abort_after_check(p, p->segmentationname, NULL,
                          "showing all segmentation steps");
diff --git a/bin/segment/segment.h b/bin/segment/segment.h
index b179dfe..720e5a6 100644
--- a/bin/segment/segment.h
+++ b/bin/segment/segment.h
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/segment/ui.c b/bin/segment/ui.c
index 77b1a44..773c323 100644
--- a/bin/segment/ui.c
+++ b/bin/segment/ui.c
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -150,7 +150,7 @@ ui_initialize_options(struct segmentparams *p,
 
         case GAL_OPTIONS_KEY_TABLEFORMAT:
           cp->coptions[i].mandatory=GAL_OPTIONS_MANDATORY;
-          cp->coptions[i].doc="`txt', `fits-ascii', `fits-binary'.";
+          cp->coptions[i].doc="'txt', 'fits-ascii', 'fits-binary'.";
           break;
         }
     }
@@ -166,18 +166,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct segmentparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* Pass 'ygal_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`
+     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, "
+    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");
 
@@ -225,18 +225,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct segmentparams *p)
 {
   /* If the full area is to be used as a single detection, we can't find
      the S/N value from the un-detected regions, so the user must have
-     given the `clumpsnthresh' option. */
+     given the 'clumpsnthresh' option. */
   if( p->detectionname
       && !strcmp(p->detectionname, DETECTION_ALL)
       && isnan(p->clumpsnthresh) )
-    error(EXIT_FAILURE, 0, "`--clumpsnthresh' (`-%c') not given.\n\n"
-          "When `--detection=all' (the whole input dataset is assumed to "
+    error(EXIT_FAILURE, 0, "'--clumpsnthresh' ('-%c') not given.\n\n"
+          "When '--detection=all' (the whole input dataset is assumed to "
           "be a detection), Segment can't use the undetected pixels to find "
           "the signal-to-noise ratio of true clumps. Therefore it is "
           "mandatory to provide a signal-to-noise ratio manually",
@@ -244,19 +244,19 @@ ui_read_check_only_options(struct segmentparams *p)
 
   /* If the convolved HDU is given. */
   if(p->convolvedname && p->chdu==NULL)
-    error(EXIT_FAILURE, 0, "no value given to `--convolvedhdu'. When the "
-          "`--convolved' option is called (to specify a convolved dataset "
+    error(EXIT_FAILURE, 0, "no value given to '--convolvedhdu'. When the "
+          "'--convolved' option is called (to specify a convolved dataset "
           "and avoid convolution) it is mandatory to also specify a HDU "
           "for it");
 
   /* For the options that make tables, the table format option is
      mandatory. */
   if( p->checksn && p->cp.tableformat==0 )
-    error(EXIT_FAILURE, 0, "`--tableformat' is necessary with the "
-          "`--checksn' option.\n"
-          "Please see description for `--tableformat' after running the "
-          "following command for more information (use `SPACE' to go down "
-          "the page and `q' to return to the command-line):\n\n"
+    error(EXIT_FAILURE, 0, "'--tableformat' is necessary with the "
+          "'--checksn' option.\n"
+          "Please see description for '--tableformat' after running the "
+          "following command for more information (use 'SPACE' to go down "
+          "the page and 'q' to return to the command-line):\n\n"
           "    $ info gnuastro \"Input Output options\"");
 
   /* Kernel checks. */
@@ -269,7 +269,7 @@ ui_read_check_only_options(struct segmentparams *p)
       if( gal_fits_name_is_fits(p->kernelname) && p->khdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified for kernel. When the "
               "kernel is a FITS file, a HDU must also be specified. You "
-              "can use the `--khdu' option and give it the HDU number "
+              "can use the '--khdu' option and give it the HDU number "
               "(starting from zero), extension name, or anything "
               "acceptable by CFITSIO");
     }
@@ -279,11 +279,11 @@ ui_read_check_only_options(struct segmentparams *p)
      (higher-is-better), not the contamination level
      (lower-is-better). This actually happened in a few cases: where we
      wanted a false detection rate of 0.0001 (a super-high value!), and
-     instead of inputing 0.9999, we mistakenly gave `--snquant' a value of
-     `0.0001'. We were thus fully confused with the output (an extremely
+     instead of inputing 0.9999, we mistakenly gave '--snquant' a value of
+     '0.0001'. We were thus fully confused with the output (an extremely
      low value) and thought its a bug, while it wasn't! */
   if(p->snquant<0.1)
-    fprintf(stderr, "\nWARNING: Value of `--snquant' (`-c') is %g. Note "
+    fprintf(stderr, "\nWARNING: Value of '--snquant' ('-c') is %g. Note "
             "that this is not a contamination rate (where lower is "
             "better), it is a purity rate (where higher is better). If you "
             "intentionally asked for such a low purity level, please "
@@ -307,8 +307,8 @@ ui_check_options_and_arguments(struct segmentparams *p)
       /* If it is FITS, a HDU is also mandatory. */
       if( gal_fits_name_is_fits(p->inputname) && p->cp.hdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified. When the input is a FITS "
-              "file, a HDU must also be specified, you can use the `--hdu' "
-              "(`-h') option and give it the HDU number (starting from "
+              "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");
 
     }
@@ -449,14 +449,14 @@ ui_prepare_inputs(struct segmentparams *p)
 
       /* Make sure it is the same size as the input. */
       if( gal_dimension_is_different(p->input, p->conv) )
-        error(EXIT_FAILURE, 0, "%s (hdu %s), given to `--convolved' and "
-              "`--chdu', is not the same size as the input (%s, hdu: %s)",
+        error(EXIT_FAILURE, 0, "%s (hdu %s), given to '--convolved' and "
+              "'--chdu', is not the same size as the input (%s, hdu: %s)",
               p->convolvedname, p->chdu, p->inputname, p->cp.hdu);
     }
 
 
   /* Read the detected label image and check its size. When the user gives
-     `--detection=all', then the whole input is assumed to be a single
+     '--detection=all', then the whole input is assumed to be a single
      detection. */
   if( strcmp(p->useddetectionname, DETECTION_ALL) )
     {
@@ -467,14 +467,14 @@ ui_prepare_inputs(struct segmentparams *p)
       p->olabel->ndim=gal_dimension_remove_extra(p->olabel->ndim,
                                                  p->olabel->dsize, NULL);
       if( gal_dimension_is_different(p->input, p->olabel) )
-        error(EXIT_FAILURE, 0, "`%s' (hdu: %s) and `%s' (hdu: %s) have a"
+        error(EXIT_FAILURE, 0, "'%s' (hdu: %s) and '%s' (hdu: %s) have a"
               "different dimension/size", p->useddetectionname, p->dhdu,
               p->inputname, p->cp.hdu);
 
       /* Make sure the detected labels are not floating point. */
       if(p->olabel->type==GAL_TYPE_FLOAT32
          || p->olabel->type==GAL_TYPE_FLOAT64)
-        error(EXIT_FAILURE, 0, "%s (hdu: %s) has a `%s' type. The detection "
+        error(EXIT_FAILURE, 0, "%s (hdu: %s) has a '%s' type. The detection "
               "(labeled) map must have an integer type (labels/classes can "
               "only be integers). If the pixel values are integers, but only "
               "the numerical type of the image is floating-point, you can "
@@ -643,7 +643,7 @@ ui_prepare_tiles(struct segmentparams *p)
       gal_fits_img_write(check, tl->tilecheckname, NULL, PROGRAM_NAME);
       gal_data_free(check);
 
-      /* If `continueaftercheck' hasn't been called, abort NoiseChisel. */
+      /* If 'continueaftercheck' hasn't been called, abort NoiseChisel. */
       if(!p->continueaftercheck)
         ui_abort_after_check(p, tl->tilecheckname, NULL,
                              "showing all tiles over the image");
@@ -665,15 +665,15 @@ ui_check_size(gal_data_t *base, gal_data_t *comp, size_t 
numtiles,
   if( gal_dimension_is_different(base, comp) && numtiles!=comp->size )
     error(EXIT_FAILURE, 0, "%s (hdu: %s): doesn't have the right size "
           "(%zu elements or pixels).\n\n"
-          "It must either be the same size as `%s' (hdu: `%s'), or "
+          "It must either be the same size as '%s' (hdu: '%s'), or "
           "it must have the same number of elements as the total "
           "number of tiles in the tessellation (%zu). In the latter "
           "case, each pixel is assumed to be a fixed value for a "
           "complete tile.\n\n"
-          "Run with `-P' to see the (tessellation) options/settings "
+          "Run with '-P' to see the (tessellation) options/settings "
           "and their values). For more information on tessellation in "
           "Gnuastro, please run the following command (use the arrow "
-          "keys for up and down and press `q' to return to the "
+          "keys for up and down and press 'q' to return to the "
           "command-line):\n\n"
           "    $ info gnuastro tessellation",
           cname, chdu, comp->size, bname, bhdu, numtiles);
@@ -683,7 +683,7 @@ ui_check_size(gal_data_t *base, gal_data_t *comp, size_t 
numtiles,
 
 
 
-/* Subtract `sky' from the input dataset depending on its size (it may be
+/* Subtract 'sky' from the input dataset depending on its size (it may be
    the whole array or a tile-values array).. */
 static void
 ui_subtract_sky(gal_data_t *in, gal_data_t *sky,
@@ -728,7 +728,7 @@ ui_subtract_sky(gal_data_t *in, gal_data_t *sky,
 
 
 
-/* The Sky and Sky standard deviation images can be a `oneelempertile'
+/* The Sky and Sky standard deviation images can be a 'oneelempertile'
    image (only one element/pixel for a tile). So we need to do some extra
    checks on them (after reading the tessellation). */
 static float
@@ -741,12 +741,12 @@ ui_read_std_and_sky(struct segmentparams *p)
   gal_data_t *sky, *keys=gal_data_array_calloc(3);
 
   /* See if the name used for the standard deviation is a filename or a
-     value. When the string is only a number (and nothing else), `tailptr'
-     will point to the end of the string (`\0'). When the string doesn't
+     value. When the string is only a number (and nothing else), 'tailptr'
+     will point to the end of the string ('\0'). When the string doesn't
      start with a number, it will point to the start of the
-     string. However, file names might also be things like `1_std.fits'. In
-     such cases, `strtod' will return `1.0' and `tailptr' will be
-     `_std.fits'. Thus the most robust test is to see if `tailptr' is the
+     string. However, file names might also be things like '1_std.fits'. In
+     such cases, 'strtod' will return '1.0' and 'tailptr' will be
+     '_std.fits'. Thus the most robust test is to see if 'tailptr' is the
      NULL string character. */
   tmpval=strtod(p->usedstdname, &tailptr);
   if(*tailptr=='\0')
@@ -760,11 +760,11 @@ ui_read_std_and_sky(struct segmentparams *p)
     {
       /* Make sure a HDU is also given. */
       if(p->stdhdu==NULL)
-        error(EXIT_FAILURE, 0, "no value given to `--stdhdu'.\n\n"
+        error(EXIT_FAILURE, 0, "no value given to '--stdhdu'.\n\n"
               "When the Sky standard deviation is a dataset, it is mandatory "
               "specify which HDU/extension it is present in. The file can "
-              "be specified explicitly with `--std'. If not, segment will "
-              "use the file given to `--detection'. If that is also not "
+              "be specified explicitly with '--std'. If not, segment will "
+              "use the file given to '--detection'. If that is also not "
               "called, it will look into the main input file (with no "
               "option)");
 
@@ -782,12 +782,12 @@ ui_read_std_and_sky(struct segmentparams *p)
 
   /* When the Standard deviation dataset (not single value) is made by
      NoiseChisel, it puts three basic statistics of the pre-interpolation
-     distribution of standard deviations in `MEDSTD', `MINSTD' and
-     `MAXSTD'. The `MEDSTD' in particular is most important because it
+     distribution of standard deviations in 'MEDSTD', 'MINSTD' and
+     'MAXSTD'. The 'MEDSTD' in particular is most important because it
      can't be inferred after the interpolations and it can be useful in
      MakeCatalog later to give a more accurate estimate of the noise
      level. So if they are present, we will read them here and write them
-     to the STD output (which is created when `--rawoutput' is not
+     to the STD output (which is created when '--rawoutput' is not
      given). */
   if(!p->rawoutput && p->std->size>1)
     {
@@ -807,7 +807,7 @@ ui_read_std_and_sky(struct segmentparams *p)
       gal_data_array_free(keys, 3, 1);
     }
 
-  /* Similar to `--std' above. */
+  /* Similar to '--std' above. */
   if(p->skyname)
     {
       tmpval=strtod(p->skyname, &tailptr);
@@ -821,11 +821,11 @@ ui_read_std_and_sky(struct segmentparams *p)
         {
           /* Make sure a HDU is also given. */
           if(p->skyhdu==NULL)
-            error(EXIT_FAILURE, 0, "no value given to `--skyhdu'.\n\n"
+            error(EXIT_FAILURE, 0, "no value given to '--skyhdu'.\n\n"
                   "When the Sky is a dataset, it is mandatory specify "
                   "which HDU/extension it is present in. The file can be "
-                  "specified explicitly with `--sky'. If it is a single "
-                  "value, you can just pass the value to `--sky' and no "
+                  "specified explicitly with '--sky'. If it is a single "
+                  "value, you can just pass the value to '--sky' and no "
                   "HDU will be necessary");
 
           /* Read the Sky dataset. */
@@ -912,9 +912,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
segmentparams *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
+  /* 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
@@ -1039,12 +1039,12 @@ ui_abort_after_check(struct segmentparams *p, char 
*filename,
 
   if(file2name)
     {
-      if( asprintf(&name, "`%s' and `%s'", filename, file2name)<0 )
+      if( asprintf(&name, "'%s' and '%s'", filename, file2name)<0 )
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
     }
   else
     {
-      if( asprintf(&name, "`%s'", filename)<0 )
+      if( asprintf(&name, "'%s'", filename)<0 )
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
     }
 
@@ -1056,7 +1056,7 @@ ui_abort_after_check(struct segmentparams *p, char 
*filename,
           "%s (%s) has been created.\n\n"
           "If you want %s to continue its processing AND save any "
           "requested check outputs, please run it again with "
-          "`--continueaftercheck'.\n"
+          "'--continueaftercheck'.\n"
           "------------------------------------------------\n",
           PROGRAM_NAME, name, description, PROGRAM_NAME);
 
diff --git a/bin/segment/ui.h b/bin/segment/ui.h
index 5badbe2..a7de4ec 100644
--- a/bin/segment/ui.h
+++ b/bin/segment/ui.h
@@ -5,7 +5,7 @@ Segment is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
diff --git a/bin/statistics/Makefile.am b/bin/statistics/Makefile.am
index 540757c..5c1130a 100644
--- a/bin/statistics/Makefile.am
+++ b/bin/statistics/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = aststatistics
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
 aststatistics_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
-                      -lgnuastro $(MAYBE_NORPATH)
+                      -lgnuastro $(CONFIG_LDADD)
 
 aststatistics_SOURCES = main.c ui.c contour.c sky.c statistics.c
 
diff --git a/bin/statistics/args.h b/bin/statistics/args.h
index 9ee8852..2abc538 100644
--- a/bin/statistics/args.h
+++ b/bin/statistics/args.h
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -36,10 +36,10 @@ struct argp_option program_options[] =
       UI_KEY_COLUMN,
       "STR",
       0,
-      "Column name or number if input is a table.",
+      "Column number (counting from 1) or search string.",
       GAL_OPTIONS_GROUP_INPUT,
-      &p->column,
-      GAL_TYPE_STRING,
+      &p->columns,
+      GAL_TYPE_STRLL,
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
@@ -100,6 +100,8 @@ struct argp_option program_options[] =
 
 
 
+
+
     /* Tessellation */
     {
       "interpolate",
@@ -424,6 +426,19 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET
     },
     {
+      "histogram2d",
+      UI_KEY_HISTOGRAM2D,
+      "STR",
+      0,
+      "2D histogram (as 'table' or 'image').",
+      UI_GROUP_PARTICULAR_STAT,
+      &p->histogram2d,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
       "mirror",
       UI_KEY_MIRROR,
       "FLT",
@@ -467,7 +482,7 @@ struct argp_option program_options[] =
       UI_KEY_SIGMACLIP,
       0,
       0,
-      "Overall sigma-clipping (see `--sclipparams')",
+      "Overall sigma-clipping (see '--sclipparams')",
       UI_GROUP_PARTICULAR_STAT,
       &p->sigmaclip,
       GAL_OPTIONS_NO_ARG_TYPE,
@@ -503,7 +518,7 @@ struct argp_option program_options[] =
     {
       "kernel",
       UI_KEY_KERNEL,
-      "STR",
+      "FITS",
       0,
       "File name of kernel to convolve input.",
       UI_GROUP_SKY,
@@ -611,7 +626,7 @@ struct argp_option program_options[] =
       UI_KEY_CHECKSKY,
       0,
       0,
-      "Store steps in `_sky_steps.fits' file.",
+      "Store steps in '_sky_steps.fits' file.",
       UI_GROUP_SKY,
       &p->checksky,
       GAL_OPTIONS_NO_ARG_TYPE,
@@ -655,6 +670,19 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_SET
     },
     {
+      "numbins2",
+      UI_KEY_NUMBINS2,
+      "INT",
+      0,
+      "No. of bins in second-dim of 2D histogram.",
+      UI_GROUP_HIST_CFP,
+      &p->numbins2,
+      GAL_TYPE_SIZE_T,
+      GAL_OPTIONS_RANGE_GT_0,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
       "numasciibins",
       UI_KEY_NUMASCIIBINS,
       "INT",
@@ -732,6 +760,45 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "greaterequal2",
+      UI_KEY_GREATEREQUAL2,
+      "FLT",
+      0,
+      "Upper range of 2nd dim in 2D histograms.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->greaterequal2,
+      GAL_TYPE_FLOAT32,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "lessthan2",
+      UI_KEY_LESSTHAN2,
+      "FLT",
+      0,
+      "Lower range of 2nd dim in 2D histograms.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->lessthan2,
+      GAL_TYPE_FLOAT32,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "onebinstart2",
+      UI_KEY_ONEBINSTART2,
+      "FLT",
+      0,
+      "Similar to --onebinstart, for 2D histogram",
+      UI_GROUP_HIST_CFP,
+      &p->onebinstart2,
+      GAL_TYPE_FLOAT32,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
 
 
     {0}
diff --git a/bin/statistics/aststatistics.conf 
b/bin/statistics/aststatistics.conf
index 8257fef..3a341e3 100644
--- a/bin/statistics/aststatistics.conf
+++ b/bin/statistics/aststatistics.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info aststatistics                  # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
@@ -23,8 +23,8 @@
 
 # Sky and its STD settings
  khdu                 1
- meanmedqdiff     0.005
- outliersigma        10
+ meanmedqdiff      0.01
+ outliersigma         5
  outliersclip     3,0.2
  smoothwidth          3
  sclipparams      3,0.1
@@ -33,4 +33,5 @@
  numasciibins        70
  asciiheight         10
  numbins            100
+ numbins2           100
  mirrordist         1.5
diff --git a/bin/statistics/authors-cite.h b/bin/statistics/authors-cite.h
index 15c0bf3..a5b46b2 100644
--- a/bin/statistics/authors-cite.h
+++ b/bin/statistics/authors-cite.h
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/statistics/contour.c b/bin/statistics/contour.c
index ffdb2e0..991799e 100644
--- a/bin/statistics/contour.c
+++ b/bin/statistics/contour.c
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2019, Free Software Foundation, Inc.
+Copyright (C) 2019-2021, 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
diff --git a/bin/statistics/contour.h b/bin/statistics/contour.h
index 4d59c86..d7540ba 100644
--- a/bin/statistics/contour.h
+++ b/bin/statistics/contour.h
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2019, Free Software Foundation, Inc.
+Copyright (C) 2019-2021, 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
diff --git a/bin/statistics/main.c b/bin/statistics/main.c
index ec27665..6e626a8 100644
--- a/bin/statistics/main.c
+++ b/bin/statistics/main.c
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/statistics/main.h b/bin/statistics/main.h
index 7455dc8..374d687 100644
--- a/bin/statistics/main.h
+++ b/bin/statistics/main.h
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -58,10 +58,12 @@ struct statisticsparams
   gal_list_i32_t         *singlevalue; /* Single value calculations.     */
   gal_list_f64_t  *tp_args;  /* Arguments for printing.                  */
   char          *inputname;  /* Input filename.                          */
-  char             *column;  /* Column name or number if input is table. */
+  gal_list_str_t  *columns;  /* Column name or number if input is table. */
   char             *refcol;  /* Reference column name or number.         */
   float       greaterequal;  /* Only use values >= this value.           */
+  float      greaterequal2;  /* Only use values >= this value (2D hist). */
   float           lessthan;  /* Only use values <  this value.           */
+  float          lessthan2;  /* Only use values <  this value (2D hist). */
   float           quantmin;  /* Quantile min or range: from Q to 1-Q.    */
   float           quantmax;  /* Quantile maximum.                        */
   uint8_t           ontile;  /* Do single value calculations on tiles.   */
@@ -70,6 +72,7 @@ struct statisticsparams
   uint8_t        asciihist;  /* Print an ASCII histogram.                */
   uint8_t         asciicfp;  /* Print an ASCII cumulative frequency plot.*/
   uint8_t        histogram;  /* Save histogram in output.                */
+  char        *histogram2d;  /* Save 2D-histogram as image or table.     */
   uint8_t       cumulative;  /* Save cumulative distibution in output.   */
   double            mirror;  /* Mirror value for hist and CFP.           */
   uint8_t              sky;  /* Find the Sky value over the image.       */
@@ -77,11 +80,13 @@ struct statisticsparams
   gal_data_t      *contour;  /* Levels to show contours.                 */
 
   size_t           numbins;  /* Number of bins in histogram or CFP.      */
+  size_t          numbins2;  /* No. of second-dim bins in 2D histogram.  */
   size_t      numasciibins;  /* Number of bins in ASCII plots.           */
   size_t       asciiheight;  /* Height of ASCII histogram or CFP plots.  */
   uint8_t        normalize;  /* set the sum of all bins to 1.            */
   uint8_t   manualbinrange;  /* Set bin min/max manually, not from data. */
   float        onebinstart;  /* Shift bins to start at this value.       */
+  float       onebinstart2;  /* Shift bins to start at this value.       */
   uint8_t        maxbinone;  /* Set the maximum bin to 1.                */
   float         mirrordist;  /* Maximum distance after mirror for mode.  */
 
diff --git a/bin/statistics/sky.c b/bin/statistics/sky.c
index b2ecdd7..951f915 100644
--- a/bin/statistics/sky.c
+++ b/bin/statistics/sky.c
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -85,7 +85,7 @@ sky_on_thread(void *in_prm)
                     ? gal_statistics_quantile_function(tile, mean, 1)
                     : NULL );
 
-      /* Reset the pointers of `tile'. */
+      /* Reset the pointers of 'tile'. */
       if(p->kernel) { tile->array=tarray; tile->block=tblock; }
 
       /* Check the mean quantile value. Note that if the mode is
@@ -94,7 +94,7 @@ sky_on_thread(void *in_prm)
       if( meanquant
           && fabs( *(double *)(meanquant->array)-0.5f) < p->meanmedqdiff )
         {
-          /* Get the sigma-clipped mean and standard deviation. `inplace'
+          /* Get the sigma-clipped mean and standard deviation. 'inplace'
              is irrelevant here because this is a tile and it will be
              copied anyway. */
           sigmaclip=gal_statistics_sigma_clip(tile, p->sclipparams[0],
@@ -190,7 +190,8 @@ sky(struct statisticsparams *p)
 
   /* Find the Sky and Sky standard deviation on the tiles. */
   if(!cp->quiet) gettimeofday(&t1, NULL);
-  gal_threads_spin_off(sky_on_thread, p, tl->tottiles, cp->numthreads);
+  gal_threads_spin_off(sky_on_thread, p, tl->tottiles, cp->numthreads,
+                       cp->minmapsize, cp->quietmmap);
   if(!cp->quiet)
     {
       num=gal_statistics_number(p->sky_t);
@@ -211,18 +212,19 @@ sky(struct statisticsparams *p)
 
 
   /* Remove outliers if requested. */
-  if(p->outliersigma!=0.0)
-    gal_tileinternal_no_outlier(p->sky_t, p->std_t, NULL, &p->cp.tl,
-                                p->outliersclip, p->outliersigma,
-                                p->checkskyname);
+  gal_tileinternal_no_outlier_local(p->sky_t, p->std_t, NULL, &cp->tl,
+                                    cp->interpmetric, cp->interpnumngb,
+                                    cp->numthreads, p->outliersclip,
+                                    p->outliersigma, p->checkskyname);
 
 
   /* Interpolate the Sky and its standard deviation. */
   if(!cp->quiet) gettimeofday(&t1, NULL);
   p->sky_t->next=p->std_t;
-  tmp=gal_interpolate_close_neighbors(p->sky_t, tl, cp->interpmetric,
-                                      cp->interpnumngb, cp->numthreads,
-                                      cp->interponlyblank, 1);
+  tmp=gal_interpolate_neighbors(p->sky_t, tl, cp->interpmetric,
+                                cp->interpnumngb, cp->numthreads,
+                                cp->interponlyblank, 1,
+                                GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN);
   gal_data_free(p->sky_t);
   gal_data_free(p->std_t);
   p->sky_t=tmp;
@@ -261,15 +263,15 @@ sky(struct statisticsparams *p)
           gal_tile_full_values_write(p->std_t, tl, !p->ignoreblankintiles,
                                      p->checkskyname, NULL, PROGRAM_NAME);
           if(!cp->quiet)
-            printf("  - Check image written to `%s'.\n", p->checkskyname);
+            printf("  - Check image written to '%s'.\n", p->checkskyname);
         }
     }
 
 
   /* Save the Sky and its standard deviation. We want the output to have a
-     `_sky.fits' suffix. So we'll temporarily re-set `p->cp.keepinputdir'
+     '_sky.fits' suffix. So we'll temporarily re-set 'p->cp.keepinputdir'
      if the user asked for a specific name. Note that we copied the actual
-     value in the `keepinputdir' above (in the definition). */
+     value in the 'keepinputdir' above (in the definition). */
   p->cp.keepinputdir = p->cp.output ? 1 : keepinputdir;
   outname=gal_checkset_automatic_output(&p->cp,
                                         ( p->cp.output
@@ -287,7 +289,7 @@ sky(struct statisticsparams *p)
   gal_fits_key_write_config(&p->cp.okeys, "Statistics configuration",
                             "STATISTICS-CONFIG", outname, "0");
   if(!cp->quiet)
-    printf("  - Sky and its STD written to `%s'.\n", outname);
+    printf("  - Sky and its STD written to '%s'.\n", outname);
 
 
   /* Clean up and return. */
diff --git a/bin/statistics/sky.h b/bin/statistics/sky.h
index 18b4409..f148448 100644
--- a/bin/statistics/sky.h
+++ b/bin/statistics/sky.h
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/statistics/statistics.c b/bin/statistics/statistics.c
index 3a36134..48986eb 100644
--- a/bin/statistics/statistics.c
+++ b/bin/statistics/statistics.c
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -30,6 +30,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <string.h>
 #include <stdlib.h>
 
+#include <gnuastro/wcs.h>
 #include <gnuastro/fits.h>
 #include <gnuastro/tile.h>
 #include <gnuastro/blank.h>
@@ -106,8 +107,8 @@ statistics_print_one_row(struct statisticsparams *p)
   for(tmp=p->singlevalue; tmp!=NULL; tmp=tmp->next)
     switch(tmp->v)
       {
-      /* Calculate respective values. Checking with `if(num==NULL)' gives
-         compiler warnings of `this if clause does not guard ...'. So we
+      /* Calculate respective values. Checking with 'if(num==NULL)' gives
+         compiler warnings of 'this if clause does not guard ...'. So we
          are using this empty-if and else statement. */
       case UI_KEY_NUMBER:
         num = num ? num : gal_statistics_number(p->input);           break;
@@ -211,7 +212,7 @@ statistics_print_one_row(struct statisticsparams *p)
 
       /* Print the number. Note that we don't want any extra white space
          characters before or after the printed outputs. So we have defined
-         `counter' to add a single white space character before any element
+         'counter' to add a single white space character before any element
          except the first one. */
       toprint=gal_type_to_string(out->array, out->type, 0);
       printf("%s%s", counter ? " " : "", toprint);
@@ -271,11 +272,12 @@ statistics_interpolate_and_write(struct statisticsparams 
*p,
   if( p->interpolate
       && !(p->cp.interponlyblank && gal_blank_present(values, 1)==0) )
     {
-      interpd=gal_interpolate_close_neighbors(values, &cp->tl,
-                                              cp->interpmetric,
-                                              cp->interpnumngb,
-                                              cp->numthreads,
-                                              cp->interponlyblank, 0);
+      interpd=gal_interpolate_neighbors(values, &cp->tl,
+                                        cp->interpmetric,
+                                        cp->interpnumngb,
+                                        cp->numthreads,
+                                        cp->interponlyblank, 0,
+                                        GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN);
       gal_data_free(values);
       values=interpd;
     }
@@ -415,7 +417,7 @@ statistics_on_tile(struct statisticsparams *p)
                     "recognized", __func__, PACKAGE_BUGREPORT, operation->v);
             }
 
-          /* Put the output value into the `values' array and clean up. */
+          /* Put the output value into the 'values' array and clean up. */
           tmp=gal_data_copy_to_new_type_free(tmp, type);
           memcpy(gal_pointer_increment(values->array, tind++, values->type),
                  tmp->array, gal_type_sizeof(type));
@@ -503,9 +505,9 @@ print_ascii_plot(struct statisticsparams *p, gal_data_t 
*plot,
 
 
 
-/* Data structure that must be fed into `gal_statistics_regular_bins'.*/
+/* Data structure that must be fed into 'gal_statistics_regular_bins'.*/
 static gal_data_t *
-set_bin_range_params(struct statisticsparams *p)
+set_bin_range_params(struct statisticsparams *p, size_t dim)
 {
   size_t rsize=2;
   gal_data_t *range=NULL;
@@ -513,10 +515,23 @@ set_bin_range_params(struct statisticsparams *p)
   if(p->manualbinrange)
     {
       /* Allocate the range data structure. */
-      range=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, 1, &rsize, NULL, 0, -1, 1,
-                           NULL, NULL, NULL);
-      ((float *)(range->array))[0]=p->greaterequal;
-      ((float *)(range->array))[1]=p->lessthan;
+      range=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, 1, &rsize, NULL,
+                           0, -1, 1, NULL, NULL, NULL);
+      switch(dim)
+        {
+        case 1:
+          ((float *)(range->array))[0]=p->greaterequal;
+          ((float *)(range->array))[1]=p->lessthan;
+          break;
+        case 2:
+          ((float *)(range->array))[0]=p->greaterequal2;
+          ((float *)(range->array))[1]=p->lessthan2;
+          break;
+        default:
+          error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+                "address the problem. The value %zu for 'dim' isn't "
+                "recogized", __func__, PACKAGE_BUGREPORT, dim);
+        }
     }
   return range;
 }
@@ -531,7 +546,7 @@ ascii_plots(struct statisticsparams *p)
   gal_data_t *bins, *hist, *cfp=NULL, *range=NULL;
 
   /* Make the bins and the respective plot. */
-  range=set_bin_range_params(p);
+  range=set_bin_range_params(p, 1);
   bins=gal_statistics_regular_bins(p->input, range, p->numasciibins, NAN);
   hist=gal_statistics_histogram(p->input, bins, 0, 0);
   if(p->asciicfp)
@@ -547,6 +562,7 @@ ascii_plots(struct statisticsparams *p)
   /* Clean up.*/
   gal_data_free(bins);
   gal_data_free(hist);
+  gal_data_free(range);
   if(p->asciicfp) gal_data_free(cfp);
 }
 
@@ -572,38 +588,59 @@ ascii_plots(struct statisticsparams *p)
 /*******************************************************************/
 /*******    Histogram and cumulative frequency tables    ***********/
 /*******************************************************************/
-void
-write_output_table(struct statisticsparams *p, gal_data_t *table,
-                   char *suf, char *contents)
+static char *
+statistics_output_name(struct statisticsparams *p, char *suf, int *isfits)
 {
-  char *output;
   int use_auto_output=0;
-  char *fix, *suffix=NULL, *tmp;
-  gal_list_str_t *comments=NULL;
-
+  char *out, *fix, *suffix=NULL;
 
   /* Automatic output should be used when no output name was specified or
      we have more than one output file. */
   use_auto_output = p->cp.output ? (p->numoutfiles>1 ? 1 : 0) : 1;
 
-
-  /* Set the `fix' and `suffix' strings. Note that `fix' is necessary in
+  /* Set the 'fix' and 'suffix' strings. Note that 'fix' is necessary in
      every case, even when no automatic output is to be used. Since it is
      used to determine the format of the output. */
-  fix = ( p->cp.output
-          ? gal_fits_name_is_fits(p->cp.output) ? "fits" : "txt"
-          : "txt" );
+  fix = ( *isfits
+          ? "fits"
+          : ( p->cp.output
+              ? gal_fits_name_is_fits(p->cp.output) ? "fits" : "txt"
+              : "txt" ) );
   if(use_auto_output)
     if( asprintf(&suffix, "%s.%s", suf, fix)<0 )
       error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
 
-
   /* Make the output name. */
-  output = ( use_auto_output
-             ? gal_checkset_automatic_output(&p->cp, p->inputname, suffix)
-             : p->cp.output );
+  out = ( use_auto_output
+          ? gal_checkset_automatic_output(&p->cp, p->inputname, suffix)
+          : p->cp.output );
+
+  /* See if it is a FITS file. */
+  *isfits=strcmp(fix, "fits")==0;
+
+  /* Make sure it doesn't already exist. */
+  gal_checkset_writable_remove(out, 0, p->cp.dontdelete);
+
+  /* Clean up and return. */
+  if(suffix) free(suffix);
+  return out;
+}
+
+
+
 
 
+void
+write_output_table(struct statisticsparams *p, gal_data_t *table,
+                   char *suf, char *contents)
+{
+  int isfits=0;
+  char *tmp, *output;
+  gal_list_str_t *comments=NULL;
+
+  /* Set the output. */
+  output=statistics_output_name(p, suf, &isfits);
+
   /* Write the comments, NOTE: we are writing the first two in reverse of
      the order we want them. They will later be freed as part of the list's
      freeing.*/
@@ -614,17 +651,18 @@ write_output_table(struct statisticsparams *p, gal_data_t 
*table,
     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
   gal_list_str_add(&comments, tmp, 0);
 
-  if(strcmp(fix, "fits"))  /* The intro info will be in FITS files anyway.*/
-    gal_table_comments_add_intro(&comments, PROGRAM_NAME, &p->rawtime);
+  if(!isfits)  /* The intro info will be in FITS files anyway.*/
+    gal_table_comments_add_intro(&comments, PROGRAM_STRING, &p->rawtime);
 
 
   /* Write the table. */
   gal_checkset_writable_remove(output, 0, p->cp.dontdelete);
-  gal_table_write(table, comments, p->cp.tableformat, output, "TABLE", 0);
+  gal_table_write(table, NULL, comments, p->cp.tableformat, output,
+                  "TABLE", 0);
 
 
   /* Write the configuration information if we have a FITS output. */
-  if(!strcmp(fix, "fits"))
+  if(isfits)
     {
       gal_fits_key_write_filename("input", p->inputname, &p->cp.okeys, 1);
       gal_fits_key_write_config(&p->cp.okeys, "Statistics configuration",
@@ -638,7 +676,6 @@ write_output_table(struct statisticsparams *p, gal_data_t 
*table,
 
 
   /* Clean up. */
-  if(suffix) free(suffix);
   gal_list_str_free(comments, 1);
   if(output!=p->cp.output) free(output);
 }
@@ -656,7 +693,7 @@ save_hist_and_or_cfp(struct statisticsparams *p)
   /* Set the bins and make the histogram, this is necessary for both the
      histogram and CFP (recall that the CFP is built from the
      histogram). */
-  range=set_bin_range_params(p);
+  range=set_bin_range_params(p, 1);
   bins=gal_statistics_regular_bins(p->input, range, p->numbins,
                                    p->onebinstart);
   hist=gal_statistics_histogram(p->input, bins, p->normalize, p->maxbinone);
@@ -665,20 +702,20 @@ save_hist_and_or_cfp(struct statisticsparams *p)
   /* Set the histogram as the next pointer of bins. This is again necessary
      in both cases: when only a histogram is requested, it is used for the
      plotting. When only a CFP is desired, it is used as input into
-     `gal_statistics_cfp'. */
+     'gal_statistics_cfp'. */
   bins->next=hist;
 
 
   /* Make the cumulative frequency plot if the user wanted it. Make the
-     CFP, note that for the CFP, `maxbinone' and `normalize' are the same:
+     CFP, note that for the CFP, 'maxbinone' and 'normalize' are the same:
      the last bin (largest value) must be one. So if any of them are given,
      then set the last argument to 1.*/
   if(p->cumulative)
     cfp=gal_statistics_cfp(p->input, bins, p->normalize || p->maxbinone);
 
 
-  /* FITS tables don't accept `uint64_t', so to be consistent, we'll conver
-     the histogram and CFP to `uint32_t'.*/
+  /* FITS tables don't accept 'uint64_t', so to be consistent, we'll conver
+     the histogram and CFP to 'uint32_t'.*/
   if(hist->type==GAL_TYPE_UINT64)
     hist=gal_data_copy_to_new_type_free(hist, GAL_TYPE_UINT32);
   if(cfp && cfp->type==GAL_TYPE_UINT64)
@@ -692,11 +729,11 @@ save_hist_and_or_cfp(struct statisticsparams *p)
 
   /* Prepare the contents. */
   if(p->histogram && p->cumulative)
-    { suf="_hist_cfp"; contents="Histogram and cumulative frequency plot"; }
+    { suf="-hist-cfp"; contents="Histogram and cumulative frequency plot"; }
   else if(p->histogram)
-    { suf="_hist";     contents="Histogram"; }
+    { suf="-hist";     contents="Histogram"; }
   else
-    { suf="_cfp";      contents="Cumulative frequency plot"; }
+    { suf="-cfp";      contents="Cumulative frequency plot"; }
 
 
   /* Set the output file name. */
@@ -709,6 +746,121 @@ save_hist_and_or_cfp(struct statisticsparams *p)
 
 
 
+/* In the WCS standard, '-' is meaningful, so if a column name contains
+   '-', it should be changed to '_'. */
+static char *
+histogram_2d_set_ctype(char *orig, char *backup)
+{
+  char *c, *out=NULL;
+
+  /* If an original name exists, then check it. Otherwise, just return the
+     backup string so 'CTYPE' isn't left empty. */
+  if(orig)
+    {
+      /* Copy the original name into a newly allocated space because we
+         later want to free it. */
+      gal_checkset_allocate_copy(orig, &out);
+
+      /* Parse the copy and if a dash is present, correct it. */
+      for(c=out; *c!='\0'; ++c) if(*c=='-') *c='_';
+    }
+  else
+    gal_checkset_allocate_copy(backup, &out);
+
+  /* Return the final string. */
+  return out;
+}
+
+
+
+
+
+static void
+histogram_2d(struct statisticsparams *p)
+{
+  int isfits=1;
+  int32_t *imgarr;
+  double *d1, *d2;
+  uint32_t *histarr;
+  gal_data_t *range1, *range2;
+  gal_data_t *img, *hist2d, *bins;
+  size_t i, j, dsize[2], nb1=p->numbins, nb2=p->numbins2;
+  char *output, suf[]="-hist2d", contents[]="2D Histogram";
+
+  /* WCS-related arrays. */
+  char *cunit[2], *ctype[2];
+  double crpix[2], crval[2], cdelt[2], pc[4]={1,0,0,1};
+
+  /* Set the bins for each dimension */
+  range1=set_bin_range_params(p, 1);
+  range2=set_bin_range_params(p, 2);
+  bins=gal_statistics_regular_bins(p->input, range1, nb1,
+                                   p->onebinstart);
+  bins->next=gal_statistics_regular_bins(p->input->next, range2,
+                                         nb2, p->onebinstart2);
+
+  /* Build the 2D histogram. */
+  hist2d=gal_statistics_histogram2d(p->input, bins);
+
+  /* Write the histogram into a 2D FITS image. Note that in the FITS image
+     standard, the first axis is the fastest array (unlike the default
+     format we have adopted for the tables). */
+  if(!strcmp(p->histogram2d,"image"))
+    {
+      /* Allocate the 2D image array. Note that 'dsize' is in the order of
+         C, where the first dimension is the slowest. However, in FITS the
+         fastest dimension is the first. */
+      dsize[0]=nb2;
+      dsize[1]=nb1;
+      histarr=hist2d->next->next->array;
+      img=gal_data_alloc(NULL, GAL_TYPE_INT32, 2, dsize, NULL, 0,
+                         p->cp.minmapsize, p->cp.quietmmap,
+                         NULL, NULL, NULL);
+
+      /* Fill the array values. */
+      imgarr=img->array;
+      for(i=0;i<nb2;++i)
+        for(j=0;j<nb1;++j)
+          imgarr[i*nb1+j]=histarr[j*nb2+i];
+
+      /* Set the WCS. */
+      d1=bins->array;
+      d2=bins->next->array;
+      crpix[0] = 1;                   crpix[1] = 1;
+      crval[0] = d1[0];               crval[1] = d2[0];
+      cdelt[0] = d1[1]-d1[0];         cdelt[1] = d2[1]-d2[0];
+      cunit[0] = p->input->unit;      cunit[1] = p->input->next->unit;
+      ctype[0] = histogram_2d_set_ctype(p->input->name, "X");
+      ctype[1] = histogram_2d_set_ctype(p->input->next->name, "Y");
+      img->wcs=gal_wcs_create(crpix, crval, cdelt, pc, cunit, ctype, 2);
+
+      /* Write the output. */
+      output=statistics_output_name(p, suf, &isfits);
+      gal_fits_img_write(img, output, NULL, PROGRAM_STRING);
+      gal_fits_key_write_filename("input", p->inputname, &p->cp.okeys, 1);
+      gal_fits_key_write_config(&p->cp.okeys, "Statistics configuration",
+                                "STATISTICS-CONFIG", output, "0");
+
+      /* Clean up and let the user know that the histogram is built. */
+      free(ctype[0]);
+      free(ctype[1]);
+      gal_data_free(img);
+      if(!p->cp.quiet) printf("%s created.\n", output);
+    }
+  /* Write 2D histogram as a table. */
+  else
+    write_output_table(p, hist2d, suf, contents);
+
+  /* Clean up. */
+  gal_data_free(range1);
+  gal_data_free(range2);
+  gal_list_data_free(bins);
+  gal_list_data_free(hist2d);
+}
+
+
+
+
 
 void
 print_mirror_hist_cfp(struct statisticsparams *p)
@@ -766,7 +918,7 @@ print_mirror_hist_cfp(struct statisticsparams *p)
 /*******************************************************************/
 /**************           Basic information          ***************/
 /*******************************************************************/
-/* To keep things in `print_basics' clean, we'll define the input data
+/* To keep things in 'print_basics' clean, we'll define the input data
    here, then only print the values there. */
 void
 print_input_info(struct statisticsparams *p)
@@ -784,8 +936,8 @@ print_input_info(struct statisticsparams *p)
   printf("Input: %s\n", name);
 
   /* If a table was given, print the column. */
-  if(p->column) printf("Column: %s\n",
-                       p->input->name ? p->input->name : p->column);
+  if(p->columns) printf("Column: %s\n",
+                        p->input->name ? p->input->name : p->columns->v);
 
   /* Range. */
   str=NULL;
@@ -874,7 +1026,7 @@ print_basics(struct statisticsparams *p)
   tmp=gal_statistics_mode(p->input, mirrdist, 1);
   d=tmp->array;
   if(d[2]>GAL_STATISTICS_MODE_GOOD_SYM)
-    {        /* Same format as `gal_data_write_to_string' */
+    {        /* Same format as 'gal_data_write_to_string' */
       printf("  %-*s %.10g\n", namewidth, "Mode:", d[0]);
       printf("  %-*s %.10g\n", namewidth, "Mode quantile:", d[1]);
     }
@@ -888,7 +1040,7 @@ print_basics(struct statisticsparams *p)
   free(str);
 
   /* Print the mean and standard deviation. Same format as
-     `gal_data_write_to_string' */
+     'gal_data_write_to_string' */
   printf("  %-*s %.10g\n", namewidth, "Mean:", mean);
   printf("  %-*s %.10g\n", namewidth, "Standard deviation:", std);
 
@@ -898,7 +1050,7 @@ print_basics(struct statisticsparams *p)
      range of the histogram. In that case, we want to print the histogram
      information. */
   printf("-------");
-  range=set_bin_range_params(p);
+  range=set_bin_range_params(p, 1);
   p->asciiheight = p->asciiheight ? p->asciiheight : 10;
   p->numasciibins = p->numasciibins ? p->numasciibins : 70;
   bins=gal_statistics_regular_bins(p->input, range, p->numasciibins, NAN);
@@ -1045,6 +1197,13 @@ statistics(struct statisticsparams *p)
       save_hist_and_or_cfp(p);
     }
 
+  /* 2D histogram. */
+  if(p->histogram2d)
+    {
+      print_basic_info=0;
+      histogram_2d(p);
+    }
+
   /* Print the sigma-clipped results. */
   if( p->sigmaclip )
     {
diff --git a/bin/statistics/statistics.h b/bin/statistics/statistics.h
index f92391b..07dd16e 100644
--- a/bin/statistics/statistics.h
+++ b/bin/statistics/statistics.h
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/statistics/ui.c b/bin/statistics/ui.c
index e590b51..0b2ad5c 100644
--- a/bin/statistics/ui.c
+++ b/bin/statistics/ui.c
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -26,6 +26,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <errno.h>
 #include <error.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <gnuastro/txt.h>
 #include <gnuastro/wcs.h>
@@ -127,8 +128,11 @@ ui_initialize_options(struct statisticsparams *p,
 
   /* Program-specific initializers */
   p->lessthan            = NAN;
+  p->lessthan2           = NAN;
   p->onebinstart         = NAN;
+  p->onebinstart2        = NAN;
   p->greaterequal        = NAN;
+  p->greaterequal2       = NAN;
   p->quantmin            = NAN;
   p->quantmax            = NAN;
   p->mirror              = NAN;
@@ -164,18 +168,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct statisticsparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -216,25 +220,25 @@ ui_add_to_single_value(struct argp_option *option, char 
*arg,
   /* In case of printing the option values. */
   if(lineno==-1)
     error(EXIT_FAILURE, 0, "currently the options to be printed in one row "
-          "(like `--number', `--mean', and etc) do not support printing "
-          "with the `--printparams' (`-P'), or writing into configuration "
+          "(like '--number', '--mean', and etc) do not support printing "
+          "with the '--printparams' ('-P'), or writing into configuration "
           "files due to lack of time when implementing these features. "
           "You can put them into configuration files manually. Please get "
-          "in touch with us at `%s', so we can implement it",
+          "in touch with us at '%s', so we can implement it",
           PACKAGE_BUGREPORT);
 
   /* Some of these options take values and some don't. */
   if(option->type==GAL_OPTIONS_NO_ARG_TYPE)
     {
-      /* If this option is given in a configuration file, then `arg' will not
-         be NULL and we don't want to do anything if it is `0'. */
+      /* If this option is given in a configuration file, then 'arg' will not
+         be NULL and we don't want to do anything if it is '0'. */
       if(arg)
         {
-          /* Make sure the value is only `0' or `1'. */
+          /* Make sure the value is only '0' or '1'. */
           if( arg[1]!='\0' && *arg!='0' && *arg!='1' )
-            error_at_line(EXIT_FAILURE, 0, filename, lineno, "the `--%s' "
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "the '--%s' "
                           "option takes no arguments. In a configuration "
-                          "file it can only have the values `1' or `0', "
+                          "file it can only have the values '1' or '0', "
                           "indicating if it should be used or not",
                           option->name);
 
@@ -250,7 +254,7 @@ ui_add_to_single_value(struct argp_option *option, char 
*arg,
       /* Read the string of numbers. */
       inputs=gal_options_parse_list_of_numbers(arg, filename, lineno);
       if(inputs->size==0)
-        error(EXIT_FAILURE, 0, "`--%s' needs a value", option->name);
+        error(EXIT_FAILURE, 0, "'--%s' needs a value", option->name);
 
       /* Do the appropriate operations with the  */
       d=inputs->array;
@@ -265,8 +269,8 @@ ui_add_to_single_value(struct argp_option *option, char 
*arg,
             {
               if(option->key==UI_KEY_QUANTILE && (d[i]<0 || d[i]>1) )
                 error_at_line(EXIT_FAILURE, 0, filename, lineno, "values "
-                              "to `--quantile' (`-u') must be between 0 "
-                              "and 1, you had asked for %g (read from `%s')",
+                              "to '--quantile' ('-u') must be between 0 "
+                              "and 1, you had asked for %g (read from '%s')",
                               d[i], arg);
               gal_list_f64_add(&p->tp_args, d[i]);
               gal_list_i32_add(&p->singlevalue, option->key);
@@ -276,7 +280,7 @@ ui_add_to_single_value(struct argp_option *option, char 
*arg,
         default:
           error_at_line(EXIT_FAILURE, 0, filename, lineno, "a bug! please "
                         "contact us at %s so we can address the problem. "
-                        "the option given to `ui_add_to_print_in_row' is "
+                        "the option given to 'ui_add_to_print_in_row' is "
                         "marked as requiring a value, but is not recognized",
                         PACKAGE_BUGREPORT);
         }
@@ -297,7 +301,7 @@ ui_read_quantile_range(struct argp_option *option, char 
*arg,
   gal_data_t *in;
   struct statisticsparams *p=(struct statisticsparams *)params;
 
-  /* For the `--printparams' (`-P') option:*/
+  /* For the '--printparams' ('-P') option:*/
   if(lineno==-1)
     {
       if( isnan(p->quantmax) )
@@ -318,11 +322,11 @@ ui_read_quantile_range(struct argp_option *option, char 
*arg,
 
   /* Check if there was only two numbers. */
   if(in->size!=1 && in->size!=2)
-    error_at_line(EXIT_FAILURE, 0, filename, lineno, "the `--%s' "
+    error_at_line(EXIT_FAILURE, 0, filename, lineno, "the '--%s' "
                   "option takes one or two values values (separated by "
                   "a comma) to define the range of used values with "
                   "quantiles. However, %zu numbers were read in the "
-                  "string `%s' (value to this option).\n\n"
+                  "string '%s' (value to this option).\n\n"
                   "If there is only one number as input, it will be "
                   "interpretted as the lower quantile (Q) range. The "
                   "higher range will be set to the quantile (1-Q). "
@@ -338,13 +342,13 @@ ui_read_quantile_range(struct argp_option *option, char 
*arg,
   if( (p->quantmin<0 || p->quantmin>1)
       || ( !isnan(p->quantmax) && (p->quantmax<0 || p->quantmax>1) ) )
     error_at_line(EXIT_FAILURE, 0, filename, lineno, "values to the "
-                  "`--quantrange' option must be between 0 and 1 "
-                  "(inclusive). Your input was: `%s'", arg);
+                  "'--quantrange' option must be between 0 and 1 "
+                  "(inclusive). Your input was: '%s'", arg);
 
   /* When only one value is given, make sure it is less than 0.5. */
   if( !isnan(p->quantmax) && p->quantmin>0.5 )
     error(EXIT_FAILURE, 0, "%g>=0.5! When only one value is given to the "
-          "`--%s' option, the range is defined as Q and 1-Q. Thus, the "
+          "'--%s' option, the range is defined as Q and 1-Q. Thus, the "
           "value must be less than 0.5", p->quantmin, option->name);
 
   /* Clean up and return. */
@@ -375,7 +379,7 @@ ui_read_quantile_range(struct argp_option *option, char 
*arg,
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct statisticsparams *p)
 {
@@ -390,27 +394,27 @@ ui_read_check_only_options(struct statisticsparams *p)
   /* If in tile-mode, we must have at least one single valued option. */
   if(p->ontile && p->singlevalue==NULL)
     error(EXIT_FAILURE, 0, "at least one of the single-value measurements "
-          "(for example `--median') must be requested with the `--ontile' "
+          "(for example '--median') must be requested with the '--ontile' "
           "option: there is no value to put in each tile");
 
   /* Tessellation related options. */
   if( p->ontile || p->sky )
     {
       /* The tile or sky mode cannot be called with any other modes. */
-      if(p->asciihist || p->asciicfp || p->histogram || p->cumulative
-         || p->sigmaclip || !isnan(p->mirror) )
-        error(EXIT_FAILURE, 0, "`--ontile' or `--sky' cannot be called with "
-              "any of the `particular' calculation options, for example "
-              "`--histogram'. This is because the latter work over the whole "
+      if( p->asciihist || p->asciicfp || p->histogram || p->histogram2d
+          || p->cumulative || p->sigmaclip || !isnan(p->mirror) )
+        error(EXIT_FAILURE, 0, "'--ontile' or '--sky' cannot be called with "
+              "any of the 'particular' calculation options, for example "
+              "'--histogram'. This is because the latter work over the whole "
               "dataset and element positions are changed, but in the former "
               "positions are significant");
 
       /* Make sure the tessellation defining options are given. */
       if( tl->tilesize==NULL || tl->numchannels==NULL
           || isnan(tl->remainderfrac) )
-         error(EXIT_FAILURE, 0, "`--tilesize', `--numchannels', and "
-               "`--remainderfrac' are mandatory options when dealing with "
-               "a tessellation (in `--ontile' or `--sky' mode). Atleast "
+         error(EXIT_FAILURE, 0, "'--tilesize', '--numchannels', and "
+               "'--remainderfrac' are mandatory options when dealing with "
+               "a tessellation (in '--ontile' or '--sky' mode). Atleast "
                "one of these options wasn't given a value.");
     }
 
@@ -421,13 +425,13 @@ ui_read_check_only_options(struct statisticsparams *p)
       /* Mandatory options. */
       if( isnan(p->meanmedqdiff) || isnan(p->sclipparams[0])
           || p->cp.interpmetric==0 || p->cp.interpnumngb==0 )
-        error(EXIT_FAILURE, 0, "`--meanmedqdiff', `--sclipparams', "
-              "`--interpmetric' and `--interpnumngb' are mandatory when "
-              "requesting Sky measurement (`--sky')");
+        error(EXIT_FAILURE, 0, "'--meanmedqdiff', '--sclipparams', "
+              "'--interpmetric' and '--interpnumngb' are mandatory when "
+              "requesting Sky measurement ('--sky')");
 
       /* If mode and median distance is a reasonable value. */
       if(p->meanmedqdiff>0.5)
-        error(EXIT_FAILURE, 0, "%f not acceptable for `--meanmedqdiff'. It "
+        error(EXIT_FAILURE, 0, "%f not acceptable for '--meanmedqdiff'. It "
               "cannot take values larger than 0.5 (quantile of median)",
               p->meanmedqdiff);
 
@@ -435,21 +439,21 @@ ui_read_check_only_options(struct statisticsparams *p)
       if(p->kernelname && gal_fits_name_is_fits(p->kernelname)
          && p->khdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified for the kernel image. When "
-              "A HDU is necessary for FITS files. You can use the `--khdu' "
-              "(`-u') option and give it the HDU number (starting from "
+              "A HDU is necessary for FITS files. You can use the '--khdu' "
+              "('-u') option and give it the HDU number (starting from "
               "zero), extension name, or anything acceptable by CFITSIO");
     }
 
 
-  /* Sigma-clipping needs `sclipparams'. */
+  /* Sigma-clipping needs 'sclipparams'. */
   if(p->sigmaclip && isnan(p->sclipparams[0]))
-    error(EXIT_FAILURE, 0, "`--sclipparams' is necessary with `--sigmaclip'. "
-          "`--sclipparams' takes two values (separated by a comma) for "
+    error(EXIT_FAILURE, 0, "'--sclipparams' is necessary with '--sigmaclip'. "
+          "'--sclipparams' takes two values (separated by a comma) for "
           "defining the sigma-clip: the multiple of sigma, and tolerance "
           "(<1) or number of clips (>1).");
 
 
-  /* If any of the mode measurements are requested, then `mirrordist' is
+  /* If any of the mode measurements are requested, then 'mirrordist' is
      mandatory. */
   for(tmp=p->singlevalue; tmp!=NULL; tmp=tmp->next)
     switch(tmp->v)
@@ -459,18 +463,18 @@ ui_read_check_only_options(struct statisticsparams *p)
       case UI_KEY_MODEQUANT:
       case UI_KEY_MODESYMVALUE:
         if( isnan(p->mirrordist) )
-          error(EXIT_FAILURE, 0, "`--mirrordist' is required for the "
-                "mode-related single measurements (`--mode', `--modequant', "
-                "`--modesym', and `--modesymvalue')");
+          error(EXIT_FAILURE, 0, "'--mirrordist' is required for the "
+                "mode-related single measurements ('--mode', '--modequant', "
+                "'--modesym', and '--modesymvalue')");
         break;
       case UI_KEY_SIGCLIPSTD:
       case UI_KEY_SIGCLIPMEAN:
       case UI_KEY_SIGCLIPNUMBER:
       case UI_KEY_SIGCLIPMEDIAN:
         if( isnan(p->sclipparams[0]) )
-          error(EXIT_FAILURE, 0, "`--sclipparams' is necessary with "
+          error(EXIT_FAILURE, 0, "'--sclipparams' is necessary with "
                 "sigma-clipping measurements.\n\n"
-                "`--sclipparams' takes two values (separated by a comma) for "
+                "'--sclipparams' takes two values (separated by a comma) for "
                 "defining the sigma-clip: the multiple of sigma, and tolerance 
"
                 "(<1) or number of clips (>1).");
         break;
@@ -481,8 +485,8 @@ ui_read_check_only_options(struct statisticsparams *p)
      to greater than is smaller than the value to less-than. */
   if( !isnan(p->lessthan) && !isnan(p->greaterequal)
       && p->lessthan < p->greaterequal )
-    error(EXIT_FAILURE, 0, "the value to `--lessthan' (%g) must be larger "
-          "than the value to `--greaterequal' (%g)", p->lessthan,
+    error(EXIT_FAILURE, 0, "the value to '--lessthan' (%g) must be larger "
+          "than the value to '--greaterequal' (%g)", p->lessthan,
           p->greaterequal);
 
 
@@ -490,23 +494,33 @@ ui_read_check_only_options(struct statisticsparams *p)
      quantrange. */
   if( ( !isnan(p->lessthan) || !isnan(p->greaterequal) )
       && !isnan(p->quantmin) )
-    error(EXIT_FAILURE, 0, "`--lessthan' and/or `--greaterequal' cannot "
-          "be called together with `--quantrange'");
+    error(EXIT_FAILURE, 0, "'--lessthan' and/or '--greaterequal' cannot "
+          "be called together with '--quantrange'");
 
 
-  /* When binned outputs are requested, make sure that `numbins' is set. */
-  if( (p->histogram || p->cumulative || !isnan(p->mirror)) && p->numbins==0)
-    error(EXIT_FAILURE, 0, "`--numbins' isn't set. When the histogram or "
+  /* When binned outputs are requested, make sure that 'numbins' is set. */
+  if( (p->histogram || p->histogram2d || p->cumulative || !isnan(p->mirror))
+      && p->numbins==0)
+    error(EXIT_FAILURE, 0, "'--numbins' isn't set. When the histogram or "
           "cumulative frequency plots are requested, the number of bins "
-          "(`--numbins') is necessary");
-
+          "('--numbins') is necessary");
+  if( p->histogram2d )
+    {
+      if( p->numbins2==0 )
+        error(EXIT_FAILURE, 0, "'--numbins2' isn't set. When a 2D histogram "
+              "is requested, the number of bins in the second dimension "
+              "('--numbins2') is also necessary");
+      if( strcmp(p->histogram2d,"table") && strcmp(p->histogram2d,"image") )
+        error(EXIT_FAILURE, 0, "the value to '--histogram2d' can either be "
+              "'table' or 'image'");
+    }
 
   /* If an ascii plot is requested, check if the ascii number of bins and
      height are given. */
   if( (p->asciihist || p->asciicfp)
       && (p->numasciibins==0 || p->asciiheight==0) )
     error(EXIT_FAILURE, 0, "when an ascii plot is requested, "
-          "`--numasciibins' and `--asciiheight' are mandatory, but atleast "
+          "'--numasciibins' and '--asciiheight' are mandatory, but atleast "
           "one of these has not been given");
 
 
@@ -532,16 +546,16 @@ ui_check_options_and_arguments(struct statisticsparams *p)
           if( p->cp.hdu==NULL )
             error(EXIT_FAILURE, 0, "no HDU specified. When the input is a "
                   "FITS file, a HDU must also be specified, you can use "
-                  "the `--hdu' (`-h') option and give it the HDU number "
+                  "the '--hdu' ('-h') option and give it the HDU number "
                   "(starting from zero), extension name, or anything "
                   "acceptable by CFITSIO");
 
           /* If its an image, make sure column isn't given (in case the
              user confuses an image with a table). */
           p->hdu_type=gal_fits_hdu_format(p->inputname, p->cp.hdu);
-          if(p->hdu_type==IMAGE_HDU && p->column)
+          if(p->hdu_type==IMAGE_HDU && p->columns)
             error(EXIT_FAILURE, 0, "%s (hdu: %s): is a FITS image "
-                  "extension. The `--column' option is only applicable "
+                  "extension. The '--column' option is only applicable "
                   "to tables.", p->inputname, p->cp.hdu);
         }
     }
@@ -574,18 +588,17 @@ ui_out_of_range_to_blank(struct statisticsparams *p)
 {
   size_t one=1;
   unsigned char flags=GAL_ARITHMETIC_NUMOK;
-  unsigned char flagsor = ( GAL_ARITHMETIC_FREE
-                            | GAL_ARITHMETIC_INPLACE
+  unsigned char flagsor = ( GAL_ARITHMETIC_INPLACE
                             | GAL_ARITHMETIC_NUMOK );
-  gal_data_t *tmp, *cond_g=NULL, *cond_l=NULL, *cond, *blank, *ref;
+  gal_data_t *tmp, *tmp2, *cond_g=NULL, *cond_l=NULL, *cond, *blank, *ref;
 
 
   /* Set the dataset that should be used for the condition. */
   ref = p->reference ? p->reference : p->input;
 
 
-  /* If the user has given a quantile range, then set the `greaterequal'
-     and `lessthan' values. */
+  /* If the user has given a quantile range, then set the 'greaterequal'
+     and 'lessthan' values. */
   if( !isnan(p->quantmin) )
     {
       /* If only one value was given, set the maximum quantile range. */
@@ -603,7 +616,7 @@ ui_out_of_range_to_blank(struct statisticsparams *p)
     }
 
 
-  /* Set the condition. Note that the `greaterequal' name is for the data
+  /* Set the condition. Note that the 'greaterequal' name is for the data
      we want. So we will set the condition based on those that are
      less-than  */
   if(!isnan(p->greaterequal))
@@ -614,9 +627,19 @@ ui_out_of_range_to_blank(struct statisticsparams *p)
       cond_g=gal_arithmetic(GAL_ARITHMETIC_OP_LT, 1, flags, ref, tmp);
       gal_data_free(tmp);
     }
+  if( p->input->next && !isnan(p->greaterequal2) )
+    {
+      tmp=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, 1, &one, NULL, 0, -1, 1,
+                         NULL, NULL, NULL);
+      *((float *)(tmp->array)) = p->greaterequal2;
+      tmp2=gal_arithmetic(GAL_ARITHMETIC_OP_LT, 1, flags, p->input->next, tmp);
+      cond_g=gal_arithmetic(GAL_ARITHMETIC_OP_OR, 1, flagsor, cond_g, tmp2);
+      gal_data_free(tmp);
+      gal_data_free(tmp2);
+    }
 
 
-  /* Same reasoning as above for `p->greaterthan'. */
+  /* Same reasoning as above for 'p->greaterthan'. */
   if(!isnan(p->lessthan))
     {
       tmp=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, 1, &one, NULL, 0, -1, 1,
@@ -625,6 +648,16 @@ ui_out_of_range_to_blank(struct statisticsparams *p)
       cond_l=gal_arithmetic(GAL_ARITHMETIC_OP_GE, 1, flags, ref, tmp);
       gal_data_free(tmp);
     }
+  if(p->input->next && !isnan(p->lessthan2))
+    {
+      tmp=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, 1, &one, NULL, 0, -1, 1,
+                         NULL, NULL, NULL);
+      *((float *)(tmp->array)) = p->lessthan2;
+      tmp2=gal_arithmetic(GAL_ARITHMETIC_OP_GE, 1, flags, p->input->next, tmp);
+      cond_l=gal_arithmetic(GAL_ARITHMETIC_OP_OR, 1, flagsor, cond_l, tmp2);
+      gal_data_free(tmp);
+      gal_data_free(tmp2);
+    }
 
 
   /* Now, set the final condition. If both values were specified, then use
@@ -637,6 +670,7 @@ ui_out_of_range_to_blank(struct statisticsparams *p)
       break;
     case 2:
       cond = gal_arithmetic(GAL_ARITHMETIC_OP_OR, 1, flagsor, cond_l, cond_g);
+      gal_data_free(cond_g);
       break;
     }
 
@@ -650,13 +684,22 @@ ui_out_of_range_to_blank(struct statisticsparams *p)
 
   /* Set all the pixels that satisfy the condition to blank. Note that a
      blank value will be used in the proper type of the input in the
-     `where' operator.*/
+     'where' operator. Also reset the blank flags so they are checked again
+     if necessary.*/
   gal_arithmetic(GAL_ARITHMETIC_OP_WHERE, 1, flagsor, p->input, cond, blank);
-
-
-  /* Reset the blank flags so they are checked again if necessary. */
   p->input->flag &= ~GAL_DATA_FLAG_BLANK_CH;
   p->input->flag &= ~GAL_DATA_FLAG_HASBLANK;
+  if(p->input->next)
+    {
+      gal_arithmetic(GAL_ARITHMETIC_OP_WHERE, 1, flagsor, p->input->next,
+                     cond, blank);
+      p->input->next->flag &= ~GAL_DATA_FLAG_BLANK_CH;
+      p->input->next->flag &= ~GAL_DATA_FLAG_HASBLANK;
+    }
+
+  /* Clean up. */
+  gal_data_free(cond);
+  gal_data_free(blank);
 }
 
 
@@ -711,25 +754,85 @@ ui_make_sorted_if_necessary(struct statisticsparams *p)
 
 
 
+/* Merge all given columns into one list. This is because people may call
+   for different columns in one call to '--column' ('-c', separated by a
+   comma), or multiple calls to '-c'. */
+gal_list_str_t *
+ui_read_columns_in_one(gal_list_str_t *incolumns)
+{
+  size_t i;
+  gal_data_t *strs;
+  char *c, **strarr;
+  gal_list_str_t *tmp, *final=NULL;
+
+  /* Go over the separate calls to the '-c' option, see the explaination in
+     Table's 'ui_columns_prepare' function for more on every step. */
+  for(tmp=incolumns;tmp!=NULL;tmp=tmp->next)
+    {
+      /* Remove any new-line character. */
+      for(c=tmp->v;*c!='\0';++c)
+        if(*c=='\\' && *(c+1)=='\n') { *c=' '; *(++c)=' '; }
+
+      /* Read the different comma-separated strings into an array (within a
+         'gal_data_t'). */
+      strs=gal_options_parse_csv_strings_raw(tmp->v, NULL, 0);
+      strarr=strs->array;
+
+      /* Add each array element to the final list of columns. */
+      for(i=0;i<strs->size;++i)
+        gal_list_str_add(&final, strarr[i], 1);
+
+      /* Clean up. */
+      gal_data_free(strs);
+    }
+
+  /* Reverse the list to be in the same order. */
+  gal_list_str_reverse(&final);
+
+  /* For a check.
+  for(tmp=final;tmp!=NULL;tmp=tmp->next)
+    printf("%s\n", tmp->v);
+  */
+
+  /* Return the final unified list. */
+  return final;
+}
+
+
+
+
+
 void
 ui_read_columns(struct statisticsparams *p)
 {
   int toomanycols=0, tformat;
-  gal_list_str_t *column=NULL;
   gal_data_t *cols, *tmp, *cinfo;
   size_t size, ncols, nrows, counter=0;
+  gal_list_str_t *incols, *columnlist=NULL;
   gal_list_str_t *lines=gal_options_check_stdin(p->inputname,
                                                 p->cp.stdintimeout, "input");
 
+  /* Merge possibly multiple calls to '-c' (each possibly separated by a
+     coma) into one list. */
+  incols=ui_read_columns_in_one(p->columns);
+
+  /* If any columns are specified, make sure there is a maximum of two
+     columns.  */
+  if(gal_list_str_number(incols)>2)
+    error(EXIT_FAILURE, 0, "%zu input columns were given but currently a "
+          "maximum of two columns are supported (two columns only for "
+          "special operations, the majority of operations are on a single "
+          "column)", gal_list_str_number(incols));
+
   /* If a reference column is also given, add it to the list of columns to
      read. */
   if(p->refcol)
-    gal_list_str_add(&column, p->refcol, 0);
+    gal_list_str_add(&columnlist, p->refcol, 0);
 
   /* If no column is specified, Statistics will abort and an error will be
      printed when the table has more than one column. If there is only one
      column, there is no need to specify any, so Statistics will use it. */
-  if(p->column==NULL)
+  if(incols==NULL)
     {
       /* Get the basic table information. */
       cinfo=gal_table_info(p->inputname, p->cp.hdu, lines, &ncols, &nrows,
@@ -740,23 +843,23 @@ ui_read_columns(struct statisticsparams *p)
       switch(ncols)
         {
         case 0:
-          error(EXIT_FAILURE, 0, "%s contains no usable information",
+          error(EXIT_FAILURE, 0, "%s contains no usable columns",
                 ( p->inputname
                   ? gal_checkset_dataset_name(p->inputname, p->cp.hdu)
                   : "Standard input" ));
         case 1:
-          gal_checkset_allocate_copy("1", &p->column);
+          gal_list_str_add(&incols, "1", 1);
           break;
         default:
           error(EXIT_FAILURE, 0, "%s is a table containing more than one "
                 "column. However, the specific column to work on isn't "
                 "specified.\n\n"
-                "Please use the `--column' (`-c') option to specify a "
+                "Please use the '--column' ('-c') option to specify a "
                 "column. You can either give it the column number "
                 "(couting from 1), or a match/search in its meta-data (e.g., "
                 "column names).\n\n"
                 "For more information, please run the following command "
-                "(press the `SPACE' key to go down and `q' to return to the "
+                "(press the 'SPACE' key to go down and 'q' to return to the "
                 "command-line):\n\n"
                 "    $ info gnuastro \"Selecting table columns\"\n",
                 ( p->inputname
@@ -765,18 +868,19 @@ ui_read_columns(struct statisticsparams *p)
         }
 
     }
-  gal_list_str_add(&column, p->column, 0);
+  if(columnlist) columnlist->next=incols;
+  else           columnlist=incols;
 
   /* Read the desired column(s). */
-  cols=gal_table_read(p->inputname, p->cp.hdu, lines, column, p->cp.searchin,
-                      p->cp.ignorecase, p->cp.minmapsize, p->cp.quietmmap,
-                      NULL);
+  cols=gal_table_read(p->inputname, p->cp.hdu, lines, columnlist,
+                      p->cp.searchin, p->cp.ignorecase, p->cp.minmapsize,
+                      p->cp.quietmmap, NULL);
   gal_list_str_free(lines, 1);
 
-  /* If the input was from standard input, we can actually write this into
-     it (for future reporting). */
+  /* If the input was from standard input, we'll set the input name to be
+     'stdin' (for future reporting). */
   if(p->inputname==NULL)
-    gal_checkset_allocate_copy("statistics", &p->inputname);
+    gal_checkset_allocate_copy("stdin", &p->inputname);
 
   /* Put the columns into the proper gal_data_t. */
   size=cols->size;
@@ -806,8 +910,15 @@ ui_read_columns(struct statisticsparams *p)
       /* Put the column into the proper pointer. */
       switch(++counter)
         {
-        case 1: p->input=tmp;                                         break;
-        case 2: if(p->refcol) p->reference=tmp; else toomanycols=1;   break;
+        case 1: p->input=tmp; break;
+        case 2:
+          if(gal_list_str_number(incols)==2)
+            p->input->next=tmp;
+          else
+            if(p->refcol) p->reference=tmp; else toomanycols=1;
+          break;
+        case 3:
+          if(p->refcol) p->reference=tmp; else toomanycols=1;   break;
         default: toomanycols=1;
         }
 
@@ -816,13 +927,23 @@ ui_read_columns(struct statisticsparams *p)
         gal_tableintern_error_col_selection(p->inputname, p->cp.hdu, "too "
                                             "many columns were selected by "
                                             "the given values to the "
-                                            "`--column' and/or `--refcol' "
+                                            "'--column' and/or '--refcol' "
                                             "options. Only one is "
                                             "acceptable for each.");
     }
 
   /* Clean up. */
-  gal_list_str_free(column, 0);
+  gal_list_str_free(incols, 1);
+  if(columnlist!=incols)
+    {
+      columnlist->next=NULL;
+      gal_list_str_free(columnlist, 0);
+    }
+
+  /* For a check:
+  printf("Number of input columns: %zu\n", gal_list_data_number(p->input));
+  exit(0);
+  */
 }
 
 
@@ -832,13 +953,16 @@ ui_read_columns(struct statisticsparams *p)
 void
 ui_preparations(struct statisticsparams *p)
 {
-  gal_data_t *check;
+  unsigned char flagsor = ( GAL_ARITHMETIC_FREE
+                            | GAL_ARITHMETIC_INPLACE
+                            | GAL_ARITHMETIC_NUMOK );
   int keepinputdir=p->cp.keepinputdir;
+  gal_data_t *check, *flag1, *flag2, *flag;
   struct gal_options_common_params *cp=&p->cp;
   struct gal_tile_two_layer_params *tl=&cp->tl;
   char *checkbasename = p->cp.output ? p->cp.output : p->inputname;
 
-  /* Change `keepinputdir' based on if an output name was given. */
+  /* Change 'keepinputdir' based on if an output name was given. */
   p->cp.keepinputdir = p->cp.output ? 1 : 0;
 
   /* Read the input. */
@@ -855,8 +979,14 @@ ui_preparations(struct statisticsparams *p)
     }
   else
     {
+      /* Read the table columns. */
       ui_read_columns(p);
       p->inputformat=INPUT_FORMAT_TABLE;
+
+      /* Two columns can only be given with 2D histogram mode. */
+      if(p->histogram2d==0 && p->input->next!=NULL)
+        error(EXIT_FAILURE, 0, "two column input is currently only "
+              "supported for 2D histogram mode");
     }
 
   /* Read the convolution kernel if necessary. */
@@ -888,8 +1018,8 @@ ui_preparations(struct statisticsparams *p)
             {
               gal_checkset_writable_remove(tl->tilecheckname, 0,
                                            cp->dontdelete);
-              gal_table_write(check, NULL, cp->tableformat, tl->tilecheckname,
-                              "TABLE", 0);
+              gal_table_write(check, NULL, NULL, cp->tableformat,
+                              tl->tilecheckname, "TABLE", 0);
             }
           gal_data_free(check);
         }
@@ -906,21 +1036,39 @@ ui_preparations(struct statisticsparams *p)
   /* If we are not to work on tiles, then re-order and change the input. */
   if(p->ontile==0 && p->sky==0 && p->contour==NULL)
     {
-      /* Only keep the elements we want. */
-      gal_blank_remove(p->input);
+      /* Only keep the elements we want. Note that if we have more than one
+         column, we need to move the same rows in both (otherwise their
+         widths won't be equal). */
+      if(p->input->next)
+        {
+          flag1=gal_blank_flag(p->input);
+          flag2=gal_blank_flag(p->input->next);
+          flag=gal_arithmetic(GAL_ARITHMETIC_OP_OR, 1, flagsor, flag1, flag2);
+
+          gal_blank_flag_apply(p->input, flag);
+          gal_blank_flag_apply(p->input->next, flag);
+
+          gal_blank_remove(p->input);
+          gal_blank_remove(p->input->next);
+
+          gal_data_free(flag);
+          p->input->next->flag &= ~GAL_DATA_FLAG_HASBLANK ;
+          p->input->next->flag |= GAL_DATA_FLAG_BLANK_CH ;
+        }
+      else
+        gal_blank_remove(p->input);
+      p->input->flag &= ~GAL_DATA_FLAG_HASBLANK ;
+      p->input->flag |= GAL_DATA_FLAG_BLANK_CH ;
 
       /* Make sure there actually are any (non-blank) elements left. */
       if(p->input->size==0)
         error(EXIT_FAILURE, 0, "%s: all elements are blank",
               gal_fits_name_save_as_string(p->inputname, cp->hdu));
 
-      p->input->flag &= ~GAL_DATA_FLAG_HASBLANK ;
-      p->input->flag |= GAL_DATA_FLAG_BLANK_CH ;
-
       /* Make sure there is data remaining: */
       if(p->input->size==0)
-        error(EXIT_FAILURE, 0, "%s: no data, maybe the `--greaterequal' or "
-              "`--lessthan' options need to be adjusted",
+        error(EXIT_FAILURE, 0, "%s: no data, maybe the '--greaterequal' or "
+              "'--lessthan' options need to be adjusted",
               gal_fits_name_save_as_string(p->inputname, cp->hdu) );
 
       /* Make the sorted array if necessary. */
@@ -931,7 +1079,7 @@ ui_preparations(struct statisticsparams *p)
       if( p->histogram || p->cumulative ) ++p->numoutfiles;
     }
 
-  /* Reset `keepinputdir' to what it originally was. */
+  /* Reset 'keepinputdir' to what it originally was. */
   p->cp.keepinputdir=keepinputdir;
 }
 
@@ -963,9 +1111,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
statisticsparams *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
+  /* 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
@@ -1011,7 +1159,7 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
statisticsparams *p)
 
   /* Prepare all the options as FITS keywords to write in output
      later. Note that in some modes, there is no output file, and
-     `ui_add_to_single_value' isn't yet prepared. */
+     'ui_add_to_single_value' isn't yet prepared. */
   if( (p->singlevalue && p->ontile) || p->sky || p->histogram \
       || p->cumulative)
     gal_options_as_fits_keywords(&p->cp);
@@ -1045,8 +1193,8 @@ ui_free_report(struct statisticsparams *p)
   /* Free the allocated arrays: */
   free(p->cp.hdu);
   free(p->cp.output);
-  gal_data_free(p->input);
   gal_data_free(p->reference);
+  gal_list_data_free(p->input);
   gal_list_f64_free(p->tp_args);
   gal_list_i32_free(p->singlevalue);
   gal_tile_full_free_contents(&p->cp.tl);
diff --git a/bin/statistics/ui.h b/bin/statistics/ui.h
index d1aab3b..22a3969 100644
--- a/bin/statistics/ui.h
+++ b/bin/statistics/ui.h
@@ -5,7 +5,7 @@ Statistics is part of GNU Astronomy Utilities (Gnuastro) 
package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -83,8 +83,10 @@ enum option_keys_enum
   UI_KEY_MODESYMVALUE,
   UI_KEY_QUANTFUNC,
   UI_KEY_ASCIICFP,
+  UI_KEY_HISTOGRAM2D,
   UI_KEY_MIRROR,
   UI_KEY_NUMBINS,
+  UI_KEY_NUMBINS2,
   UI_KEY_NUMASCIIBINS,
   UI_KEY_ASCIIHEIGHT,
   UI_KEY_LOWERBIN,
@@ -105,6 +107,9 @@ enum option_keys_enum
   UI_KEY_SIGCLIPMEDIAN,
   UI_KEY_SIGCLIPMEAN,
   UI_KEY_SIGCLIPSTD,
+  UI_KEY_GREATEREQUAL2,
+  UI_KEY_LESSTHAN2,
+  UI_KEY_ONEBINSTART2,
 };
 
 
diff --git a/bin/table/Makefile.am b/bin/table/Makefile.am
index 8b4dcdb..9f382ae 100644
--- a/bin/table/Makefile.am
+++ b/bin/table/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2016-2019, Free Software Foundation, Inc.
+## Copyright (C) 2016-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = asttable
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-asttable_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                 $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+asttable_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                 -lgnuastro $(CONFIG_LDADD)
 
 asttable_SOURCES = main.c ui.c arithmetic.c table.c
 
diff --git a/bin/table/args.h b/bin/table/args.h
index 11d2c21..5a264a7 100644
--- a/bin/table/args.h
+++ b/bin/table/args.h
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -47,7 +47,7 @@ struct argp_option program_options[] =
     {
       "wcsfile",
       UI_KEY_WCSFILE,
-      "STR",
+      "FITS",
       0,
       "File with WCS if conversion is requested.",
       GAL_OPTIONS_GROUP_INPUT,
@@ -70,6 +70,45 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "catcolumnfile",
+      UI_KEY_CATCOLUMNFILE,
+      "FITS/TXT",
+      0,
+      "File(s) to be concatenated by column.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->catcolumnfile,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "catcolumnhdu",
+      UI_KEY_CATCOLUMNHDU,
+      "STR/INT",
+      0,
+      "HDU/Extension(s) in catcolumnfile.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->catcolumnhdu,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "catcolumns",
+      UI_KEY_CATCOLUMNS,
+      "STR",
+      0,
+      "Columns to use in catcolumnfile.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->catcolumns,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
 
 
 
@@ -102,6 +141,33 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "catcolumnrawname",
+      UI_KEY_CATCOLUMNRAWNAME,
+      0,
+      0,
+      "Don't touch column names of --catcolumnfile.",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->catcolumnrawname,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "colmetadata",
+      UI_KEY_COLMETADATA,
+      "STR,STR[,STR,STR]",
+      0,
+      "Update output metadata (name, unit, comments).",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->colmetadata,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_name_and_strings
+    },
 
 
 
@@ -125,7 +191,49 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      gal_options_parse_name_and_values
+      gal_options_parse_name_and_float64s
+    },
+    {
+      "inpolygon",
+      UI_KEY_INPOLYGON,
+      "STR,STR",
+      0,
+      "Coord. columns that are inside '--polygon'.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->inpolygon,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_strings
+    },
+    {
+      "outpolygon",
+      UI_KEY_OUTPOLYGON,
+      "STR,STR",
+      0,
+      "Coord. columns that are outside '--polygon'.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->outpolygon,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_strings
+    },
+    {
+      "polygon",
+      UI_KEY_POLYGON,
+      "FLT:FLT[,...]",
+      0,
+      "Polygon for '--inpolygon' or '--outpolygon'.",
+      UI_GROUP_OUTROWS,
+      &p->polygon,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_colon_sep_csv
     },
     {
       "equal",
@@ -139,7 +247,7 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      gal_options_parse_name_and_values
+      gal_options_parse_name_and_strings
     },
     {
       "notequal",
@@ -153,7 +261,7 @@ struct argp_option program_options[] =
       GAL_OPTIONS_RANGE_ANY,
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET,
-      gal_options_parse_name_and_values
+      gal_options_parse_name_and_strings
     },
     {
       "sort",
@@ -207,6 +315,61 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "rowlimit",
+      UI_KEY_ROWLIMIT,
+      "INT,INT",
+      0,
+      "Only rows in this row-counter range.",
+      UI_GROUP_OUTROWS,
+      &p->rowlimit,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_GE_0,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_float64
+    },
+    {
+      "rowrandom",
+      UI_KEY_ROWRANDOM,
+      "INT",
+      0,
+      "Number of rows to select randomly.",
+      UI_GROUP_OUTROWS,
+      &p->rowrandom,
+      GAL_TYPE_SIZE_T,
+      GAL_OPTIONS_RANGE_GE_0,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+    },
+    {
+      "envseed",
+      UI_KEY_ENVSEED,
+      0,
+      0,
+      "Use GSL_RNG_SEED env. for '--rowrandom'.",
+      UI_GROUP_OUTROWS,
+      &p->envseed,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "noblank",
+      UI_KEY_NOBLANK,
+      "STR[,STR]",
+      0,
+      "Remove rows with blank in given columns.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->noblank,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      gal_options_parse_csv_strings
+    },
+
 
 
 
diff --git a/bin/table/arithmetic.c b/bin/table/arithmetic.c
index 279a9a5..dfa576c 100644
--- a/bin/table/arithmetic.c
+++ b/bin/table/arithmetic.c
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2019, Free Software Foundation, Inc.
+Copyright (C) 2019-2021, 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
@@ -29,8 +29,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdlib.h>
 
 #include <gnuastro/wcs.h>
+#include <gnuastro/type.h>
 
 #include <gnuastro-internal/checkset.h>
+#include <gnuastro-internal/arithmetic-set.h>
 
 #include "main.h"
 #include "arithmetic.h"
@@ -38,6 +40,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
+
 /*********************************************************************/
 /********************       List operations      *********************/
 /*********************************************************************/
@@ -55,6 +58,8 @@ arithmetic_add_new_to_end(struct arithmetic_token **list)
 
   /* Initialize its elements. */
   node->next=NULL;
+  node->name_def=NULL;
+  node->name_use=NULL;
   node->constant=NULL;
   node->index=GAL_BLANK_SIZE_T;
   node->operator=GAL_ARITHMETIC_OP_INVALID;
@@ -77,6 +82,25 @@ arithmetic_add_new_to_end(struct arithmetic_token **list)
 
 
 
+void
+arithmetic_token_free(struct arithmetic_token *list)
+{
+  struct arithmetic_token *tmp;
+  while(list!=NULL)
+    {
+      /* Free allocated elements first. */
+      if(list->name_def) free(list->name_def);
+      if(list->name_use) free(list->name_use);
+
+      /* Set the next list node, and free this one. */
+      tmp=list->next;
+      free(list);
+      list=tmp;
+    }
+}
+
+
+
 
 
 
@@ -104,9 +128,12 @@ arithmetic_operator_name(int operator)
   if(out==NULL)
     switch(operator)
       {
+      case ARITHMETIC_TABLE_OP_SET: out="set"; break;
       case ARITHMETIC_TABLE_OP_WCSTOIMG: out="wcstoimg"; break;
       case ARITHMETIC_TABLE_OP_IMGTOWCS: out="imgtowcs"; break;
-      case ARITHMETIC_TABLE_OP_ANGULARDISTANCE: out="angular-distance"; break;
+      case ARITHMETIC_TABLE_OP_DATETOSEC: out="date-to-sec"; break;
+      case ARITHMETIC_TABLE_OP_DISTANCEFLAT: out="distance-flat"; break;
+      case ARITHMETIC_TABLE_OP_DISTANCEONSPHERE: out="distance-on-sphere"; 
break;
       default:
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
               "the problem. %d is not a recognized operator code", __func__,
@@ -128,8 +155,8 @@ arithmetic_init_wcs(struct tableparams *p, char *operator)
     {
       /* A small sanity check. */
       if(p->wcsfile==NULL || p->wcshdu==NULL)
-        error(EXIT_FAILURE, 0, "`--wcsfile' and `--wcshdu' are necessary "
-              "for the `%s' operator", operator);
+        error(EXIT_FAILURE, 0, "'--wcsfile' and '--wcshdu' are necessary "
+              "for the '%s' operator", operator);
 
       /* Read the WCS. */
       p->wcs=gal_wcs_read(p->wcsfile, p->wcshdu, 0, 0, &p->nwcs);
@@ -153,12 +180,17 @@ arithmetic_set_operator(struct tableparams *p, char 
*string,
   /* Set the operator and number of operands. */
   if( op==GAL_ARITHMETIC_OP_INVALID )
     {
-      if(      !strncmp(string, "wcstoimg", 8))
+      /* Simple operators. */
+      if(      !strcmp(string, "wcstoimg"))
         { op=ARITHMETIC_TABLE_OP_WCSTOIMG; *num_operands=0; }
-      else if (!strncmp(string, "imgtowcs", 8))
+      else if( !strcmp(string, "imgtowcs"))
         { op=ARITHMETIC_TABLE_OP_IMGTOWCS; *num_operands=0; }
-      else if (!strncmp(string, "angular-distance", 8))
-        { op=ARITHMETIC_TABLE_OP_ANGULARDISTANCE; *num_operands=0; }
+      else if( !strcmp(string, "date-to-sec"))
+        { op=ARITHMETIC_TABLE_OP_DATETOSEC; *num_operands=0; }
+      else if( !strcmp(string, "distance-flat"))
+        { op=ARITHMETIC_TABLE_OP_DISTANCEFLAT; *num_operands=0; }
+      else if( !strcmp(string, "distance-on-sphere"))
+        { op=ARITHMETIC_TABLE_OP_DISTANCEONSPHERE; *num_operands=0; }
       else
         { op=GAL_ARITHMETIC_OP_INVALID; *num_operands=GAL_BLANK_INT; }
     }
@@ -189,7 +221,7 @@ arithmetic_init(struct tableparams *p, struct 
arithmetic_token **arith,
   size_t one=1;
   uint8_t ntype;
   char *str, *delimiter=" \t";
-  struct arithmetic_token *node=NULL;
+  struct arithmetic_token *atmp, *node=NULL;
   char *token=NULL, *lasttoken=NULL, *saveptr;
 
   /* Parse all the given tokens. */
@@ -206,18 +238,40 @@ arithmetic_init(struct tableparams *p, struct 
arithmetic_token **arith,
         {
           /* Token is a single number.*/
           if( (num=gal_type_string_to_number(token, &ntype)) )
-            node->constant=gal_data_alloc(num, ntype, 1, &one, NULL, 0, -1, 1,
-                                          NULL, NULL, NULL);
+            node->constant=gal_data_alloc(num, ntype, 1, &one, NULL, 0,
+                                          -1, 1, NULL, NULL, NULL);
+
+          /* This is a 'set-' operator. */
+          else if( !strncmp(token, GAL_ARITHMETIC_SET_PREFIX,
+                            GAL_ARITHMETIC_SET_PREFIX_LENGTH) )
+            {
+              node->num_operands=0;
+              node->operator=ARITHMETIC_TABLE_OP_SET;
+              gal_checkset_allocate_copy(token, &node->name_def);
+            }
 
-          /* Token is a column operand (column number or name). */
+          /* Non-pre-defined situation.*/
           else
             {
-              str = ( (token[0]=='c' && isdigit(token[1]))
-                      ? &token[1]   /* Column number (starting with `c'). */
-                      : token );    /* Column name, just add it.          */
-              gal_list_str_add(toread, str, 1);
-              node->index=*totcalled;
-              *totcalled+=1;
+              /* See if this is an already defined name. */
+              for(atmp=*arith; atmp!=NULL; atmp=atmp->next)
+                if( atmp->name_def
+                    && strcmp(token,
+                              ( atmp->name_def
+                                + GAL_ARITHMETIC_SET_PREFIX_LENGTH) )==0 )
+                  gal_checkset_allocate_copy(token, &node->name_use);
+
+              /* If it wasn't found to be a pre-defined name, then its a
+                 column operand (column number or name). */
+              if(node->name_use==NULL)
+                {
+                  str = ( (token[0]=='$' && isdigit(token[1]))
+                          ? &token[1]   /* Column number (starting with '$'). 
*/
+                          : token );    /* Column name, just add it.          
*/
+                  gal_list_str_add(toread, str, 1);
+                  node->index=*totcalled;
+                  *totcalled+=1;
+                }
             }
         }
 
@@ -227,7 +281,7 @@ arithmetic_init(struct tableparams *p, struct 
arithmetic_token **arith,
 
   /* A small sanity check: the last added token must be an operator. */
   if( node==NULL || node->operator==GAL_ARITHMETIC_OP_INVALID )
-    error(EXIT_FAILURE, 0, "last token in arithmetic column (`%s') is not a "
+    error(EXIT_FAILURE, 0, "last token in arithmetic column ('%s') is not a "
           "recognized operator", lasttoken);
 }
 
@@ -253,9 +307,9 @@ arithmetic_indexs_final(struct tableparams *p, size_t 
*colmatch)
   for(tmp=p->outcols;tmp!=NULL;tmp=tmp->next)
     {
       /* If we are on an arithmetic operation. */
-      if(tmp->tokens)
+      if(tmp->arith)
         {
-          for(atmp=tmp->tokens;atmp!=NULL;atmp=atmp->next)
+          for(atmp=tmp->arith;atmp!=NULL;atmp=atmp->next)
             if(atmp->index!=GAL_BLANK_SIZE_T)
               {
                 /* Small sanity check. */
@@ -312,7 +366,7 @@ arithmetic_indexs_final(struct tableparams *p, size_t 
*colmatch)
 /********************       Low-level tools      *********************/
 /*********************************************************************/
 static gal_data_t *
-arithmetic_stack_pop(gal_data_t **stack, int operator)
+arithmetic_stack_pop(gal_data_t **stack, int operator, char *errormsg)
 {
   gal_data_t *out=*stack;
 
@@ -320,10 +374,18 @@ arithmetic_stack_pop(gal_data_t **stack, int operator)
   if(*stack)
     *stack=(*stack)->next;
   else
-    error(EXIT_FAILURE, 0, "not enough operands for `%s'",
-          arithmetic_operator_name(operator));
-
-  /* Remove the `next' element to break from the stack and return. */
+    error(EXIT_FAILURE, 0, "not enough operands for '%s'%s",
+          arithmetic_operator_name(operator), errormsg?errormsg:"");
+
+  /* Arithmetic changes the contents of a dataset, so the old name (in the
+     FITS 'EXTNAME' keyword) must not be used beyond this
+     point. Furthermore, in Arithmetic, the 'name' element is used to
+     identify variables (with the 'set-' operator). */
+  if(out->name)    { free(out->name);    out->name=NULL;    }
+  if(out->unit)    { free(out->unit);    out->unit=NULL;    }
+  if(out->comment) { free(out->comment); out->comment=NULL; }
+
+  /* Remove the 'next' element to break from the stack and return. */
   out->next=NULL;
   return out;
 }
@@ -332,6 +394,53 @@ arithmetic_stack_pop(gal_data_t **stack, int operator)
 
 
 
+/* Wrapper function to pop operands within the 'set-' operator. */
+static gal_data_t *
+arithmetic_stack_pop_wrapper_set(void *in)
+{
+  struct gal_arithmetic_set_params *tprm
+    = (struct gal_arithmetic_set_params *)in;
+  gal_data_t **stack=(gal_data_t **)tprm->params;
+  return arithmetic_stack_pop(stack, ARITHMETIC_TABLE_OP_SET, NULL);
+}
+
+
+
+
+
+/* For the 'set-' operator. */
+static int
+arithmetic_set_name_used_later(void *in, char *name)
+{
+  struct gal_arithmetic_set_params *p=(struct gal_arithmetic_set_params *)in;
+  struct arithmetic_token *tokens = (struct arithmetic_token *)(p->tokens);
+  struct arithmetic_token *token;
+
+  size_t counter=0;
+
+  /* If the name exists after the current token, then return 1. Note that
+     we have already separated the usage of names set with 'set-' in the
+     'name_use' element of the 'token' structure. */
+  for(token=tokens;token!=NULL;token=token->next)
+    {
+      if( token->name_use
+          && counter > p->tokencounter
+          && strcmp(token->name_use, name)==0 )
+        return 1;
+
+      /* Increment the counter (has to be after the check above because it
+         may not always get to the 'counter' variable). */
+      ++counter;
+    }
+
+  /* If we get to this point, it means that the name doesn't exist. */
+  return 0;
+}
+
+
+
+
+
 /* Set the converted column metadata. */
 static void
 arithmetic_update_metadata(gal_data_t *col, char *name, char *unit,
@@ -374,16 +483,21 @@ static void
 arithmetic_wcs(struct tableparams *p, gal_data_t **stack, int operator)
 {
   gal_data_t *tmp;
+  char errormsg[100];
   struct wcsprm *wcs=p->wcs;
   size_t i, ndim=p->wcs->naxis;
   gal_data_t *coord[3]={NULL, NULL, NULL};
 
+  /* Prepare the (possibly necessaary!) error message for the number of
+     popped operand. */
+  sprintf(errormsg, " (input WCS has %zu dimensions)", ndim);
+
   /* Pop all the necessary datasets and make sure they are
      double-precision. NOTE: the top dataset on the stack is the
      highest-dimensional dataset. */
   for(i=0;i<ndim;++i)
     {
-      tmp=arithmetic_stack_pop(stack, operator);
+      tmp=arithmetic_stack_pop(stack, operator, errormsg);
       tmp=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT64);
       coord[ndim-i-1]=tmp;
     }
@@ -392,7 +506,6 @@ arithmetic_wcs(struct tableparams *p, gal_data_t **stack, 
int operator)
   if(coord[1]) coord[0]->next=coord[1];
   if(coord[2]) coord[1]->next=coord[2];
 
-
   /* Final preparations. */
   if(operator==ARITHMETIC_TABLE_OP_WCSTOIMG)
     {
@@ -433,24 +546,37 @@ arithmetic_wcs(struct tableparams *p, gal_data_t **stack, 
int operator)
 
 
 
+static double
+arithmetic_distance_flat(double a1, double a2, double b1, double b2)
+{
+  double d1=a1-b1, d2=a2-b2;
+  return sqrt(d1*d1 + d2*d2);
+}
+
+
+
+
+
 static void
-arithmetic_angular_dist(struct tableparams *p, gal_data_t **stack, int 
operator)
+arithmetic_distance(struct tableparams *p, gal_data_t **stack, int operator)
 {
   size_t i, j;
   double *o, *a1, *a2, *b1, *b2;
   gal_data_t *a, *b, *tmp, *out;
+  char *colname=NULL, *colcomment=NULL;
+  double (*distance_func)(double, double, double, double)=NULL;
 
-  /* Pop the columns for point `b'.*/
-  tmp=arithmetic_stack_pop(stack, operator);
+  /* Pop the columns for point 'b'.*/
+  tmp=arithmetic_stack_pop(stack, operator, NULL);
   tmp=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT64);
-  b=arithmetic_stack_pop(stack, operator);
+  b=arithmetic_stack_pop(stack, operator, NULL);
   b=gal_data_copy_to_new_type_free(b, GAL_TYPE_FLOAT64);
   b->next=tmp;
 
-  /* Pop the columns for point `a'.*/
-  tmp=arithmetic_stack_pop(stack, operator);
+  /* Pop the columns for point 'a'.*/
+  tmp=arithmetic_stack_pop(stack, operator, NULL);
   tmp=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_FLOAT64);
-  a=arithmetic_stack_pop(stack, operator);
+  a=arithmetic_stack_pop(stack, operator, NULL);
   a=gal_data_copy_to_new_type_free(a, GAL_TYPE_FLOAT64);
   a->next=tmp;
 
@@ -458,20 +584,39 @@ arithmetic_angular_dist(struct tableparams *p, gal_data_t 
**stack, int operator)
      single coordinate, but we don't know which one. */
   if(a->size!=a->next->size)
     error(EXIT_FAILURE, 0, "the sizes of the third and fourth operands "
-          "of the `%s' operator (respectively containing %zu and %zu "
+          "of the '%s' operator (respectively containing %zu and %zu "
           "numbers) must be equal", arithmetic_operator_name(operator),
           a->next->size, a->size);
   if(b->size!=b->next->size)
-    error(EXIT_FAILURE, 0, "the sizes of the third and fourth operands "
-          "of the `%s' operator (respectively containing %zu and %zu "
+    error(EXIT_FAILURE, 0, "the sizes of the first and second operands "
+          "of the '%s' operator (respectively containing %zu and %zu "
           "numbers) must be equal", arithmetic_operator_name(operator),
           b->next->size, b->size);
 
+  /* Basic settings based on the operator. */
+  switch(operator)
+    {
+    case ARITHMETIC_TABLE_OP_DISTANCEFLAT:
+      colname="dist-flat";
+      distance_func=arithmetic_distance_flat;
+      colcomment="Distance measured on a flat surface.";
+      break;
+    case ARITHMETIC_TABLE_OP_DISTANCEONSPHERE:
+      colname="dist-spherical";
+      distance_func=gal_wcs_angular_distance_deg;
+      colcomment="Distance measured on a great circle.";
+      break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+            "fix the problem. The operator code %d isn't recognized",
+            __func__, PACKAGE_BUGREPORT, operator);
+    }
+
   /* Make the output array based on the largest size. */
   out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1,
                      (a->size>b->size ? &a->size : &b->size), NULL, 0,
-                     p->cp.minmapsize, p->cp.quietmmap, "angular-dist",
-                     NULL, "Angular distance between input points");
+                     p->cp.minmapsize, p->cp.quietmmap, colname, NULL,
+                     colcomment);
 
   /* Measure the distances.  */
   o=out->array;
@@ -480,11 +625,10 @@ arithmetic_angular_dist(struct tableparams *p, gal_data_t 
**stack, int operator)
   if(a->size==1 || b->size==1) /* One of them is a single point. */
     for(i=0;i<a->size;++i)
       for(j=0;j<b->size;++j)
-        o[a->size>b->size?i:j] = gal_wcs_angular_distance_deg(a1[i], a2[i],
-                                                              b1[j], b2[j]);
+        o[a->size>b->size?i:j] = distance_func(a1[i], a2[i], b1[j], b2[j]);
   else                         /* Both have the same length. */
     for(i=0;i<a->size;++i)     /* (all were originally from the same table) */
-      o[i] = gal_wcs_angular_distance_deg(a1[i], a2[i], b1[i], b2[i]);
+      o[i] = distance_func(a1[i], a2[i], b1[i], b2[i]);
 
   /* Clean up and put the output dataset onto the stack. */
   gal_list_data_free(a);
@@ -496,6 +640,83 @@ arithmetic_angular_dist(struct tableparams *p, gal_data_t 
**stack, int operator)
 
 
 
+/* Convert the ISO date format to seconds since Unix time. */
+static void
+arithmetic_datetosec(struct tableparams *p, gal_data_t **stack,
+                     int operator)
+{
+  size_t i, v;
+  int64_t *iarr;
+  gal_data_t *out;
+  char *subsecstr=NULL;
+  double *darr, subsec=NAN;
+
+  /* Input dataset. */
+  gal_data_t *in=arithmetic_stack_pop(stack, operator, NULL);
+  char **strarr=in->array;
+
+  /* Output metadata. */
+  char *unit="sec";
+  char *name="UNIXSEC";
+  char *comment="Unix seconds (from 00:00:00 UTC, 1 January 1970)";
+
+  /* Make sure the input has a 'string' type. */
+  if(in->type!=GAL_TYPE_STRING)
+    error(EXIT_FAILURE, 0, "the operand given to 'date-to-sec' "
+          "should have a string type, but it is '%s'",
+          gal_type_name(in->type, 1));
+
+  /* Allocate the output dataset. */
+  out=gal_data_alloc(NULL, GAL_TYPE_INT64, 1, &in->size, NULL, 1,
+                     p->cp.minmapsize, p->cp.quietmmap, name, unit,
+                     comment);
+
+  /* Convert each input string (first try as an integer, assuming no
+     sub-second, or floating point precision).  */
+  iarr=out->array;
+  for(i=0; i<in->size; ++i)
+    {
+      v=gal_fits_key_date_to_seconds(strarr[i], &subsecstr,
+                                     &subsec);
+      iarr[i] = v==GAL_BLANK_SIZE_T ? GAL_BLANK_INT64 : v;
+      if(subsecstr) break;
+    }
+
+  /* If a sub-second string was present, then save the output as double
+     precision floating point. */
+  if(subsecstr)
+    {
+      /* Free the initially allocated output (as an integer). */
+      free(subsecstr);
+      gal_data_free(out);
+
+      /* Allocate a double-precision output. */
+      out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &in->size, NULL, 1,
+                         p->cp.minmapsize, p->cp.quietmmap, name, unit,
+                         comment);
+
+      /* Convert the date. */
+      darr=out->array;
+      for(i=0; i<in->size; ++i)
+        {
+          subsecstr=NULL;
+          v=gal_fits_key_date_to_seconds(strarr[i], &subsecstr,
+                                               &subsec);
+          darr[i] = v==GAL_BLANK_SIZE_T ? NAN : v;
+          if(subsecstr) { darr[i]+=subsec; free(subsecstr); }
+        }
+    }
+
+  /* Clean up and put the resulting calculation back on the stack. */
+  if(in) gal_data_free(in);
+  gal_list_data_add(stack, out);
+}
+
+
+
+
+
+
 
 
 
@@ -542,36 +763,38 @@ arithmetic_placeholder_name(gal_data_t *col)
 
 
 static void
-arithmetic_operator_run(struct tableparams *p, gal_data_t **stack,
-                        int operator, size_t num_operands)
+arithmetic_operator_run(struct tableparams *p,
+                        struct arithmetic_token *token,
+                        struct gal_arithmetic_set_params *setprm,
+                        gal_data_t **stack)
 {
   gal_data_t *d1=NULL, *d2=NULL, *d3=NULL;
   int flags = ( GAL_ARITHMETIC_INPLACE | GAL_ARITHMETIC_FREE
                 | GAL_ARITHMETIC_NUMOK );
 
-  /* When `num_operands!=0', the operator is in the library. */
-  if(num_operands)
+  /* When 'num_operands!=0', the operator is in the library. */
+  if(token->num_operands)
     {
       /* Pop the necessary number of operators. Note that the
          operators are poped from a linked list (which is
          last-in-first-out). So for the operators which need a
          specific order, the first poped operand is actally the
          last (right most, in in-fix notation) input operand.*/
-      switch(num_operands)
+      switch(token->num_operands)
         {
         case 1:
-          d1=arithmetic_stack_pop(stack, operator);
+          d1=arithmetic_stack_pop(stack, token->operator, NULL);
           break;
 
         case 2:
-          d2=arithmetic_stack_pop(stack, operator);
-          d1=arithmetic_stack_pop(stack, operator);
+          d2=arithmetic_stack_pop(stack, token->operator, NULL);
+          d1=arithmetic_stack_pop(stack, token->operator, NULL);
           break;
 
         case 3:
-          d3=arithmetic_stack_pop(stack, operator);
-          d2=arithmetic_stack_pop(stack, operator);
-          d1=arithmetic_stack_pop(stack, operator);
+          d3=arithmetic_stack_pop(stack, token->operator, NULL);
+          d2=arithmetic_stack_pop(stack, token->operator, NULL);
+          d1=arithmetic_stack_pop(stack, token->operator, NULL);
           break;
 
         case -1:
@@ -582,18 +805,19 @@ arithmetic_operator_run(struct tableparams *p, gal_data_t 
**stack,
 
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-                "the problem. `%zu' is not recognized as an operand "
-                "counter (with `%s')", __func__, PACKAGE_BUGREPORT,
-                num_operands, arithmetic_operator_name(operator));
+                "the problem. '%zu' is not recognized as an operand "
+                "counter (with '%s')", __func__, PACKAGE_BUGREPORT,
+                token->num_operands,
+                arithmetic_operator_name(token->operator));
         }
 
-      /* Run the arithmetic operation. Note that `gal_arithmetic' is a
+      /* Run the arithmetic operation. Note that 'gal_arithmetic' is a
          variable argument function (like printf). So the number of
          arguments it uses depend on the operator. In other words, when the
          operator doesn't need three operands, the extra arguments will be
          ignored. */
-      gal_list_data_add(stack, gal_arithmetic(operator, p->cp.numthreads,
-                                              flags, d1, d2, d3));
+      gal_list_data_add(stack, gal_arithmetic(token->operator, 
p->cp.numthreads,
+                                              flags, d1, d2, d3) );
 
       /* Reset the meta-data for the element that was just put on the
          stack. */
@@ -603,21 +827,30 @@ arithmetic_operator_run(struct tableparams *p, gal_data_t 
**stack,
   /* This operator is specific to this program (Table). */
   else
     {
-      switch(operator)
+      switch(token->operator)
         {
         case ARITHMETIC_TABLE_OP_WCSTOIMG:
         case ARITHMETIC_TABLE_OP_IMGTOWCS:
-          arithmetic_wcs(p, stack, operator);
+          arithmetic_wcs(p, stack, token->operator);
           break;
 
-        case ARITHMETIC_TABLE_OP_ANGULARDISTANCE:
-          arithmetic_angular_dist(p, stack, operator);
+        case ARITHMETIC_TABLE_OP_DATETOSEC:
+          arithmetic_datetosec(p, stack, token->operator);
+          break;
+
+        case ARITHMETIC_TABLE_OP_DISTANCEFLAT:
+        case ARITHMETIC_TABLE_OP_DISTANCEONSPHERE:
+          arithmetic_distance(p, stack, token->operator);
+          break;
+
+        case ARITHMETIC_TABLE_OP_SET:
+          gal_arithmetic_set_name(setprm, token->name_def);
           break;
 
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
                 "fix the problem. The operator code %d is not recognized",
-                __func__, PACKAGE_BUGREPORT, operator);
+                __func__, PACKAGE_BUGREPORT, token->operator);
         }
     }
 }
@@ -630,16 +863,28 @@ arithmetic_operator_run(struct tableparams *p, gal_data_t 
**stack,
 static void
 arithmetic_reverse_polish(struct tableparams *p, struct column_pack *outpack)
 {
-  gal_data_t *single, *stack=NULL;
   struct arithmetic_token *token;
+  gal_data_t *single, *stack=NULL;
+  struct gal_arithmetic_set_params setprm={0};
+
+  /* Initialize the arithmetic functions/pointers. */
+  setprm.params=&stack;
+  setprm.tokens=outpack->arith;
+  setprm.pop=arithmetic_stack_pop_wrapper_set;
+  setprm.used_later=arithmetic_set_name_used_later;
 
   /* Go through all the tokens given to this element. */
-  for(token=outpack->tokens;token!=NULL;token=token->next)
+  for(token=outpack->arith;token!=NULL;token=token->next)
     {
       /* We are on an operator. */
       if(token->operator!=GAL_ARITHMETIC_OP_INVALID)
-        arithmetic_operator_run(p, &stack, token->operator,
-                                token->num_operands);
+        arithmetic_operator_run(p, token, &setprm, &stack);
+
+      /* We are on a named variable. */
+      else if( token->name_use )
+        gal_list_data_add(&stack,
+                          gal_arithmetic_set_copy_named(&setprm,
+                                                        token->name_use));
 
       /* Constant number: just put it ontop of the stack. */
       else if(token->constant)
@@ -657,16 +902,19 @@ arithmetic_reverse_polish(struct tableparams *p, struct 
column_pack *outpack)
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
               "fix the problem. The token can't be identified as an "
               "operator, constant or column", __func__, PACKAGE_BUGREPORT);
+
+      /* Increment the token counter. */
+      ++setprm.tokencounter;
     }
 
   /* Put everything that remains in the stack (reversed) into the final
-     table. Just note that `gal_list_data_add' behaves differently for
-     lists, so we'll add have to manually set the `next' element to NULL
+     table. Just note that 'gal_list_data_add' behaves differently for
+     lists, so we'll add have to manually set the 'next' element to NULL
      before adding the column to the final table. */
   gal_list_data_reverse(&stack);
   while(stack!=NULL)
     {
-      /* Keep the top element in `single' and move `stack' to the next
+      /* Keep the top element in 'single' and move 'stack' to the next
          element. */
       single=stack;
       stack=stack->next;
@@ -677,7 +925,7 @@ arithmetic_reverse_polish(struct tableparams *p, struct 
column_pack *outpack)
               "single value, but other columns have also been requested "
               "which have more elements/rows");
 
-      /* Set `single->next' to NULL so it isn't treated as a list and
+      /* Set 'single->next' to NULL so it isn't treated as a list and
          remove all metadata */
       single->next=NULL;
       gal_list_data_add(&p->table, single);
@@ -713,18 +961,18 @@ arithmetic_operate(struct tableparams *p)
   struct column_pack *outpack;
 
   /* From now on, we will be looking for columns from the index in
-     `colarray', so to keep things clean, we'll set all the `next' elements
+     'colarray', so to keep things clean, we'll set all the 'next' elements
      to NULL. */
-  for(i=0;i<p->numcolarray;++i) p->colarray[i]->next=NULL;
+  for(i=0; i<p->numcolarray; ++i) p->colarray[i]->next=NULL;
 
   /* We'll also reset the output table pointer, to fill it in as we
      progress. */
   p->table=NULL;
 
   /* Go over each package of columns. */
-  for(outpack=p->outcols;outpack!=NULL;outpack=outpack->next)
+  for(outpack=p->outcols; outpack!=NULL; outpack=outpack->next)
     {
-      if(outpack->tokens)
+      if(outpack->arith)
         arithmetic_reverse_polish(p, outpack);
       else
         {
diff --git a/bin/table/arithmetic.h b/bin/table/arithmetic.h
index 7695484..dd682fb 100644
--- a/bin/table/arithmetic.h
+++ b/bin/table/arithmetic.h
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2019, Free Software Foundation, Inc.
+Copyright (C) 2020-2021, 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
@@ -35,9 +35,12 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Operators used for arithmetic on columns. */
 enum arithmetic_operators
 {
- ARITHMETIC_TABLE_OP_WCSTOIMG = GAL_ARITHMETIC_OP_LAST_CODE,
- ARITHMETIC_TABLE_OP_IMGTOWCS,
- ARITHMETIC_TABLE_OP_ANGULARDISTANCE,
+  ARITHMETIC_TABLE_OP_SET = GAL_ARITHMETIC_OP_LAST_CODE,
+  ARITHMETIC_TABLE_OP_WCSTOIMG,
+  ARITHMETIC_TABLE_OP_IMGTOWCS,
+  ARITHMETIC_TABLE_OP_DATETOSEC,
+  ARITHMETIC_TABLE_OP_DISTANCEFLAT,
+  ARITHMETIC_TABLE_OP_DISTANCEONSPHERE,
 };
 
 
@@ -52,6 +55,9 @@ arithmetic_init(struct tableparams *p, struct 
arithmetic_token **arith,
                 gal_list_str_t **toread, size_t *totcalled, char *expression);
 
 void
+arithmetic_token_free(struct arithmetic_token *list);
+
+void
 arithmetic_indexs_final(struct tableparams *p, size_t *colmatch);
 
 void
diff --git a/bin/table/asttable.conf b/bin/table/asttable.conf
index 4660480..ebb1c64 100644
--- a/bin/table/asttable.conf
+++ b/bin/table/asttable.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '[space], or tab). Lines starting with `#' are ignored.
+# example ' '[space], or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info asttable                       # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
@@ -20,4 +20,5 @@
 # warranty.
 
 # Inputs
- wcshdu        1
\ No newline at end of file
+ wcshdu        1
+ catcolumnhdu  1
diff --git a/bin/table/authors-cite.h b/bin/table/authors-cite.h
index 35d1228..3143d8f 100644
--- a/bin/table/authors-cite.h
+++ b/bin/table/authors-cite.h
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/table/main.c b/bin/table/main.c
index 5361480..9da4fd8 100644
--- a/bin/table/main.c
+++ b/bin/table/main.c
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/table/main.h b/bin/table/main.h
index 0a55512..065302a 100644
--- a/bin/table/main.h
+++ b/bin/table/main.h
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -24,9 +24,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #define MAIN_H
 
 /* Include necessary headers */
+#include <gsl/gsl_rng.h>
 #include <gnuastro/data.h>
 
 #include <gnuastro-internal/options.h>
+#include <gnuastro-internal/arithmetic-set.h>
 
 /* Progarm names.  */
 #define PROGRAM_NAME   "Table"         /* Program full name.       */
@@ -38,6 +40,8 @@ enum select_types
 {
  /* Different types of row-selection */
  SELECT_TYPE_RANGE,             /* 0 by C standard */
+ SELECT_TYPE_INPOLYGON,
+ SELECT_TYPE_OUTPOLYGON,
  SELECT_TYPE_EQUAL,
  SELECT_TYPE_NOTEQUAL,
 
@@ -62,6 +66,8 @@ struct arithmetic_token
   size_t     num_operands;  /* OPERATOR: Number of required operands.     */
   size_t            index;  /* OPERAND: Index in requested columns.       */
   gal_data_t    *constant;  /* OPERAND: a constant/single number.         */
+  char          *name_def;  /* Name given to the 'set-' operator.         */
+  char          *name_use;  /* If this a usage of a name.                 */
   struct arithmetic_token *next;  /* Pointer to next token.               */
 };
 
@@ -69,7 +75,7 @@ struct column_pack
 {
   size_t                    start; /* Starting ind. in requested columns. */
   size_t                numsimple; /* Number of simple columns.           */
-  struct arithmetic_token *tokens; /* Arithmetic tokens to use.           */
+  struct arithmetic_token  *arith; /* Arithmetic tokens to use.           */
   struct column_pack        *next; /* Next output column.                 */
 };
 
@@ -89,12 +95,24 @@ struct tableparams
   uint8_t         information;  /* ==1: only print FITS information.    */
   uint8_t     colinfoinstdout;  /* ==1: print column metadata in CL.    */
   gal_data_t           *range;  /* Range to limit output.               */
+  gal_data_t       *inpolygon;  /* Columns to check if inside polygon.  */
+  gal_data_t      *outpolygon;  /* Columns to check if outside polygon. */
+  gal_data_t         *polygon;  /* Values of vertices of the polygon.   */
   gal_data_t           *equal;  /* Values to keep in output.            */
   gal_data_t        *notequal;  /* Values to not include in output.     */
   char                  *sort;  /* Column name or number for sorting.   */
   uint8_t          descending;  /* Sort columns in descending order.    */
   size_t                 head;  /* Output only the no. of top rows.     */
   size_t                 tail;  /* Output only the no. of bottom rows.  */
+  gal_data_t        *rowlimit;  /* Output rows in row-counter range.    */
+  size_t            rowrandom;  /* Number of rows to show randomly.     */
+  uint8_t             envseed;  /* Use the environment for random seed. */
+  gal_data_t         *noblank;  /* Remove rows that have blank.         */
+  gal_list_str_t *catcolumnfile; /* Filename to concat column wise.     */
+  gal_list_str_t *catcolumnhdu;  /* HDU/extension for the catcolumn.    */
+  gal_list_str_t  *catcolumns;  /* List of columns to concatenate.      */
+  uint8_t    catcolumnrawname;  /* Don't modify name of appended col.   */
+  gal_data_t     *colmetadata;  /* Set column metadata.                 */
 
   /* Internal. */
   struct column_pack *outcols;  /* Output column packages.              */
@@ -111,7 +129,10 @@ struct tableparams
   uint8_t              sortin;  /* If the sort column is in the output. */
   time_t              rawtime;  /* Starting time of the program.        */
   gal_data_t       **colarray;  /* Array of columns, with arithmetic.   */
-  size_t          numcolarray;  /* Number of elements in `colarray'.    */
+  size_t          numcolarray;  /* Number of elements in 'colarray'.    */
+  gsl_rng                *rng;  /* Main random number generator.        */
+  const char        *rng_name;  /* Name of random number generator.     */
+  unsigned long int  rng_seed;  /* Random number generator seed.        */
 
   /* For arithmetic operators. */
   gal_list_str_t  *wcstoimg_p;  /* Pointer to the node.                 */
diff --git a/bin/table/table.c b/bin/table/table.c
index 7db80f7..86fda2b 100644
--- a/bin/table/table.c
+++ b/bin/table/table.c
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -29,13 +29,16 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <gsl/gsl_rng.h>
 #include <gsl/gsl_heapsort.h>
 
+#include <gnuastro/txt.h>
 #include <gnuastro/wcs.h>
 #include <gnuastro/fits.h>
 #include <gnuastro/table.h>
 #include <gnuastro/qsort.h>
 #include <gnuastro/pointer.h>
+#include <gnuastro/polygon.h>
 #include <gnuastro/arithmetic.h>
 #include <gnuastro/statistics.h>
 #include <gnuastro/permutation.h>
@@ -75,6 +78,56 @@ table_apply_permutation(gal_data_t *table, size_t 
*permutation,
 
 
 
+static void
+table_bring_to_top(gal_data_t *table, gal_data_t *rowids)
+{
+  char **strarr;
+  gal_data_t *col;
+  size_t i, *ids=rowids->array;
+
+  /* Make sure the rowids are sorted by increasing index. */
+  gal_statistics_sort_increasing(rowids);
+
+  /* Go over each column and move the desired rows to the top. */
+  for(col=table;col!=NULL;col=col->next)
+    {
+      /* For easy operation if the column is a string. */
+      strarr = col->type==GAL_TYPE_STRING ? col->array : NULL;
+
+      /* Move the desired rows up to the top. */
+      for(i=0;i<rowids->size;++i)
+        if( i != ids[i] )
+          {
+            /* Copy the contents. For strings, its just about freeing and
+               copying pointers. */
+            if(col->type==GAL_TYPE_STRING)
+              {
+                free(strarr[i]);
+                strarr[i]=strarr[ ids[i] ];
+                strarr[ ids[i] ]=NULL;
+              }
+            else
+              memcpy(gal_pointer_increment(col->array, i,      col->type),
+                     gal_pointer_increment(col->array, ids[i], col->type),
+                     gal_type_sizeof(col->type));
+          }
+
+      /* For string arrays, free the pointers of the remaining rows. */
+      if(col->type==GAL_TYPE_STRING)
+        for(i=rowids->size;i<col->size;++i)
+          if(strarr[i]) free(strarr[i]);
+
+      /* Correct the size (this should be after freeing of the string
+         pointers. */
+      col->size = col->dsize[0] = rowids->size;
+    }
+
+}
+
+
+
+
+
 static gal_data_t *
 table_selection_range(struct tableparams *p, gal_data_t *col)
 {
@@ -87,7 +140,7 @@ table_selection_range(struct tableparams *p, gal_data_t *col)
   /* First, make sure everything is OK. */
   if(p->range==NULL)
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us to fix the "
-          "problem at %s. `p->range' should not be NULL at this point",
+          "problem at %s. 'p->range' should not be NULL at this point",
           __func__, PACKAGE_BUGREPORT);
 
   /* Allocations. */
@@ -101,7 +154,7 @@ table_selection_range(struct tableparams *p, gal_data_t 
*col)
   ((double *)(min->array))[0] = darr[0];
   ((double *)(max->array))[0] = darr[1];
 
-  /* Move `p->range' to the next element in the list and free the current
+  /* Move 'p->range' to the next element in the list and free the current
      one (we have already read its values and don't need it any more). */
   tmp=p->range;
   p->range=p->range->next;
@@ -135,11 +188,121 @@ table_selection_range(struct tableparams *p, gal_data_t 
*col)
 
 
 
+/* Read column value of any type as a double for the polygon options. */
+static double
+selection_polygon_read_point(gal_data_t *col, size_t i)
+{
+  /* Check and assign the points to the points array. */
+  switch(col->type)
+    {
+    case GAL_TYPE_INT8:    return (( int8_t   *)col->array)[i];
+    case GAL_TYPE_UINT8:   return (( uint8_t  *)col->array)[i];
+    case GAL_TYPE_UINT16:  return (( uint16_t *)col->array)[i];
+    case GAL_TYPE_INT16:   return (( int16_t  *)col->array)[i];
+    case GAL_TYPE_UINT32:  return (( uint32_t *)col->array)[i];
+    case GAL_TYPE_INT32:   return (( int32_t  *)col->array)[i];
+    case GAL_TYPE_UINT64:  return (( uint64_t *)col->array)[i];
+    case GAL_TYPE_INT64:   return (( int64_t  *)col->array)[i];
+    case GAL_TYPE_FLOAT32: return (( float    *)col->array)[i];
+    case GAL_TYPE_FLOAT64: return (( double   *)col->array)[i];
+    default:
+      error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
+            __func__, col->type);
+    }
+
+  /* Control should not reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+        "problem. Control should not reach the end of this function",
+        __func__, PACKAGE_BUGREPORT);
+  return NAN;
+}
+
+
+
+
+
+/* Mask the rows that are not in the given polygon. */
+static gal_data_t *
+table_selection_polygon(struct tableparams *p, gal_data_t *col1,
+                        gal_data_t *col2, int in1out0)
+{
+  uint8_t *oarr;
+  double point[2];
+  gal_data_t *out=NULL;
+  size_t i, psize=p->polygon->size/2;
+
+  /* Allocate the output array: This array will have a '0' for the points
+     which are inside the polygon and '1' for those that are outside of it
+     (to be masked/removed from the input). */
+  out=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, col1->dsize, NULL, 0, -1, 1,
+                     NULL, NULL, NULL);
+  oarr=out->array;
+
+  /* Loop through all the rows in the given columns and check the points.*/
+  for(i=0; i<col1->size; i++)
+    {
+      /* Read the column values as a double. */
+      point[0]=selection_polygon_read_point(col1, i);
+      point[1]=selection_polygon_read_point(col2, i);
+
+      /* For '--inpolygon', if point is inside polygon, put 0, otherwise
+         1. Note that we are building a mask for the rows that must be
+         discarded, so we want '1' for the points we don't want. */
+      oarr[i] = (in1out0
+                 ? !gal_polygon_is_inside(p->polygon->array, point, psize)
+                 :  gal_polygon_is_inside(p->polygon->array, point, psize));
+
+      /* For a check
+      printf("(%f,%f): %s, %u\n", point[0], point[1], oarr[i]);
+      */
+    }
+
+  /* Return the output column. */
+  return out;
+}
+
+
+
+
+
+/* Given a string dataset and a single string, return a 'uint8_t' array
+   with the same size as the string dataset that has a '1' for all the
+   elements that are equal. */
+static gal_data_t *
+table_selection_string_eq_ne(gal_data_t *column, char *reference, int e0n1)
+{
+  gal_data_t *out;
+  uint8_t *oarr, comp;
+  size_t i, size=column->size;
+  char **strarr=column->array;
+
+  /* Allocate the output binary dataset. */
+  out=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &size, NULL, 0, -1, 1,
+                     NULL, NULL, NULL);
+  oarr=out->array;
+
+  /* Parse the values and mark the outputs IN THE OPPOSITE manner (we are
+     marking the ones that must be removed). */
+  for(i=0;i<size;++i)
+    {
+      comp=strcmp(strarr[i], reference);
+      oarr[i] = e0n1 ? (comp==0) : (comp!=0);
+    }
+
+  /* Return. */
+  return out;
+}
+
+
+
+
+
 static gal_data_t *
 table_selection_equal_or_notequal(struct tableparams *p, gal_data_t *col,
                                   int e0n1)
 {
-  double *darr;
+  void *varr;
+  char **strarr;
   size_t i, one=1;
   int numok=GAL_ARITHMETIC_NUMOK;
   int inplace=GAL_ARITHMETIC_INPLACE;
@@ -147,31 +310,50 @@ table_selection_equal_or_notequal(struct tableparams *p, 
gal_data_t *col,
   gal_data_t *arg = e0n1 ? p->notequal : p->equal;
 
   /* Note that this operator is used to make the "masked" array, so when
-     `e0n1==0' the operator should be `GAL_ARITHMETIC_OP_NE' and
+     'e0n1==0' the operator should be 'GAL_ARITHMETIC_OP_NE' and
      vice-versa.
 
-     For the merging with other elements, when `e0n1==0', we need the
-     `GAL_ARITHMETIC_OP_AND', but for `e0n1==1', it should be `OR'. */
+     For the merging with other elements, when 'e0n1==0', we need the
+     'GAL_ARITHMETIC_OP_AND', but for 'e0n1==1', it should be 'OR'. */
   int mergeop  = e0n1 ? GAL_ARITHMETIC_OP_OR : GAL_ARITHMETIC_OP_AND;
   int operator = e0n1 ? GAL_ARITHMETIC_OP_EQ : GAL_ARITHMETIC_OP_NE;
 
   /* First, make sure everything is OK. */
   if(arg==NULL)
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us to fix the "
-          "problem at %s. `p->range' should not be NULL at this point",
+          "problem at %s. 'p->range' should not be NULL at this point",
           __func__, PACKAGE_BUGREPORT);
 
-  /* Allocate space for the value. */
-  value=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &one, NULL, 0, -1, 1,
-                     NULL, NULL, NULL);
+  /* To easily parse the given values. */
+  strarr=arg->array;
 
   /* Go through the values given to this call of the option and flag the
      elements. */
   for(i=0;i<arg->size;++i)
     {
-      darr=arg->array;
-      ((double *)(value->array))[0] = darr[i];
-      eq=gal_arithmetic(operator, 1, numok, col, value);
+      /* Write the value  */
+      if(col->type==GAL_TYPE_STRING)
+        eq=table_selection_string_eq_ne(col, strarr[i], e0n1);
+      else
+        {
+          /* Allocate the value dataset. */
+          value=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &one, NULL, 0, -1, 1,
+                               NULL, NULL, NULL);
+          varr=value->array;
+
+          /* Read the stored string as a float64. */
+          if( gal_type_from_string(&varr, strarr[i], GAL_TYPE_FLOAT64) )
+            {
+              fprintf(stderr, "%s couldn't be read as a number.\n", strarr[i]);
+              exit(EXIT_FAILURE);
+            }
+
+          /* Mark the rows that are equal (irrespective of the column's
+             original numerical datatype). */
+          eq=gal_arithmetic(operator, 1, numok, col, value);
+        }
+
+      /* Merge the results with (possible) previous results. */
       if(out)
         {
           out=gal_arithmetic(mergeop, 1, inplace, out, eq);
@@ -189,8 +371,10 @@ table_selection_equal_or_notequal(struct tableparams *p, 
gal_data_t *col,
   }
   */
 
+
   /* Move the main pointer to the next possible call of the given
-     option. With this, we can safely free `arg' at this point. */
+     option. Note that 'arg' already points to 'p->equal' or 'p->notequal',
+     so it will automatically be freed with the next step.*/
   if(e0n1) p->notequal=p->notequal->next;
   else     p->equal=p->equal->next;
 
@@ -205,18 +389,16 @@ table_selection_equal_or_notequal(struct tableparams *p, 
gal_data_t *col,
 
 
 static void
-table_selection(struct tableparams *p)
+table_select_by_value(struct tableparams *p)
 {
-  uint8_t *u;
+  gal_data_t *rowids;
   struct list_select *tmp;
-  gal_data_t *mask, *addmask=NULL;
-  gal_data_t *sum, *perm, *blmask;
-  size_t i, g, b, *s, *sf, ngood=0;
+  uint8_t *u, *uf, *ustart;
+  size_t i, *s, ngood=0;
   int inplace=GAL_ARITHMETIC_INPLACE;
+  gal_data_t *mask, *blmask, *addmask=NULL;
 
   /* Allocate datasets for the necessary numbers and write them in. */
-  perm=gal_data_alloc(NULL, GAL_TYPE_SIZE_T, 1, p->table->dsize, NULL, 0,
-                      p->cp.minmapsize, p->cp.quietmmap, NULL, NULL, NULL);
   mask=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, p->table->dsize, NULL, 1,
                       p->cp.minmapsize, p->cp.quietmmap, NULL, NULL, NULL);
 
@@ -229,6 +411,14 @@ table_selection(struct tableparams *p)
           addmask=table_selection_range(p, tmp->col);
           break;
 
+        /* '--inpolygon' and '--outpolygon' need two columns. */
+        case SELECT_TYPE_INPOLYGON:
+        case SELECT_TYPE_OUTPOLYGON:
+          addmask=table_selection_polygon(p, tmp->col, tmp->next->col,
+                                          tmp->type==SELECT_TYPE_INPOLYGON);
+          tmp=tmp->next;
+          break;
+
         case SELECT_TYPE_EQUAL:
           addmask=table_selection_equal_or_notequal(p, tmp->col, 0);
           break;
@@ -275,49 +465,37 @@ table_selection(struct tableparams *p)
       gal_data_free(addmask);
     }
 
-  /* Find the final number of elements to print. */
-  sum=gal_statistics_sum(mask);
-  ngood = p->table->size - ((double *)(sum->array))[0];
+  /* Find the final number of elements to print and allocate the array to
+     keep them. */
+  uf=(u=mask->array)+mask->size;
+  ngood=0; do if(*u==0) ++ngood; while(++u<uf);
+  rowids=gal_data_alloc(NULL, GAL_TYPE_SIZE_T, 1, &ngood, NULL, 0,
+                        p->cp.minmapsize, p->cp.quietmmap, NULL,
+                        NULL, NULL);
 
-  /* Define the permutation: elements within range remain on the top of
-     the list, while the ones outside of will be placed after them
-     (starting from the index after the last good one). */
-  g=0;          /* Good indexs (starting from 0). */
-  b=ngood;      /* Bad indexs (starting from total number of good). */
-  u=mask->array;
-  sf=(s=perm->array)+perm->size;
-  do *s = *u++ ? b++ : g++; while(++s<sf);
+  /* Fill-in the finally desired row-IDs. */
+  s=rowids->array;
+  ustart=mask->array;
+  uf=(u=mask->array)+mask->size;
+  do if(*u==0) *s++ = u-ustart; while(++u<uf);
 
-  /* For a check
-  {
-    size_t i;
-    double *v=ref->array;
-    uint8_t *a=mask->array;
-    printf("ref->type: %s\n", gal_type_name(ref->type, 1));
-    for(i=0;i<ref->size;++i) printf("%u, %g\n", a[i], v[i]);
-    gal_permutation_check(perm->array, perm->size);
-  }
-  */
-
-  /* Apply the final permutation to the whole table. */
-  table_apply_permutation(p->table, perm->array, ngood, 1);
+  /* Move the desired rows to the top of the table. */
+  table_bring_to_top(p->table, rowids);
 
   /* If the sort column is not in the table (the proper range has already
      been applied to it), and we need to sort the resulting columns
      afterwards, we should also apply the permutation on the sort
      column. */
-  if(p->sortcol && p->sortin==0)
-    table_apply_permutation(p->sortcol, perm->array, ngood, 1);
+  if(p->sortcol && p->sortin==0) table_bring_to_top(p->sortcol, rowids);
 
   /* Clean up. */
   i=0;
   for(tmp=p->selectcol;tmp!=NULL;tmp=tmp->next)
     { if(p->freeselect[i]) {gal_data_free(tmp->col); tmp->col=NULL;} ++i; }
   ui_list_select_free(p->selectcol, 0);
-  gal_data_free(mask);
-  gal_data_free(perm);
   free(p->freeselect);
-  gal_data_free(sum);
+  gal_data_free(mask);
+  gal_data_free(rowids);
 }
 
 
@@ -348,10 +526,10 @@ table_sort(struct tableparams *p)
           "TIP: if you know the columns contents are all numbers that are "
           "just stored as strings, you can use this program to save the "
           "table as a text file, modify the column meta-data (for example "
-          "to type `i32' or `f32' instead of `strN'), then use this "
+          "to type 'i32' or 'f32' instead of 'strN'), then use this "
           "program again to save it as a FITS table.\n\n"
           "For more on column metadata in plain text format, please run "
-          "the following command (or see the `Gnuastro text table format "
+          "the following command (or see the 'Gnuastro text table format "
           "section of the book/manual):\n\n"
           "    $ info gnuastro \"gnuastro text table format\"");
 
@@ -371,7 +549,7 @@ table_sort(struct tableparams *p)
       case GAL_TYPE_FLOAT64: qsortfn=gal_qsort_index_single_float64_d; break;
       default:
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-              "the problem. The code `%u' wasn't recognized as a data type",
+              "the problem. The code '%u' wasn't recognized as a data type",
               __func__, PACKAGE_BUGREPORT, p->sortcol->type);
       }
   else
@@ -389,7 +567,7 @@ table_sort(struct tableparams *p)
       case GAL_TYPE_FLOAT64: qsortfn=gal_qsort_index_single_float64_i; break;
       default:
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-              "the problem. The code `%u' wasn't recognized as a data type",
+              "the problem. The code '%u' wasn't recognized as a data type",
               __func__, PACKAGE_BUGREPORT, p->sortcol->type);
       }
 
@@ -397,7 +575,7 @@ table_sort(struct tableparams *p)
   gal_qsort_index_single=p->sortcol->array;
   qsort(perm->array, perm->size, sizeof *s, qsortfn);
 
-  /* For a check (only on float32 type `sortcol'):
+  /* For a check (only on float32 type 'sortcol'):
   {
     float *f=p->sortcol->array;
     sf=(s=perm->array)+perm->size;
@@ -418,47 +596,146 @@ table_sort(struct tableparams *p)
 
 
 
+/* Apply random row selection. If the returned value is 'EXIT_SUCCESS',
+   then, it was successful. Otherwise, it will return 'EXIT_FAILURE' and
+   the input won't be touched. */
+static int
+table_random_rows(gal_data_t *table, gsl_rng *rng, size_t numrandom,
+                  size_t minmapsize, int quietmmap)
+{
+  int bad;
+  gal_data_t *rowids;
+  size_t i, j, *ids, ind;
+
+  /* Sanity check. */
+  if(numrandom>table->size)
+    return EXIT_FAILURE;
+
+  /* Allocate space for the list of rows to use. */
+  rowids=gal_data_alloc(NULL, GAL_TYPE_SIZE_T, 1, &numrandom, NULL, 0,
+                        minmapsize, quietmmap, NULL, NULL, NULL);
+
+  /* Select the row numbers. */
+  ids=rowids->array;
+  for(i=0;i<numrandom;++i)
+    {
+      /* Select a random index and make sure its new. */
+      bad=1;
+      while(bad)
+        {
+          ind = gsl_rng_uniform(rng) * table->size;
+          for(j=0;j<i;++j) if(ids[j]==ind) break;
+          if(i==j) bad=0;
+        }
+      ids[i]=ind;
+    }
+
+  /* Move the desired rows to the top. */
+  table_bring_to_top(table, rowids);
+
+  /* Clean up and return. */
+  gal_data_free(rowids);
+  return EXIT_SUCCESS;
+}
+
+
+
+
+
 static void
-table_head_tail(struct tableparams *p)
+table_select_by_position(struct tableparams *p)
 {
   char **strarr;
   gal_data_t *col;
   size_t i, start, end;
+  double *darr = p->rowlimit ? p->rowlimit->array : NULL;
+
+  /* Random row selection (by position, not value). This step is
+     independent of the other operations of this function, so as soon as
+     its finished return. */
+  if(p->rowrandom)
+    {
+      if( table_random_rows(p->table, p->rng, p->rowrandom,
+                            p->cp.minmapsize, p->cp.quietmmap)
+          == EXIT_FAILURE && p->cp.quiet==0 )
+        error(EXIT_SUCCESS, 0, "'--rowrandom' not activated because "
+              "the number of rows in the table at this stage (%zu) "
+              "is smaller than the number of requested random rows "
+              "(%zu). You can supress this message with '--quiet'",
+              p->table->size, p->rowrandom);
+      return;
+    }
+
+  /* Sanity check  */
+  if(p->rowlimit)
+    {
+      if(darr[0]>=p->table->size)
+        error(EXIT_FAILURE, 0, "the first value to '--rowlimit' (%g) "
+              "is larger than the number of rows (%zu)",
+              darr[0]+1, p->table->size);
+      else if( darr[1]>=p->table->size )
+        error(EXIT_FAILURE, 0, "the second value to '--rowlimit' (%g) "
+              "is larger than the number of rows (%zu)",
+              darr[1]+1, p->table->size);
+    }
 
   /* Go over all the columns and make the necessary corrections. */
   for(col=p->table;col!=NULL;col=col->next)
     {
-      /* If we are dealing with strings, we'll need to free the strings
-         that the columns that will not be used point to (outside the
-         allocated array directly `gal_data_t'). We don't have to worry
-         about the space for the actual pointers (they will be free'd by
-         `free' in any case, since they are in the initially allocated
-         array).*/
+      /* FOR STRING: we'll need to free the individual strings that will
+         not be used (outside the allocated array directly
+         'gal_data_t'). We don't have to worry about the space for the
+         actual pointers (they will be free'd by 'free' in any case, since
+         they are in the initially allocated array).*/
       if(col->type==GAL_TYPE_STRING)
         {
-          /* Set the start and ending indexs. */
-          start = p->head!=GAL_BLANK_SIZE_T ? p->head        : 0;
-          end   = p->head!=GAL_BLANK_SIZE_T ? p->table->size : p->tail;
-
-          /* Free their allocated spaces. */
+          /* Parse the rows and free extra pointers. */
           strarr=col->array;
-          for(i=start; i<end; ++i) { free(strarr[i]); strarr[i]=NULL; }
+          if(p->rowlimit)
+            {
+              /* Note that the given values to '--rowlimit' started from 1,
+                 but in 'ui.c' we subtracted one from it (so at this stage,
+                 it starts from 0). */
+              start = darr[0];
+              end   = darr[1];
+              for(i=0;i<p->table->size;++i)
+                if(i<start || i>end) { free(strarr[i]); strarr[i]=NULL; }
+            }
+          else
+            {
+              /* Free their allocated spaces. */
+              start = p->head!=GAL_BLANK_SIZE_T ? p->head        : 0;
+              end   = p->head!=GAL_BLANK_SIZE_T ? p->table->size : p->tail;
+              for(i=start; i<end; ++i) { free(strarr[i]); strarr[i]=NULL; }
+            }
         }
 
-      /* For `--tail', we'll need to bring the last columns to the
-         start. Note that we are using `memmove' because we want to be safe
-         with overlap. */
-      if(p->tail!=GAL_BLANK_SIZE_T)
-        memmove(col->array,
-                gal_pointer_increment(col->array, col->size - p->tail,
-                                      col->type),
-                p->tail*gal_type_sizeof(col->type));
-
-      /* In any case (head or tail), the new number of column elements is
-         the given value. */
-      col->size = col->dsize[0] = ( p->head!=GAL_BLANK_SIZE_T
-                                    ? p->head
-                                    : p->tail );
+      /* Make the final adjustment. */
+      if(p->rowlimit)
+        {
+          /* Move the values up to the top and correct the size. */
+          col->size=darr[1]-darr[0]+1;
+          memmove(col->array,
+                  gal_pointer_increment(col->array, darr[0], col->type),
+                  (darr[1]-darr[0]+1)*gal_type_sizeof(col->type));
+        }
+      else
+        {
+          /* For '--tail', we'll need to bring the last columns to the
+             start. Note that we are using 'memmove' because we want to be
+             safe with overlap. */
+          if(p->tail!=GAL_BLANK_SIZE_T)
+            memmove(col->array,
+                    gal_pointer_increment(col->array, col->size - p->tail,
+                                          col->type),
+                    p->tail*gal_type_sizeof(col->type));
+
+          /* In any case (head or tail), the new number of column elements
+             is the given value. */
+          col->size = col->dsize[0] = ( p->head!=GAL_BLANK_SIZE_T
+                                        ? p->head
+                                        : p->tail );
+        }
     }
 }
 
@@ -466,27 +743,279 @@ table_head_tail(struct tableparams *p)
 
 
 
+/*This function concatenates two table column wise .
+ It  attaches catcolumn table at the back of first table */
+static void
+table_catcolumn(struct tableparams *p)
+{
+  size_t counter=1;
+  gal_list_str_t *filell, *hdull;
+  gal_data_t *tocat, *final, *newcol;
+  char *tmpname, *hdu=NULL, cstr[100];
+  struct gal_options_common_params *cp=&p->cp;
+
+  /* Go over all the given files. */
+  hdull=p->catcolumnhdu;
+  for(filell=p->catcolumnfile; filell!=NULL; filell=filell->next)
+    {
+      /* Set the HDU (not necessary for non-FITS tables). */
+      if(gal_fits_name_is_fits(filell->v))
+        {
+          if(hdull) { hdu=hdull->v; hdull=hdull->next; }
+          else
+            error(EXIT_FAILURE, 0, "not enough '--catcolumnhdu's (or '-u'). "
+                  "For every FITS table given to '--catcolumnfile'. A call to "
+                  "'--catcolumnhdu' is necessary to identify its "
+                  "HDU/extension");
+        }
+      else hdu=NULL;
+
+      /* Read the catcolumn table. */
+      tocat=gal_table_read(filell->v, hdu, NULL, p->catcolumns, cp->searchin,
+                           cp->ignorecase, cp->minmapsize, p->cp.quietmmap,
+                           NULL);
+
+      /* Check the number of rows. */
+      if(tocat->dsize[0]!=p->table->dsize[0])
+        error(EXIT_FAILURE, 0, "%s: incorrect number of rows. The table given "
+              "to '--catcolumn' must have the same number of rows as the "
+              "main argument (after all row-selections have been applied), "
+              "but they have %zu and %zu rows respectively",
+              gal_fits_name_save_as_string(filell->v, hdu), tocat->dsize[0],
+              p->table->dsize[0]);
+
+      /* Append a counter to the column names because this option is most
+         often used with columns that have a similar name and it would help
+         the user if the output doesn't have multiple columns with same
+         name. */
+      if(p->catcolumnrawname==0)
+        for(newcol=tocat; newcol!=NULL; newcol=newcol->next)
+          if(newcol->name)
+            {
+              /* Add the counter suffix to the column name. */
+              sprintf(cstr, "-%zu", counter);
+              tmpname=gal_checkset_malloc_cat(newcol->name, cstr);
+
+              /* Free the old name and put in the new one. */
+              free(newcol->name);
+              newcol->name=tmpname;
+            }
+
+      /* Find the final column of the main table and add this table.*/
+      final=gal_list_data_last(p->table);
+      final->next=tocat;
+      ++counter;
+    }
+}
+
+
+
+
+
+void
+table_colmetadata(struct tableparams *p)
+{
+  char **strarr;
+  gal_data_t *meta, *col;
+  size_t counter, *colnum;
+
+  /* Loop through all the given updates and implement them. */
+  for(meta=p->colmetadata;meta!=NULL;meta=meta->next)
+    {
+      /* If the given column specifier is a name (not parse-able as a
+         number), then this condition will fail. */
+      colnum=NULL;
+      if( gal_type_from_string((void **)(&colnum), meta->name,
+                               GAL_TYPE_SIZE_T) )
+        {
+          /* We have been given a string, so find the first column that has
+             the same name. */
+          for(col=p->table; col!=NULL; col=col->next)
+            if(!strcmp(col->name, meta->name)) break;
+        }
+      /* The column specifier is a number. */
+      else
+        {
+          /* Go over the columns and find the one with this counter. */
+          counter=1;
+          for(col=p->table; col!=NULL; col=col->next)
+            if(counter++==colnum[0]) break;
+
+          /* Clean up the space that was allocated for 'colnum' (its not
+             allocated when the given value was a string). */
+          free(colnum);
+        }
+
+      /* If a match was found, then 'col' should not be NULL. */
+      if(col==NULL)
+        error(EXIT_FAILURE, 0, "no column found for '%s' (given to "
+              "'--colmetadata'). Columns can either be specified by "
+              "their position in the output table (integer counter, "
+              "starting from 1), or their name (the first column "
+              "found with the given name will be used)", meta->name);
+
+      /* The matching column is found and we know that atleast one value is
+         already given (otherwise 'gal_options_parse_name_and_values' would
+         abort the program). The first given string is the new name. */
+      strarr=meta->array;
+      if(col->name) free(col->name);
+      gal_checkset_allocate_copy(strarr[0], &col->name);
+
+      /* If more than one string is given, the second one is the new
+         unit. */
+      if(meta->size>1)
+        {
+          /* Replace the unit. */
+          if(col->unit) free(col->unit);
+          gal_checkset_allocate_copy(strarr[1], &col->unit);
+
+          /* The next element is the comment of the column. */
+          if(meta->size>2)
+            {
+              if(col->comment) free(col->comment);
+              gal_checkset_allocate_copy(strarr[2], &col->comment);
+            }
+        }
+    }
+}
+
+
+
+
+
+void
+table_noblank(struct tableparams *p)
+{
+  int found;
+  size_t i, j, *index;
+  gal_data_t *tcol, *flag;
+  char **strarr=p->noblank->array;
+  gal_list_sizet_t *column_indexs=NULL;
+
+  /* See if all columns should be checked, or just a select few. */
+  if( p->noblank->size==1 && !strcmp(strarr[0],"_all") )
+    {
+      for(i=0;i<gal_list_data_number(p->table);++i)
+        gal_list_sizet_add(&column_indexs, i);
+    }
+
+  /* Only certain columns should be checked, so find/add their index. */
+  else
+    for(i=0;i<p->noblank->size;++i)
+      {
+        /* First go through the column names and if they match, add
+           them. Note that we don't want to stop once a name is found, in
+           this scenario, if multiple columns have the same name, we should
+           use all.*/
+        j=0;
+        found=0;
+        for(tcol=p->table; tcol!=NULL; tcol=tcol->next)
+          {
+            if( tcol->name && !strcmp(tcol->name, strarr[i]) )
+              {
+                found=1;
+                gal_list_sizet_add(&column_indexs, j);
+              }
+            ++j;
+          }
+
+        /* If the given string didn't match any column name, it must be a
+           number, so parse it as a number and use that number. */
+        if(found==0)
+          {
+            /* Parse the given index. */
+            index=NULL;
+            if( gal_type_from_string((void **)(&index), strarr[i],
+                                     GAL_TYPE_SIZE_T) )
+              error(EXIT_FAILURE, 0, "column '%s' didn't match any of the "
+                    "final column names and can't be parsed as a column "
+                    "counter (starting from 1) either", strarr[i]);
+
+            /* Make sure its not zero (the user counts from 1). */
+            if(*index==0)
+              error(EXIT_FAILURE, 0, "the column number (given to the "
+                    "'--noblank' option) should start from 1, but you have "
+                    "given 0.");
+
+            /* Make sure that the index falls within the number (note that
+               it still counts from 1).  */
+            if(*index > gal_list_data_number(p->table))
+              error(EXIT_FAILURE, 0, "the final output table only has %zu "
+                    "columns, but you have given column %zu to '--noblank'. "
+                    "Recall that '--noblank' operates on the output columns "
+                    "and that you can also use output column names (if they "
+                    "have any)",
+                    gal_list_data_number(p->table), *index);
+
+            /* Everything is fine, add the index to the list of columns to
+               check. */
+            gal_list_sizet_add(&column_indexs, *index-1);
+
+            /* Clean up. */
+            free(index);
+          }
+
+        /* For a check.
+           printf("%zu\n", column_indexs->v);
+        */
+      }
+
+  /* Remove all blank rows from the output table, note that we don't need
+     the flags of the removed columns here. So we can just free it up. */
+  flag=gal_blank_remove_rows(p->table, column_indexs);
+  gal_data_free(flag);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 /**************************************************************/
 /***************       Top function         *******************/
 /**************************************************************/
 void
 table(struct tableparams *p)
 {
-  /* Apply a certain range (if required) to the output sample. */
-  if(p->selection) table_selection(p);
+  /* Apply ranges based on row values (if required). */
+  if(p->selection) table_select_by_value(p);
 
   /* Sort it (if required). */
   if(p->sort) table_sort(p);
 
   /* If the output number of rows is limited, apply them. */
-  if(p->head!=GAL_BLANK_SIZE_T || p->tail!=GAL_BLANK_SIZE_T)
-    table_head_tail(p);
+  if( p->rowlimit
+      || p->rowrandom
+      || p->head!=GAL_BLANK_SIZE_T
+      || p->tail!=GAL_BLANK_SIZE_T )
+    table_select_by_position(p);
 
-  /* If any operations are needed, do them. */
+  /* If any arithmetic operations are needed, do them. */
   if(p->outcols)
     arithmetic_operate(p);
 
+  /* Concatenate the columns of tables (if required)*/
+  if(p->catcolumnfile) table_catcolumn(p);
+
+  /* When column metadata should be updated. */
+  if(p->colmetadata) table_colmetadata(p);
+
+  /* When any columns with blanks should be removed. */
+  if(p->noblank) table_noblank(p);
+
   /* Write the output. */
-  gal_table_write(p->table, NULL, p->cp.tableformat, p->cp.output,
+  gal_table_write(p->table, NULL, NULL, p->cp.tableformat, p->cp.output,
                   "TABLE", p->colinfoinstdout);
 }
diff --git a/bin/table/table.h b/bin/table/table.h
index ee78434..ce72962 100644
--- a/bin/table/table.h
+++ b/bin/table/table.h
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/table/ui.c b/bin/table/ui.c
index 63226a6..adbd834 100644
--- a/bin/table/ui.c
+++ b/bin/table/ui.c
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -70,8 +70,8 @@ doc[] = GAL_STRINGS_TOP_HELP_INFO PROGRAM_NAME" can be used 
to view the "
   "can be plain text (with white-space or comma as delimiters), FITS ascii, "
   "or FITS binary tables. The output columns can either be selected by "
   "number (counting from 1), name or using regular expressions. For regular "
-  "expressions, enclose the value to the `--column' (`-c') option in "
-  "slashes (`\\', as in `-c\\^mag\\'). To print the selected columns on the "
+  "expressions, enclose the value to the '--column' ('-c') option in "
+  "slashes ('\\', as in '-c\\^mag\\'). To print the selected columns on the "
   "command-line, don't specify an output file.\n"
   GAL_STRINGS_MORE_HELP_INFO
   /* After the list of options: */
@@ -162,18 +162,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct tableparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -221,7 +221,7 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /***************       Sanity Check         *******************/
 /**************************************************************/
 /* Read and check ONLY the options. When arguments are involved, do the
-   check in `ui_check_options_and_arguments'. */
+   check in 'ui_check_options_and_arguments'. */
 static void
 ui_read_check_only_options(struct tableparams *p)
 {
@@ -232,28 +232,63 @@ ui_read_check_only_options(struct tableparams *p)
      the output. */
   gal_tableintern_check_fits_format(p->cp.output, p->cp.tableformat);
 
-  /* Some checks on `--range'. */
+  /* Some checks on '--range'. */
   if(p->range)
     for(tmp=p->range;tmp!=NULL;tmp=tmp->next)
       {
         /* Range needs two input numbers. */
         if(tmp->size!=2)
-          error(EXIT_FAILURE, 0, "two values (separated by `:' or `,') are "
-                "necessary for `--range' in this format: "
-                "`--range=COLUMN,min:max'");
+          error(EXIT_FAILURE, 0, "two values (separated by ':' or ',') are "
+                "necessary for '--range' in this format: "
+                "'--range=COLUMN,min:max'");
 
         /* The first must be smaller than the second. */
         darr=tmp->array;
         if( darr[0] > darr[1] )
-          error(EXIT_FAILURE, 0, "first value (%g) given to `--range' must "
+          error(EXIT_FAILURE, 0, "first value (%g) given to '--range' must "
                 "be smaller than the second (%g)", darr[0], darr[1]);
       }
 
+  /* Basic checks for '--inpolygon' or '--outpolygon'. */
+  if(p->inpolygon || p->outpolygon)
+    {
+      if(p->inpolygon && p->outpolygon)
+        error(EXIT_FAILURE, 0, "'--inpolygon' and '--outpolygon' options "
+              "cannot be called together");
+
+      if(p->inpolygon && p->inpolygon->size!=2)
+        error(EXIT_FAILURE, 0, "two columns (for coordinates) can "
+              "be given to '--inpolygon'");
+
+      if(p->outpolygon && p->outpolygon->size!=2)
+        error(EXIT_FAILURE, 0, "two columns (for coordinates) can "
+              "be given to '--outpolygon'");
+
+      if(p->polygon==NULL)
+        error(EXIT_FAILURE, 0, "no polygon specified for '--inpolygon' or "
+              "'--outpolygon'! Please provide the vertices of the desired "
+              "polygon with the '--polygon' option in this format: "
+              "v1x,v1y:v2x,v2y:v3x,v3y:...");
+    }
 
-  /* Make sure `--head' and `--tail' aren't given together. */
-  if(p->head!=GAL_BLANK_SIZE_T && p->tail!=GAL_BLANK_SIZE_T)
-    error(EXIT_FAILURE, 0, "`--head' and `--tail' options cannot be "
-          "called together");
+  /* Make sure only one of the positional row selection operations is
+     called in this run.*/
+  if(p->rowlimit
+     && p->rowrandom
+     && p->head!=GAL_BLANK_SIZE_T
+     && p->tail!=GAL_BLANK_SIZE_T)
+    error(EXIT_FAILURE, 0, "only one of the following options can be "
+          "called in one run: '--head', '--tail', '--rowlimit' and "
+          "'--rowrandom'");
+
+  /* If '--colmetadata' is given, make sure none of the given options have
+     more than three values. */
+  if(p->colmetadata)
+    for(tmp=p->colmetadata;tmp!=NULL;tmp=tmp->next)
+      if(tmp->size>3)
+        error(EXIT_FAILURE, 0, "at most three values can be given to each "
+              "call of '--colmetadata' ('-m') after the original columns "
+              "name or number. But %zu strings have been given", tmp->size);
 }
 
 
@@ -269,8 +304,8 @@ ui_check_options_and_arguments(struct tableparams *p)
     {
       if( gal_fits_name_is_fits(p->filename) && p->cp.hdu==NULL )
         error(EXIT_FAILURE, 0, "no HDU specified. When the input is a FITS "
-              "file, a HDU must also be specified, you can use the `--hdu' "
-              "(`-h') option and give it the HDU number (starting from "
+              "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");
 
     }
@@ -414,7 +449,7 @@ ui_outcols_last(struct column_pack *list)
 
 
 
-/* Allocate a clean `out_columns' structure and put it at the top of the
+/* Allocate a clean 'out_columns' structure and put it at the top of the
    list. */
 static struct column_pack *
 ui_outcols_add_new_to_end(struct column_pack **list)
@@ -430,8 +465,8 @@ ui_outcols_add_new_to_end(struct column_pack **list)
 
   /* Initialize its elements. */
   node->next=NULL;
+  node->arith=NULL;
   node->numsimple=0;
-  node->tokens=NULL;
   node->start=GAL_BLANK_SIZE_T;
 
   /* If the list already has elements, go to the last node in the list and
@@ -452,6 +487,22 @@ ui_outcols_add_new_to_end(struct column_pack **list)
 
 
 
+static void
+ui_outcols_free(struct column_pack *list)
+{
+  struct column_pack *tmp;
+  while(list!=NULL)
+    {
+      arithmetic_token_free(list->arith);
+      tmp=list->next;
+      free(list);
+      list=tmp;
+    }
+}
+
+
+
+
 
 
 
@@ -525,8 +576,8 @@ ui_print_info_exit(struct tableparams *p)
 static void
 ui_columns_prepare(struct tableparams *p)
 {
-  char **strarr;
   gal_data_t *strs;
+  char *c, **strarr;
   size_t i, totcalled=0;
   struct column_pack *node, *last;
   gal_list_str_t *tmp, *toread=NULL;
@@ -535,8 +586,16 @@ ui_columns_prepare(struct tableparams *p)
      one value separated by a comma. */
   for(tmp=p->columns;tmp!=NULL;tmp=tmp->next)
     {
+      /* Remove any possibly commented new-line where we have a backslash
+         followed by a new-line character (replace the two characters with
+         two single space characters). This can happen with the 'arith'
+         argument in a script, when it gets long (bug #58371). But to be
+         general in other cases too, we'll just correct it here. */
+      for(c=tmp->v;*c!='\0';++c)
+        if(*c=='\\' && *(c+1)=='\n') { *c=' '; *(++c)=' '; }
+
       /* Read the different comma-separated strings into an array (within a
-         `gal_data_t'). */
+         'gal_data_t'). */
       strs=gal_options_parse_csv_strings_raw(tmp->v, NULL, 0);
       strarr=strs->array;
 
@@ -550,8 +609,8 @@ ui_columns_prepare(struct tableparams *p)
               /* If this is the first arithmetic operation and the user has
                  already asked for some columns, we'll need to put all
                  previously requested simply-printed columns into an
-                 `outcols' structure, then add this arithmetic operation's
-                 `outcols'. */
+                 'outcols' structure, then add this arithmetic operation's
+                 'outcols'. */
               if(p->outcols==NULL && toread)
                 {
                   /* Allocate an empty structure and set the necessary
@@ -565,7 +624,7 @@ ui_columns_prepare(struct tableparams *p)
               /* Add a new column pack, then read all the tokens (while
                  specifying which columns it needs). */
               node=ui_outcols_add_new_to_end(&p->outcols);
-              arithmetic_init(p, &node->tokens, &toread, &totcalled,
+              arithmetic_init(p, &node->arith, &toread, &totcalled,
                               strarr[i]+ARITHMETIC_CALL_LENGTH);
               free(strarr[i]);
             }
@@ -581,7 +640,7 @@ ui_columns_prepare(struct tableparams *p)
                   /* If the previous column package was an arithmetic
                      operation, allocate a new node. */
                   last=ui_outcols_last(p->outcols);
-                  if(last->tokens)
+                  if(last->arith)
                     {
                       node=ui_outcols_add_new_to_end(&p->outcols);
                       node->start=totcalled;
@@ -616,30 +675,31 @@ ui_columns_prepare(struct tableparams *p)
       struct column_pack *tmp;
       struct arithmetic_token *atmp;
       for(tmp=p->outcols;tmp!=NULL;tmp=tmp->next)
-        {
-          if(tmp->tokens)
-            for(atmp=tmp->tokens;atmp!=NULL;atmp=atmp->next)
+        if(tmp->arith)
+          {
+            printf("Arithmetic: \n");
+            for(atmp=tmp->arith;atmp!=NULL;atmp=atmp->next)
               {
-                printf("Arithmetic: ");
-                if(atmp->constant) printf("Constant number\n");
-                else if(atmp->index) printf("Called column: %zu\n",
-                                            atmp->index);
-                else if(atmp->operator!=ARITHMETIC_TABLE_OP_INVALID)
-                  printf("Operator: %d\n", atmp->operator);
-                else
-                  error(EXIT_FAILURE, 0, "%s: UNKNOWN SITUATION!",
-                        __func__);
+                if(atmp->operator!=GAL_ARITHMETIC_OP_INVALID)
+                  {
+                    printf("\tOperator: %d\n", atmp->operator);
+                    if(atmp->name_def)
+                      printf("\t\t(Name definition: %s)\n", atmp->name_def);
+                  }
+                else if(atmp->constant) printf("\tConstant number\n");
+                else if(atmp->name_use) printf("\tName usage: %s\n",
+                                               atmp->name_use);
+                else printf("\tCalled column: %zu\n", atmp->index);
               }
-          else
-            printf("Simple: start: %zu, num: %zu\n", tmp->start,
-                   tmp->numsimple);
-        }
+          }
+        else
+          printf("Simple: start: %zu, num: %zu\n", tmp->start,
+                 tmp->numsimple);
     }
   */
 
-
-  /* Delete the old list, then reverse the `toread' list, and put it into
-     `p->columns'. */
+  /* Delete the old list, then reverse the 'toread' list, and put it into
+     'p->columns'. */
   gal_list_str_free(p->columns, 1);
   gal_list_str_reverse(&toread);
   p->columns=toread;
@@ -649,7 +709,7 @@ ui_columns_prepare(struct tableparams *p)
 
 
 
-/* The users give column numbers counting from 1. But we need an `index'
+/* The users give column numbers counting from 1. But we need an 'index'
    (starting from 0). So if we can read it as a number, we'll subtract one
    from it. */
 static size_t
@@ -676,24 +736,52 @@ ui_check_select_sort_before(struct tableparams *p, 
gal_list_str_t *lines,
                             size_t *sortindout, size_t **selectindout_out,
                             size_t **selecttypeout_out)
 {
-  gal_data_t *dtmp, *allcols;
-  size_t sortind=GAL_BLANK_SIZE_T;
+  char **strarr;
   gal_list_sizet_t *tmp, *indexll;
   gal_list_str_t *stmp, *add=NULL;
   int tableformat, selecthasname=0;
+  size_t one=1, sortind=GAL_BLANK_SIZE_T;
   size_t *selectind=NULL, *selecttype=NULL;
   size_t *selectindout=NULL, *selecttypeout=NULL;
   size_t i, j, k, *s, *sf, allncols, numcols, numrows;
+  gal_data_t *dtmp, *allcols, *inpolytmp=NULL, *outpolytmp=NULL;
 
-  /* Important note: these have to be in the same order as the `enum
-     select_types' in `main.h'. */
-  gal_data_t *select[SELECT_TYPE_NUMBER]={p->range, p->equal, p->notequal};
+  /* Important note: these have to be in the same order as the 'enum
+     select_types' in 'main.h'. We'll fill the two polygon elements
+     later. */
+  gal_data_t *select[SELECT_TYPE_NUMBER]={p->range, NULL, NULL,
+                                          p->equal, p->notequal};
+
+
+  /* The inpolygon dataset is currently a single dataset with two elements
+     (strings). But we need to have it as two linked datasets with a set
+     name. */
+  if(p->inpolygon)
+    {
+      strarr=p->inpolygon->array;
+      inpolytmp=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &one, NULL, 1, -1, 1,
+                               strarr[0], NULL, NULL);
+      inpolytmp->next=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &one, NULL,
+                                     1, -1, 1, strarr[1], NULL, NULL);
+      select[SELECT_TYPE_INPOLYGON]=inpolytmp;
+    }
+  if(p->outpolygon)
+    {
+      strarr=p->outpolygon->array;
+      outpolytmp=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &one, NULL, 1, -1, 1,
+                               strarr[0], NULL, NULL);
+      outpolytmp->next=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, &one, NULL,
+                                     1, -1, 1, strarr[1], NULL, NULL);
+      select[SELECT_TYPE_OUTPOLYGON]=outpolytmp;
+    }
 
 
   /* Allocate necessary spaces. */
   if(p->selection)
     {
       *nselect = ( gal_list_data_number(p->range)
+                   + gal_list_data_number(inpolytmp)
+                   + gal_list_data_number(outpolytmp)
                    + gal_list_data_number(p->equal)
                    + gal_list_data_number(p->notequal) );
       selectind=gal_pointer_allocate(GAL_TYPE_SIZE_T, *nselect, 0,
@@ -712,7 +800,7 @@ ui_check_select_sort_before(struct tableparams *p, 
gal_list_str_t *lines,
 
   /* See if the given columns are numbers or names. */
   i=0;
-  if(p->sort)  sortind  = ui_check_select_sort_read_col_ind(p->sort);
+  if(p->sort) sortind=ui_check_select_sort_read_col_ind(p->sort);
   if(p->selection)
     for(k=0;k<SELECT_TYPE_NUMBER;++k)
       for(dtmp=select[k];dtmp!=NULL;dtmp=dtmp->next)
@@ -733,14 +821,15 @@ ui_check_select_sort_before(struct tableparams *p, 
gal_list_str_t *lines,
      zero. */
   if(p->sort && sortind!=GAL_BLANK_SIZE_T && sortind>=numcols)
     error(EXIT_FAILURE, 0, "%s has %zu columns, less than the column "
-          "number given to  `--sort' (%s)",
+          "number given to  '--sort' (%s)",
           gal_fits_name_save_as_string(p->filename, p->cp.hdu), numcols,
           p->sort);
   if(p->selection)
     for(i=0;i<*nselect;++i)
       if(selectind[i]!=GAL_BLANK_SIZE_T && selectind[i]>=numcols)
         error(EXIT_FAILURE, 0, "%s has %zu columns, less than the column "
-              "number given to  `--range', `--equal', or `--sort' (%zu)",
+              "number given to  '--range', '--inpolygon', '--outpolygon', "
+              "'--equal', or '--sort' (%zu)",
               gal_fits_name_save_as_string(p->filename, p->cp.hdu), numcols,
               selectind[i]);
 
@@ -752,9 +841,9 @@ ui_check_select_sort_before(struct tableparams *p, 
gal_list_str_t *lines,
       if(selectind[i]==GAL_BLANK_SIZE_T) { selecthasname=1; break; }
   if( (p->sort && sortind==GAL_BLANK_SIZE_T) || selecthasname )
     {
-      /* For `--sort', go over all the columns if an index hasn't been set
+      /* For '--sort', go over all the columns if an index hasn't been set
          yet. If the input columns have a name, see if their names matches
-         the name given to `sort'. */
+         the name given to 'sort'. */
       if(p->sort && sortind==GAL_BLANK_SIZE_T)
         for(i=0;i<numcols;++i)
           if( allcols[i].name && !strcasecmp(allcols[i].name, p->sort) )
@@ -763,7 +852,7 @@ ui_check_select_sort_before(struct tableparams *p, 
gal_list_str_t *lines,
       /* Same for the selection. Just note that here we may have multiple
          calls. It is thus important to loop over the values given to range
          first, then loop over the column names from the start for each new
-         `--ran */
+         '--ran */
       i=0;
       for(k=0;k<SELECT_TYPE_NUMBER;++k)
         for(dtmp=select[k];dtmp!=NULL;dtmp=dtmp->next)
@@ -781,7 +870,7 @@ ui_check_select_sort_before(struct tableparams *p, 
gal_list_str_t *lines,
   /* The columns must be good indexs now, if they don't the user didn't
      specify the name properly and the program must abort. */
   if( p->sort && sortind==GAL_BLANK_SIZE_T )
-    error(EXIT_FAILURE, 0, "%s: no column named `%s' (value to `--sort') "
+    error(EXIT_FAILURE, 0, "%s: no column named '%s' (value to '--sort') "
           "you can either specify a name or number",
           gal_fits_name_save_as_string(p->filename, p->cp.hdu), p->sort);
   if(p->selection)
@@ -791,8 +880,8 @@ ui_check_select_sort_before(struct tableparams *p, 
gal_list_str_t *lines,
         for(dtmp=select[k];dtmp!=NULL;dtmp=dtmp->next)
           {
             if(selectind[i]==GAL_BLANK_SIZE_T)
-              error(EXIT_FAILURE, 0, "%s: no column named `%s' (value to "
-                    "`--%s') you can either specify a name or number",
+              error(EXIT_FAILURE, 0, "%s: no column named '%s' (value to "
+                    "'--%s') you can either specify a name or number",
                     gal_fits_name_save_as_string(p->filename, p->cp.hdu),
                     dtmp->name,
                     ( k==0?"range":( k==1?"equal":"notequal") ));
@@ -873,6 +962,8 @@ ui_check_select_sort_before(struct tableparams *p, 
gal_list_str_t *lines,
   if(selectind) free(selectind);
   if(selecttype) free(selecttype);
   gal_data_array_free(allcols, numcols, 0);
+  if(inpolytmp) gal_list_data_free(inpolytmp);
+  if(outpolytmp) gal_list_data_free(outpolytmp);
 }
 
 
@@ -924,12 +1015,12 @@ ui_check_select_sort_after(struct tableparams *p, size_t 
nselect,
 
 
   /* Terminate the desired output table where it should be terminated (by
-     setting `origlast->next' to NULL. */
+     setting 'origlast->next' to NULL. */
   origlast->next=NULL;
 
 
-  /*  Also, remove any possibly existing `next' pointer for `sortcol' and
-     `selectcol'. */
+  /*  Also, remove any possibly existing 'next' pointer for 'sortcol' and
+     'selectcol'. */
   if(p->sort && sortindout>=origoutncols)
     { p->sortcol->next=NULL;  p->freesort=1; }
   else p->sortin=1;
@@ -956,7 +1047,8 @@ ui_check_select_sort_after(struct tableparams *p, size_t 
nselect,
 static void
 ui_preparations(struct tableparams *p)
 {
-  size_t *colmatch;
+  double *darr;
+  size_t i, *colmatch;
   gal_list_str_t *lines;
   size_t nselect=0, origoutncols=0;
   size_t sortindout=GAL_BLANK_SIZE_T;
@@ -973,18 +1065,20 @@ ui_preparations(struct tableparams *p)
   ui_columns_prepare(p);
 
 
-  /* If the input is from stdin, save it as `lines'. */
+  /* If the input is from stdin, save it as 'lines'. */
   lines=gal_options_check_stdin(p->filename, p->cp.stdintimeout, "input");
 
 
-  /* If any kind of row-selection is requested set `p->selection' to 1. */
-  p->selection = p->range || p->equal || p->notequal;
+  /* If any kind of row-selection is requested set 'p->selection' to 1. */
+  p->selection = ( p->range || p->inpolygon || p->outpolygon || p->equal
+                   || p->notequal );
+
 
   /* If row sorting or selection are requested, see if we should read any
      extra columns. */
   if(p->selection || p->sort)
-    ui_check_select_sort_before(p, lines, &nselect, &origoutncols, &sortindout,
-                                &selectindout, &selecttypeout);
+    ui_check_select_sort_before(p, lines, &nselect, &origoutncols,
+                                &sortindout, &selectindout, &selecttypeout);
 
 
   /* If we have any arithmetic operations, we need to make sure how many
@@ -1035,7 +1129,7 @@ ui_preparations(struct tableparams *p)
 
   /* If the head or tail values are given and are larger than the number of
      rows, just set them to the number of rows (print the all the final
-     rows). This is how the `head' and `tail' programs of GNU Coreutils
+     rows). This is how the 'head' and 'tail' programs of GNU Coreutils
      operate. */
   p->head = ( ((p->head!=GAL_BLANK_SIZE_T) && (p->head > p->table->size))
               ? p->table->size
@@ -1044,6 +1138,51 @@ ui_preparations(struct tableparams *p)
               ? p->table->size
               : p->tail );
 
+  /* If rows are given, do some sanity checks and make sure that they are
+     within the table's limits. */
+  if(p->rowlimit)
+    {
+      /* There should only be two values. */
+      if(p->rowlimit->size!=2)
+        error(EXIT_FAILURE, 0, "only two should be given to "
+              "'--rowlimit' (the top and bottom row numbers specifying "
+              "your desired range)");
+
+      /* Do individual checks. */
+      darr=p->rowlimit->array;
+      for(i=0;i<p->rowlimit->size;++i)
+        {
+          /* Make sure it isn't 0 or negative. */
+          if( darr[i]<=0 )
+            error(EXIT_FAILURE, 0, "%g (value given to '--rowlimit') "
+                  "is smaller than, or equal to, zero! This option's "
+                  "values are row-counters (starting from 1), so they "
+                  "must be positive integers", darr[i]);
+
+          /* Make sure its an integer. */
+          if( darr[i] != (size_t)(darr[i]) )
+            error(EXIT_FAILURE, 0, "%g (value given to '--rowlimit') is "
+                  "not an integer! This option's values are row-counters "
+                  "so they must be integers.", darr[i]);
+
+          /* Subtract 1 from the value, so it counts from 0. */
+          --darr[i];
+        }
+
+      /* Make sure that the first value is smaller than the second. */
+      if( darr[0] > darr[1] )
+        error(EXIT_FAILURE, 0, "the first value to '--rowlimit' (%g) is "
+              "larger than the second (%g). This option's values defines "
+              "a row-counter interval, assuming the first value is the top "
+              "of the desired interval (smaller row counter) and the second "
+              "value is the bottom of the desired interval (larger row "
+              "counter)", darr[0], darr[1]);
+    }
+
+  /* If random rows are desired, we need to define a GSL random number
+     generator structure. */
+  if(p->rowrandom)
+    p->rng=gal_checkset_gsl_rng(p->envseed, &p->rng_name, &p->rng_seed);
 
   /* Clean up. */
   free(colmatch);
@@ -1079,9 +1218,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
tableparams *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
+  /* 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
@@ -1123,6 +1262,19 @@ ui_read_check_inputs_setup(int argc, char *argv[], 
struct tableparams *p)
 
   /* Read/allocate all the necessary starting arrays. */
   ui_preparations(p);
+
+  /* Let the user know basic information if necessary (for example when a
+     random number generator has been used). */
+  if(p->rng && !p->cp.quiet)
+    {
+      /* Write the information. */
+      printf(PROGRAM_NAME" "PACKAGE_VERSION" started on %s",
+             ctime(&p->rawtime));
+      printf("Parameters used for '--randomrows':\n");
+      printf("  - Random number generator name: %s\n", p->rng_name);
+      printf("  - Random number generator seed: %lu\n", p->rng_seed);
+      printf("(use '--quiet' to supress this starting message)\n");
+    }
 }
 
 
@@ -1153,6 +1305,12 @@ ui_free_report(struct tableparams *p)
   /* Free the allocated arrays: */
   free(p->cp.hdu);
   free(p->cp.output);
+  ui_outcols_free(p->outcols);
   gal_list_data_free(p->table);
+  gal_list_data_free(p->colmetadata);
   if(p->colarray) free(p->colarray);
+
+  /* If a random number generator was allocated, free it. */
+  if(p->rng) gsl_rng_free(p->rng);
+
 }
diff --git a/bin/table/ui.h b/bin/table/ui.h
index 7af1d1c..a898b19 100644
--- a/bin/table/ui.h
+++ b/bin/table/ui.h
@@ -5,7 +5,7 @@ Table is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -41,8 +41,8 @@ enum program_args_groups
 
 /* Available letters for short options:
 
-   a b d f g j k l m p t u v x y z
-   A B C E G H J L O Q R X Y
+   a d f g j k l p t v x y z
+   A B E G H J O Q R X Y
 */
 enum option_keys_enum
 {
@@ -59,9 +59,21 @@ enum option_keys_enum
   UI_KEY_DESCENDING      = 'd',
   UI_KEY_HEAD            = 'H',
   UI_KEY_TAIL            = 't',
+  UI_KEY_NOBLANK         = 'b',
+  UI_KEY_CATCOLUMNS      = 'C',
+  UI_KEY_CATCOLUMNHDU    = 'u',
+  UI_KEY_CATCOLUMNFILE   = 'L',
+  UI_KEY_COLMETADATA     = 'm',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
+  UI_KEY_POLYGON         = 1000,
+  UI_KEY_ENVSEED,
+  UI_KEY_ROWLIMIT,
+  UI_KEY_ROWRANDOM,
+  UI_KEY_INPOLYGON,
+  UI_KEY_OUTPOLYGON,
+  UI_KEY_CATCOLUMNRAWNAME,
 };
 
 
diff --git a/bin/warp/Makefile.am b/bin/warp/Makefile.am
index 9759465..850600b 100644
--- a/bin/warp/Makefile.am
+++ b/bin/warp/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -20,20 +20,19 @@
 
 
 ## Necessary pre-processer and linker flags.
-AM_LDFLAGS = -L\$(top_builddir)/lib
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib -I\$(top_srcdir)/lib
+AM_LDFLAGS  = -L\$(top_builddir)/lib
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
+              -I\$(top_srcdir)/lib
 
-if COND_NORPATH
-  MAYBE_NORPATH = $(CONFIG_LDADD)
-endif
 
 
 ## Program definition (name, linking, sources and headers)
 bin_PROGRAMS = astwarp
 
-## Reason for linking with `libgnu' described in `bin/TEMPLATE/Makefile.am'.
-astwarp_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la -lgnuastro \
-                $(MAYBE_NORPATH)
+## Reason for linking with 'libgnu' described in 'bin/TEMPLATE/Makefile.am'.
+astwarp_LDADD = $(top_builddir)/bootstrapped/lib/libgnu.la \
+                -lgnuastro $(CONFIG_LDADD)
 
 astwarp_SOURCES = main.c ui.c warp.c
 
diff --git a/bin/warp/args.h b/bin/warp/args.h
index c1d10d5..ffb17b4 100644
--- a/bin/warp/args.h
+++ b/bin/warp/args.h
@@ -5,7 +5,7 @@ Warp is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/warp/astwarp.conf b/bin/warp/astwarp.conf
index 6b2604e..03656ce 100644
--- a/bin/warp/astwarp.conf
+++ b/bin/warp/astwarp.conf
@@ -3,7 +3,7 @@
 #
 # 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
-# example ` '(space), or tab). Lines starting with `#' are ignored.
+# example ' '(space), or tab). Lines starting with '#' are ignored.
 #
 # For more information, please run these commands:
 #
@@ -12,7 +12,7 @@
 #  $ info astwarp                        # All options and input/output.
 #  $ info gnuastro "Configuration files" # How to use configuration files.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/bin/warp/authors-cite.h b/bin/warp/authors-cite.h
index a22b983..e8d1d23 100644
--- a/bin/warp/authors-cite.h
+++ b/bin/warp/authors-cite.h
@@ -5,7 +5,7 @@ Warp is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -26,10 +26,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* 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.
+   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.*/
+   This macro will be used in 'gal_options_print_citation' function of
+   'lib/options.c' (from the top Gnuastro source code directory). */
 
 #define PROGRAM_BIBTEX ""
 
diff --git a/bin/warp/main.c b/bin/warp/main.c
index 18677a6..cd1fd1d 100644
--- a/bin/warp/main.c
+++ b/bin/warp/main.c
@@ -5,7 +5,7 @@ Warp is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bin/warp/main.h b/bin/warp/main.h
index acbfbfc..af5a8da 100644
--- a/bin/warp/main.h
+++ b/bin/warp/main.h
@@ -5,7 +5,7 @@ Warp is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/warp/ui.c b/bin/warp/ui.c
index 2a95287..a91fb14 100644
--- a/bin/warp/ui.c
+++ b/bin/warp/ui.c
@@ -5,7 +5,7 @@ Warp is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -154,18 +154,18 @@ parse_opt(int key, char *arg, struct argp_state *state)
 {
   struct warpparams *p = state->input;
 
-  /* Pass `gal_options_common_params' into the child parser.  */
+  /* 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`
+     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, "
+    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");
 
@@ -212,7 +212,7 @@ parse_opt(int key, char *arg, struct argp_state *state)
 /**********      Modular matrix linked list       *************/
 /**************************************************************/
 /* Save the codes of the user's desired modular warpings into the linked
-   list. Because the types of these options are `GAL_TYPE_INVALID', this
+   list. Because the types of these options are 'GAL_TYPE_INVALID', this
    function will not be called when printing the full list of parameters
    and their values. */
 static void *
@@ -224,27 +224,27 @@ ui_add_to_modular_warps_ll(struct argp_option *option, 
char *arg,
   gal_data_t *new;
   struct warpparams *p=(struct warpparams *)params;
 
-  /* When an argument is necessary (note that `--align' doesn't take an
+  /* When an argument is necessary (note that '--align' doesn't take an
      argument), make sure we actually have a string to parse. Also note
      that if an argument is necessary, but none is given Argp will
      automatically abort the program with an informative error. */
   if(arg && *arg=='\0')
-    error(EXIT_FAILURE, 0, "empty string given to `--%s'", option->name);
+    error(EXIT_FAILURE, 0, "empty string given to '--%s'", option->name);
 
   /* Parse the (possible) arguments. */
   if(option->key==UI_KEY_ALIGN)
     {
       /* For functions the standard checking isn't done, so first, we'll
          make sure that if we are in a configuration file (where
-         `arg!=NULL'), the value is either 0 or 1. */
+         'arg!=NULL'), the value is either 0 or 1. */
       if( arg && strcmp(arg, "0") && strcmp(arg, "1") )
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "the `--align' "
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "the '--align' "
                       "option takes no arguments. In a configuration file "
-                      "it can only have the values `1' or `0', indicating "
+                      "it can only have the values '1' or '0', indicating "
                       "if it should be used or not");
 
       /* Align doesn't take any values, but if called in a configuration
-         file with a value of `0', we should ignore it. */
+         file with a value of '0', we should ignore it. */
       if(arg && *arg=='0') return NULL;
 
       /* Allocate the data structure. */
@@ -265,7 +265,7 @@ ui_add_to_modular_warps_ll(struct argp_option *option, char 
*arg,
                       "may be given, you can use multiple modular warpings");
       if(new->size!=4 && new->size!=9)
         error_at_line(EXIT_FAILURE, 0, filename, lineno, "only a 4 or 9 "
-                      "element `matrix' is currently acceptable. `%s' has "
+                      "element 'matrix' is currently acceptable. '%s' has "
                       "%zu elements", arg, new->size);
 
       /* Keep the matrix in the main structure. */
@@ -277,7 +277,7 @@ ui_add_to_modular_warps_ll(struct argp_option *option, char 
*arg,
          warpings. */
       if(new->size>2)
         error_at_line(EXIT_FAILURE, 0, filename, lineno, "%zu numbers "
-                      "given to the `%s' option. Modular warpings can "
+                      "given to the '%s' option. Modular warpings can "
                       "accept 2 numbers at the most currently (for 2D "
                       "datasets)", new->size, option->name);
 
@@ -286,9 +286,9 @@ ui_add_to_modular_warps_ll(struct argp_option *option, char 
*arg,
       if(option->key==UI_KEY_ROTATE)
         {
           if(new->size!=1)
-            error_at_line(EXIT_FAILURE, 0, filename, lineno, "the `rotate' "
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "the 'rotate' "
                       "option only takes one value (the angle of rotation). "
-                      "You have given: `%s'", arg);
+                      "You have given: '%s'", arg);
         }
       else if (option->key==UI_KEY_FLIP)
         {
@@ -296,9 +296,9 @@ ui_add_to_modular_warps_ll(struct argp_option *option, char 
*arg,
             {
               tmp=((double *)(new->array))[i];
               if(tmp!=0.0f && tmp!=1.0f)
-                error_at_line(EXIT_FAILURE, 0, filename, lineno, "`flip' "
-                              "only takes values of `1' and `0'. You have "
-                              "given `%s'", arg);
+                error_at_line(EXIT_FAILURE, 0, filename, lineno, "'flip' "
+                              "only takes values of '1' and '0'. You have "
+                              "given '%s'", arg);
             }
         }
 
@@ -340,8 +340,8 @@ ui_check_options_and_arguments(struct warpparams *p)
     {
       /* Make sure a HDU is given. */
       if( gal_fits_name_is_fits(p->inputname) && p->cp.hdu==NULL )
-        error(EXIT_FAILURE, 0, "no HDU specified, you can use the `--hdu' "
-              "(`-h') option and give it the HDU number (starting from "
+        error(EXIT_FAILURE, 0, "no HDU specified, you can use the '--hdu' "
+              "('-h') option and give it the HDU number (starting from "
               "zero), or extension name (generally, anything acceptable "
               "by CFITSIO)");
 
@@ -350,11 +350,24 @@ ui_check_options_and_arguments(struct warpparams *p)
                                              NULL, GAL_TYPE_FLOAT64,
                                              p->cp.minmapsize,
                                              p->cp.quietmmap);
+
+      /* Read the WCS and remove one-element wide dimension(s). */
       p->input->wcs=gal_wcs_read(p->inputname, p->cp.hdu, p->hstartwcs,
                                  p->hendwcs, &p->input->nwcs);
       p->input->ndim=gal_dimension_remove_extra(p->input->ndim,
                                                 p->input->dsize,
                                                 p->input->wcs);
+
+      /* Currently Warp only works on 2D images. */
+      if(p->input->ndim!=2)
+        error(EXIT_FAILURE, 0, "input has %zu dimensions but Warp currently "
+              "only works on 2D datasets (images).\n\n"
+              "We do plan to add 3D functionality (see "
+              "https://savannah.gnu.org/task/?15729), so please get in "
+              "touch if you need it (any further interest, support or help "
+              "would be useful)", p->input->ndim);
+
+      /* Get basic WCS information. */
       if(p->input->wcs)
         {
           p->pixelscale=gal_wcs_pixel_scale(p->input->wcs);
@@ -394,9 +407,9 @@ static void
 ui_error_no_warps()
 {
   error(EXIT_FAILURE, 0, "no warping specified, you can either use the "
-        "`--matrix' option for any low-level warp, or specify multipole "
-        "modular warpings with options like `--rotate', `--scale' and etc. "
-        "You can see the full list with the `--help' option");
+        "'--matrix' option for any low-level warp, or specify multipole "
+        "modular warpings with options like '--rotate', '--scale' and etc. "
+        "You can see the full list with the '--help' option");
 }
 
 
@@ -464,13 +477,13 @@ ui_matrix_make_align(struct warpparams *p, double 
*tmatrix)
   /* Make sure the input image had a WCS structure. */
   if(p->input->wcs==NULL)
     error(EXIT_FAILURE, 0, "%s (hdu: %s): no WCS information present, "
-          "hence the `--align' option cannot be used", p->inputname,
+          "hence the '--align' option cannot be used", p->inputname,
           p->cp.hdu);
 
   /* Check if there is only two WCS axises: */
   if(p->input->wcs->naxis!=2)
     error(EXIT_FAILURE, 0, "the WCS structure of %s (hdu: %s) has %d "
-          "axises. For the `--align' option to operate it must be 2",
+          "axises. For the '--align' option to operate it must be 2",
           p->inputname, p->cp.hdu, p->input->wcs->naxis);
 
 
@@ -479,15 +492,17 @@ ui_matrix_make_align(struct warpparams *p, double 
*tmatrix)
   w=p->inwcsmatrix;
 
 
-  /* Lets call the given WCS orientation `W', the rotation matrix we want
-     to find as `X' and the final (aligned matrix) `P' (which is the pixel
+  /* Lets call the given WCS orientation 'W', the rotation matrix we want
+     to find as 'X' and the final (aligned matrix) 'P' (which is the pixel
      scale):
 
+          X            W             P
+        ------       ------      -------
         x0  x1       w0  w1      -P0   0
         x2  x3   *   w2  w3   =   0    P1
 
-     Let's open up the matrix multiplication, so we can find the `X'
-     elements as function of the `W' elements and `a'.
+     Let's open up the matrix multiplication, so we can find the 'X'
+     elements as function of the 'W' elements and 'a'.
 
         x0*w0 + x1*w2 = -P0                                        (1)
         x0*w1 + x1*w3 =  0                                         (2)
@@ -527,6 +542,13 @@ ui_matrix_make_align(struct warpparams *p, double *tmatrix)
       x[2] = 0.0f;
       x[3] = w[3]>0 ? 1.0f : -1.0f;  /* Has to be positive. */
     }
+  else if (w[0]==0.0f && w[3]==0.0f )
+    {
+      x[0] = 0.0f;
+      x[1] = w[1]<0 ? 1.0f : -1.0f;  /* Has to be negative. */
+      x[2] = w[2]>0 ? 1.0f : -1.0f;  /* Has to be positive. */
+      x[3] = 0.0f;
+    }
   else
     {
       A = (w[3]/w[1]) - (w[2]/w[0]);
@@ -534,6 +556,21 @@ ui_matrix_make_align(struct warpparams *p, double *tmatrix)
       x[3] = P[1] / w[1] / A;
       x[0] = -1 * x[1] * w[3] / w[1];
       x[2] = -1 * x[3] * w[2] / w[0];
+
+      /* When the input matrix is flipped, the diagonal elements of the
+         necessary rotation will have a different sign. */
+      if(x[0]*x[3]<0)
+        if(!p->cp.quiet)
+          error(0, 0, "%s (hdu %s): WARNING (bug #55217)! The alignment may "
+                "not be correct! This is a recognized bug, we just haven't "
+                "had enough time to fix it yet, any help or support is "
+                "most welcome.\n\n"
+                "This may be due to the East and North not being left-hand "
+                "oriented. If this is the case, you can fix it by flipping "
+                "the image along the problematic axis (with '--flip=1,0' or "
+                "'--flip=0,1') and re-running Warp with '--align'. Note "
+                "that you can't yet call '--flip' in the same command as "
+                "'--align'",  p->inputname, p->cp.hdu);
     }
 
   /* For a check:
@@ -560,7 +597,7 @@ ui_matrix_make_align(struct warpparams *p, double *tmatrix)
 static void
 ui_matrix_inplacw_multiply(double *in, double *with)
 {
-  /* `tin' will keep the values of the input array because we want to
+  /* 'tin' will keep the values of the input array because we want to
      write the multiplication result in the input array. */
   double tin[9]={in[0],in[1],in[2],in[3],in[4],in[5],in[6],in[7],in[8]};
 
@@ -619,7 +656,7 @@ ui_matrix_from_modular(struct warpparams *p)
       v2 = pop->size>1 ? ((double *)(pop->array))[1] : v1;
 
       /* Depending on the type of the modular warp do it. Recall that the
-         code for the warp, was stored in the `status' element of the data
+         code for the warp, was stored in the 'status' element of the data
          structure.*/
       switch(pop->status)
         {
@@ -674,6 +711,16 @@ ui_matrix_from_modular(struct warpparams *p)
           break;
 
         case UI_KEY_TRANSLATE:
+          /* Translate cannot be called with '--centeroncorner'. */
+          if(p->centeroncorner)
+            error(EXIT_FAILURE, 0, "'--translate' and '--centeroncorner' "
+                  "(which is a type of translation) cannot be called "
+                  "together. To achieve the effect of --centeroncorner, "
+                  "start the warp steps with a translation of 0.5 to move "
+                  "the coordinate center to the corner of a pixel in each "
+                  "dimension");
+
+          /* Build the translation matrix. */
           module[0]=1.0f;       module[1]=0.0f;     module[2]=v1;
           module[3]=0.0f;       module[4]=1.0f;     module[5]=v2;
           module[6]=0.0f;       module[7]=0.0f;     module[8]=1.0f;
@@ -687,7 +734,7 @@ ui_matrix_from_modular(struct warpparams *p)
 
         default:
           error(EXIT_FAILURE, 0, "a bug! the code %d is not recognized as "
-                "a valid modular warp in `ui_matrix_from_modular', this is "
+                "a valid modular warp in 'ui_matrix_from_modular', this is "
                 "not your fault, something in the programming has gone "
                 "wrong. Please contact us at %s so we can correct it",
                 pop->status, PACKAGE_BUGREPORT);
@@ -720,7 +767,7 @@ ui_matrix_center_on_corner(struct warpparams *p)
   /* Translate them back into the proper FITS center. */
   ui_matrix_inplacw_multiply(before, after);
 
-  /* The final matrix is in `before', so put its values into the output
+  /* The final matrix is in 'before', so put its values into the output
      matrix. */
   b = before;
   df = (d=p->matrix->array) + p->matrix->size;
@@ -754,7 +801,7 @@ ui_matrix_finalize(struct warpparams *p)
     if(!isfinite(*d++))
       {
         ui_matrix_print(p->matrix->array);
-        error(EXIT_FAILURE, 0, "%f is not a `normal' number in the "
+        error(EXIT_FAILURE, 0, "%f is not a 'normal' number in the "
               "input matrix shown above", *(d-1));
       }
   while(d<df);
@@ -828,7 +875,7 @@ ui_set_suffix(struct warpparams *p)
   if(p->matrix==NULL && p->modularll==NULL) ui_error_no_warps();
 
   /* We only want the more meaningful suffix when the list is defined AND
-     when its only has one node (the `next' element is NULL). */
+     when its only has one node (the 'next' element is NULL). */
   if(p->matrix==NULL && p->modularll->next==NULL)
     switch(p->modularll->status)
       {
@@ -856,7 +903,7 @@ ui_set_suffix(struct warpparams *p)
       default:
         error(EXIT_FAILURE, 0, "a bug! please contact us at %s so we can "
               "fix the problem. The modular warp code %d is not recognized "
-              "in `ui_set_suffix'", PACKAGE_BUGREPORT, p->modularll->status);
+              "in 'ui_set_suffix'", PACKAGE_BUGREPORT, p->modularll->status);
         return NULL;
       }
   else
@@ -870,7 +917,7 @@ ui_set_suffix(struct warpparams *p)
 static void
 ui_preparations(struct warpparams *p)
 {
-  /* Set the output name. This needs to be done before `ui_finalize_matrix'
+  /* Set the output name. This needs to be done before 'ui_finalize_matrix'
      because that function will free the linked list of modular warpings
      which we will need to determine the suffix if no output name is
      specified. */
@@ -913,9 +960,9 @@ ui_read_check_inputs_setup(int argc, char *argv[], struct 
warpparams *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
+  /* 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
diff --git a/bin/warp/ui.h b/bin/warp/ui.h
index 17df9db..91b9518 100644
--- a/bin/warp/ui.h
+++ b/bin/warp/ui.h
@@ -5,7 +5,7 @@ Warp is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/bin/warp/warp.c b/bin/warp/warp.c
index 68cf7cf..e895d63 100644
--- a/bin/warp/warp.c
+++ b/bin/warp/warp.c
@@ -5,7 +5,7 @@ Warp is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -31,6 +31,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/wcs.h>
 #include <gnuastro/fits.h>
 #include <gnuastro/polygon.h>
+#include <gnuastro/pointer.h>
 
 #include "main.h"
 #include "warp.h"
@@ -74,7 +75,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* Similar to `nearestint_halflower' but:
+/* Similar to 'nearestint_halflower' but:
 
    nearestint_halflower(0.5f) --> 0.0f;
  */
@@ -297,6 +298,7 @@ warp_preparations(struct warpparams *p)
   double is0=p->input->dsize[0], is1=p->input->dsize[1];
 
   double output[8], forarea[8];
+  double *matrix=p->matrix->array;
   double icrn[8]={0,0,0,0,0,0,0,0};
   size_t i, *extinds=p->extinds, dsize[2];
   double xmin=DBL_MAX, xmax=-DBL_MAX, ymin=DBL_MAX, ymax=-DBL_MAX;
@@ -304,17 +306,18 @@ warp_preparations(struct warpparams *p)
   double input[8]={ 0.5f, 0.5f,         is1+0.5f, 0.5f,
                     0.5f, is0+0.5f,     is1+0.5f, is0+0.5f };
 
-  /* Find the range of pixels of the input image. All the input
-     positions are moved to the negative by half a pixel since the
-     center of the pixel is an integer value.*/
+  /* Find the range of pixels of the input image. All the input positions
+     are moved to the negative by half a pixel since the center of the
+     pixel is an integer value.*/
   for(i=0;i<4;++i)
     {
-      mappoint(&input[i*2], (double *)(p->matrix->array), &output[i*2]);
+      mappoint(&input[i*2], matrix, &output[i*2]);
       if(output[i*2]<xmin)     xmin = output[i*2];
       if(output[i*2]>xmax)     xmax = output[i*2];
       if(output[i*2+1]<ymin)   ymin = output[i*2+1];
       if(output[i*2+1]>ymax)   ymax = output[i*2+1];
     }
+
   /* For a check:
   for(i=0;i<4;++i)
       printf("(%.3f, %.3f) --> (%.3f, %.3f)\n",
@@ -324,15 +327,26 @@ warp_preparations(struct warpparams *p)
          xmin, xmax, ymin, ymax);
   */
 
-  /* Set the final size of the image. The X axis is horizontal. The
-     reason we are using the halflower variation of `nearestint' for
-     the maximums is that these points are the farthest extremes of
-     the input image. If they are half a pixel value, they should
-     point to the pixel before. */
+  /* Set the final size of the image. The X axis is horizontal. The reason
+     we are using the halflower variation of 'nearestint' for the maximums
+     is that these points are the farthest extremes of the input image. If
+     they are half a pixel value, they should point to the pixel before. */
   dsize[1]=nearestint_halflower(xmax)-nearestint_halfhigher(xmin)+1;
   dsize[0]=nearestint_halflower(ymax)-nearestint_halfhigher(ymin)+1;
   p->outfpixval[0]=nearestint_halfhigher(xmin);
   p->outfpixval[1]=nearestint_halfhigher(ymin);
+
+  /* If we have translation, the 'dsize's and 'outfpixval's should be
+     corrected. Note that centeroncorner is also a translation operation,
+     but in that scenario, we don't want this feature! */
+  if( p->centeroncorner==0 && (matrix[2]!=0.0f || matrix[5]!=0.0f) )
+    {
+      dsize[1] += abs( (int)(matrix[2]) )+1; /* (int): avoid warnings. */
+      dsize[0] += abs( (int)(matrix[5]) )+1;
+      if(xmin>0) p->outfpixval[0]=0;
+      if(ymin>0) p->outfpixval[1]=0;
+    }
+
   /* For a check:
   printf("Wrapped:\n");
   printf("dsize [C]: (%zu, %zu)\n", dsize[0], dsize[1]);
@@ -362,7 +376,7 @@ warp_preparations(struct warpparams *p)
 
 
   /* Order the transformed output pixel. */
-  gal_polygon_ordered_corners(icrn, 4, p->ordinds);
+  gal_polygon_vertices_sort_convex(icrn, 4, p->ordinds);
 
 
   /* Find the area of the output pixel in units of the input pixel,
@@ -416,7 +430,7 @@ correct_wcs_save_output(struct warpparams *p)
   gal_fits_list_key_t *headers=NULL;
   double *crpix=wcs?wcs->crpix:NULL, *w=p->inwcsmatrix;
 
-  /* `tinv' is the 2 by 2 inverse matrix. Recall that `p->inverse' is 3 by
+  /* 'tinv' is the 2 by 2 inverse matrix. Recall that 'p->inverse' is 3 by
      3 to account for homogeneous coordinates. */
   double tinv[4]={p->inverse[0]/p->inverse[8], p->inverse[1]/p->inverse[8],
                   p->inverse[3]/p->inverse[8], p->inverse[4]/p->inverse[8]};
@@ -450,7 +464,7 @@ correct_wcs_save_output(struct warpparams *p)
 
       /* Due to floating point errors extremely small values of PC matrix
          can be set to zero and extremely small differences between PC1_1
-         and PC2_2 can be ignored. The reason for all the `fabs' functions
+         and PC2_2 can be ignored. The reason for all the 'fabs' functions
          is because the signs are usually different.*/
       if( fabs(wcs->pc[1])<ABSOLUTEFLTERROR ) wcs->pc[1]=0.0f;
       if( fabs(wcs->pc[2])<ABSOLUTEFLTERROR ) wcs->pc[2]=0.0f;
@@ -466,7 +480,7 @@ correct_wcs_save_output(struct warpparams *p)
       sprintf(&keyword[i*FLEN_KEYWORD], "WMTX%zu_%zu", i/3+1, i%3+1);
       gal_fits_key_list_add_end(&headers, GAL_TYPE_FLOAT64,
                                 &keyword[i*FLEN_KEYWORD], 0, &m[i], 0,
-                                "Warp matrix element value", 0, NULL);
+                                "Warp matrix element value", 0, NULL, 0);
     }
 
   /* Save the output into the proper type and write it. */
@@ -507,6 +521,7 @@ warp(struct warpparams *p)
 {
   int err;
   pthread_t t;          /* All thread ids saved in this, not used. */
+  char *mmapname;
   pthread_attr_t attr;
   pthread_barrier_t b;
   struct iwpparams *iwp;
@@ -527,7 +542,9 @@ warp(struct warpparams *p)
 
 
   /* Distribute the output pixels into the threads: */
-  gal_threads_dist_in_threads(p->output->size, nt, &indexs, &thrdcols);
+  mmapname=gal_threads_dist_in_threads(p->output->size, nt,
+                                       p->cp.minmapsize, p->cp.quietmmap,
+                                       &indexs, &thrdcols);
 
 
   /* Start the warp. */
@@ -569,10 +586,13 @@ warp(struct warpparams *p)
 
   /* Save the output. */
   correct_wcs_save_output(p);
+  if(!p->cp.quiet)
+    printf(" Output: %s\n", p->cp.output);
 
 
-  /* Free the allocated spaces: */
+  /* Free the allocated spaces, note that 'indexs' may be memory-mapped. */
   free(iwp);
-  free(indexs);
   gal_data_free(p->output);
+  if(mmapname) gal_pointer_mmap_free(&mmapname, p->cp.quietmmap);
+  else         free(indexs);
 }
diff --git a/bin/warp/warp.h b/bin/warp/warp.h
index 504493c..acfa30e 100644
--- a/bin/warp/warp.h
+++ b/bin/warp/warp.h
@@ -5,7 +5,7 @@ Warp is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/bootstrap.conf b/bootstrap.conf
index a298ac2..bbf7318 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -1,24 +1,16 @@
 # Bootstrap configuration for GNU Astronomy Utilities
 #
-# This file will be read by the ./bootstrap script and everything that
-# is defined here will be replaced with the variables defined
-# there. ./bootstrap is a script made by Gnulib in order to run Gnulib
-# and any other operation that is necessary to do on the version
-# controlled source prior to running ./configure.
-#
-# IMPORTANT NOTE(s) FOR GNUASTRO:
-#
-#    - We have separated the variables defined here into three
-#      categories as clearly seen below. The main reason is that if
-#      Gnulib's template bootstrap.conf ever changes in the future, we
-#      know exactly which parts we have modified for Gnuastro and so,
-#      we can easily update this template.
+# This file will be read by the './bootstrap' script and everything that is
+# defined here will be replaced with the variables defined
+# there. ./bootstrap is a script made by Gnulib in order to run Gnulib and
+# any other operation that is necessary to do on the version controlled
+# source prior to running ./configure.
 #
 # Original author:
 #     Mosè Giordano <mose@gnu.org>
 # Contributing author(s):
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
-# Copyright (C) 2016-2019 Free Software Foundation, Inc.
+# Copyright (C) 2016-2021, Free Software Foundation, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -58,12 +50,17 @@
 # necessary in autoreconf. Recall that we have to satisfy the GNU
 # Coding Standards which require this file's existence and autoreconf
 # will complain if its not here.
-./genauthors ./
+if ./genauthors ./ ; then
+  echo "AUTHORS file created."
+else
+  echo "AUTHORS file NOT created."
+  exit 1
+fi
 
 
 
 # Extra options we want to have added to the execution of gnulib-tool
-# for Gnuastro. See the `gnulib_tool_options' variable in bootstrap
+# for Gnuastro. See the 'gnulib_tool_options' variable in bootstrap
 # for the default options that will be used and as a template.
 gnulib_tool_option_extras="\
  --with-tests\
@@ -98,7 +95,7 @@ gnulib_name="libgnu"
 bootstrap_post_import_hook()
 {
   # List of m4 files we need from the GNU Autoconf Archives (excluding the
-  # `ax_' prefix and `.m4' suffix):
+  # 'ax_' prefix and '.m4' suffix):
   neededm4s="pthread compare_version check_compile_flag"
 
   # Get the necesssary Autoconf-archive macros. It is assumed that
@@ -110,11 +107,11 @@ bootstrap_post_import_hook()
       cp "$GNULIB_SRCDIR/../autoconf-archive/m4/ax_$m4name.m4" $m4_base/
     done
   else
-    # Choose the program to use to download files from the web.  `curl' is
-    # present in most GNU/Linux systems and OS X as well, `wget' is usually
-    # present only on GNU/Linux systems.  Since `curl' is available in more
+    # Choose the program to use to download files from the web.  'curl' is
+    # present in most GNU/Linux systems and OS X as well, 'wget' is usually
+    # present only on GNU/Linux systems.  Since 'curl' is available in more
     # systems, we first try if it's installed, otherwise we fallback on
-    # `wget'.
+    # 'wget'.
     if type curl > /dev/null; then
       downloader="curl -o"
     else
@@ -126,7 +123,20 @@ bootstrap_post_import_hook()
     done
   fi
 
-  # Add a value pointer to `argp_option' for easy setting of option values.
+  # With Autoconf 2.70, the 'as_echo' has been depreciated and will cause
+  # an error with autoreconf. But unfortunately the ax_pthread test still
+  # uses it. So until it is fixed there, we need to manually correct it
+  # here.
+  sed -e's|\$as_echo \"\$ac_link\"|AS_ECHO([\"\$ac_link\"])|' \
+      $m4_base/ax_pthread.m4 > $m4_base/ax_pthread_tmp.m4
+  mv $m4_base/ax_pthread_tmp.m4 $m4_base/ax_pthread.m4
+
+  # Hack in 'AC_LIB_HAVE_LINKFLAGS' so it doesn't search for shared
+  # libraries when '--disable-shared' is used.
+  sed 's|if test -n \"$acl_shlibext\"; then|if test -n \"\$acl_shlibext\" -a 
\"X$enable_shared\" = \"Xyes\"; then|' bootstrapped/m4/lib-link.m4 > 
bootstrapped/m4/lib-link_tmp.m4
+  mv bootstrapped/m4/lib-link_tmp.m4 bootstrapped/m4/lib-link.m4
+
+  # Add a value pointer to 'argp_option' for easy setting of option values.
   awk '{                                                                    \
          if($1=="struct" && $2=="argp_option")                              \
            inargp=1;                                                        \
@@ -183,25 +193,6 @@ bootstrap_post_import_hook()
 
 
 
-# Run other necessary operations after all the default bootstrap
-# operations are complete.
-bootstrap_epilogue()
-{
-  # Prepare all the figure formats for the various manual formats.
-  cd doc/plotsrc
-  make
-  cd ../../
-}
-
-
-
-
-
-
-
-
-
-
 # Configuration variable values for Gnuastro
 # ==========================================
 #
@@ -211,7 +202,6 @@ bootstrap_epilogue()
 
 # gnulib modules used by this package.
 gnulib_modules="
-    fdl
     func
     math
     argp
diff --git a/bootstrapped/README b/bootstrapped/README
index 73adb0f..a6c4f17 100644
--- a/bootstrapped/README
+++ b/bootstrapped/README
@@ -1,7 +1,7 @@
 Imported files to GNU Astronomy Utilities
 =========================================
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2020 Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
 The contents of this directory (except this README file!) are imported
@@ -48,7 +48,7 @@ operating systems.
 Copyright information
 ---------------------
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2020 Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
diff --git a/configure.ac b/configure.ac
index d3b48c6..173763d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,7 +12,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019, Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, 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
@@ -50,7 +50,7 @@ AC_CONFIG_MACRO_DIRS([bootstrapped/m4])
 
 # Library version, see the GNU Libtool manual ("Library interface versions"
 # section for the exact definition of each) for
-GAL_CURRENT=9
+GAL_CURRENT=13
 GAL_REVISION=0
 GAL_AGE=0
 GAL_LT_VERSION="${GAL_CURRENT}:${GAL_REVISION}:${GAL_AGE}"
@@ -65,6 +65,8 @@ AC_SUBST(GAL_LT_VERSION)
 : ${CXXFLAGS=""}
 AC_PROG_CC
 AC_PROG_CXX
+AC_PROG_AWK
+AC_PROG_SED
 gl_EARLY
 AM_PROG_AR
 LT_INIT
@@ -75,10 +77,10 @@ LT_INIT
 
 # This macro will let the libraries know that we are now in the Gnuastro
 # build system, not on the user's system. While we are building Gnuastro,
-# we have the important installation information in `config.h'. But in the
+# we have the important installation information in 'config.h'. But in the
 # user's own programs, this information is defined in
-# `gnuastro/config.h'. With this macro, the installed headers can decide
-# if the latter should be included or not. Note that `gnuastro/config.h'
+# 'gnuastro/config.h'. With this macro, the installed headers can decide
+# if the latter should be included or not. Note that 'gnuastro/config.h'
 # is only built at installation time and doesn't exist when building
 # Gnuastro. Therefore, this macro must not be defined in a user's program.
 AC_DEFINE([IN_GNUASTRO_BUILD], [1], [In building, not usage])
@@ -87,12 +89,12 @@ AC_DEFINE([IN_GNUASTRO_BUILD], [1], [In building, not 
usage])
 
 
 
-# See if `make check' should be made with Valgrind. This should be done
+# See if 'make check' should be made with Valgrind. This should be done
 # before checking the compilation flags because it can re-write
-# `enable-debug').
+# 'enable-debug').
 AC_ARG_ENABLE(check-with-valgrind,
               [AS_HELP_STRING([--enable-check-with-valgrind],
-                              [Run `make check' programs within Valgrind])],
+                              [Run 'make check' programs within Valgrind])],
               [AS_IF([test "x$enable_check_with_valgrind" != xno],
                      [enable_check_with_valgrind=yes])],
               [enable_check_with_valgrind=no])
@@ -101,7 +103,7 @@ AS_IF([test "x$enable_check_with_valgrind" = "xyes"],
         enable_debug=yes;
         AC_CHECK_PROG(has_valgrind, valgrind, [yes], [no])
         AS_IF([test "x$has_valgrind" = "xno"],
-              [AC_MSG_ERROR([Valgrind not found. Please install it or don't 
use `--enable-check-with-valgrind'])])
+              [AC_MSG_ERROR([Valgrind not found. Please install it or don't 
use '--enable-check-with-valgrind'])])
       ])
 AM_CONDITIONAL([COND_CHECK_WITH_VALGRIND], [test 
"x$enable_check_with_valgrind" = "xyes"])
 
@@ -125,15 +127,15 @@ CXXFLAGS="-Wall $cflags_add $CXXFLAGS"
 
 
 
-# See if the C++ compiler was present. `CXX' has already been set by
-# `AC_PROG_CXX' (above). According to the Autoconf manual: "if none of the
-# [AC_PROG_CXX] checks succeed, then as a last resort [it will] set `CXX'
-# to `g++'". Therefore, we can't rely on it to see if the compiler
+# See if the C++ compiler was present. 'CXX' has already been set by
+# 'AC_PROG_CXX' (above). According to the Autoconf manual: "if none of the
+# [AC_PROG_CXX] checks succeed, then as a last resort [it will] set 'CXX'
+# to 'g++'". Therefore, we can't rely on it to see if the compiler
 # executable is actually usable. Therefore tests that rely on it will
-# fail. Unfortunately some OSs (like Fedora), don't install `g++' with
-# `gcc', so it may not always be present. To fix this, here we are just
-# using `AC_CHECK_PROG' to see if the `CXX' executable is reachable in the
-# search path and if it isn't, we'll disable the C++ check during `make
+# fail. Unfortunately some OSs (like Fedora), don't install 'g++' with
+# 'gcc', so it may not always be present. To fix this, here we are just
+# using 'AC_CHECK_PROG' to see if the 'CXX' executable is reachable in the
+# search path and if it isn't, we'll disable the C++ check during 'make
 # check' with an Automake conditional.
 AC_CHECK_PROG(has_cxx, $CXX, "yes", "no")
 AM_CONDITIONAL([COND_HASCXX], [test "x$has_cxx" = "xyes"])
@@ -144,7 +146,7 @@ AM_CONDITIONAL([COND_HASCXX], [test "x$has_cxx" = "xyes"])
 
 # Check for pthreads and add the appropriate compilation flags. AX_PTHREAD
 # comes from the GNU Autoconf Archive's ax_pthread.m4, see there for the
-# documentation. Note that
+# documentation.
 AX_PTHREAD([],[AC_MSG_ERROR([AC_PACKAGE_NAME Needs POSIX Threads (pthread)])])
 CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
 CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
@@ -155,13 +157,14 @@ CC="$PTHREAD_CC"
 
 
 
-# See if the C++ compiler understands `-Qunused-arguments'. AX_PTHREAD adds
-# puts this option in `PTHREAD_CFLAGS' when the C compiler knows this
+# See if the C++ compiler understands '-Qunused-arguments'. AX_PTHREAD adds
+# puts this option in 'PTHREAD_CFLAGS' when the C compiler knows this
 # option. We then pass it to CFLAGS and CXXFLAGS above. But as reported in
 # bug #52490, it can happen that sometimes, the C++ compiler doesn't
 # recognize it. So we need to do a separate check for C++.
 cxxflags_tmp=
-for flg in $CXXFLAGS; do
+for flg in $CXXFLAGS
+do
   AS_IF([test "$flg" = \-Qunused-arguments],
         [ AC_LANG(C++)
           AX_CHECK_COMPILE_FLAG([-Qunused-arguments],
@@ -177,7 +180,7 @@ CXXFLAGS="$cxxflags_tmp"
 
 
 
-# Check if `malloc(0)' returns valid pointer
+# Check if 'malloc(0)' returns valid pointer
 AC_FUNC_MALLOC
 
 
@@ -209,9 +212,9 @@ anywarnings=no
 
 
 
-# Remove any occurance of the current directory `./', `.', or the full
+# Remove any occurance of the current directory './', '.', or the full
 # address of the current directory in PATH. The main problem is the
-# `libtool' executable which Gnuastro builds internally in the top build
+# 'libtool' executable which Gnuastro builds internally in the top build
 # directory. However, we also need to know if the system has libtool or
 # not.
 AC_MSG_CHECKING(if PATH contains current directory)
@@ -219,21 +222,21 @@ oldPATH=$PATH
 currpwd=$(pwd)
 
 # The first call to SED will remove any occurance of the current directory:
-# `./', `.', or the full address.
+# './', '.', or the full address.
 #
-#    NOTE 1: We cannot simply remove all `.'s, because hidden directories
-#            (like the `~/.local' that is suggested for local
+#    NOTE 1: We cannot simply remove all '.'s, because hidden directories
+#            (like the '~/.local' that is suggested for local
 #            installations) will also be altered.
 #
-#    NOTE 2: An empty string in the list of strings (separated by `:')
-#            means the current directory. This includes cases like: `::',
-#            or a leading and trailing `:'. So after all the removals of
+#    NOTE 2: An empty string in the list of strings (separated by ':')
+#            means the current directory. This includes cases like: '::',
+#            or a leading and trailing ':'. So after all the removals of
 #            the current directory, we will remove all such cases.
 #
-#    NOTE 3: The SED separator can be any character immediately after `s',
-#            it doesn't just have to be the commonly used `/'. Since `$pwd'
-#            will possibly contain many `/'s, it is much more easier to use
-#            a differen separator (`|' in this call to SED).
+#    NOTE 3: The SED separator can be any character immediately after 's',
+#            it doesn't just have to be the commonly used '/'. Since '$pwd'
+#            will possibly contain many '/'s, it is much more easier to use
+#            a differen separator ('|' in this call to SED).
 PATH=$(AS_ECHO([$PATH]) | $SED -e 's|'"$currpwd"'||g' \
                                -e 's|\.\.*//*||g'     \
                                -e 's|:\.\.*:|:|g'     \
@@ -274,21 +277,70 @@ missing_optional_lib=no
 # Keep the original LIBS to re-set in the end.
 orig_LIBS="$LIBS"
 
-# Order is important here.
+
+
+
+
+# Basics of the library linking checks
+# ------------------------------------
+#
+# Outputs of 'AC_LIB_HAVE_LINKFLAGS' (which checks for libraries):
+#
+#   - LIB<NAME>: contains the raw 'libNAME.so' or 'libNAME.a' file for some
+#     libraries. This is necessary for static libraries, but is problematic
+#     for shared libraries (they will not be linked to the produced
+#     library: when you run 'ldd libgnuastro.so', you don't see those that
+#     were linked with a '.so' file here).
+#
+#     To make things worse, when linking Gnuastro's programs with
+#     Gnuastro's library, libtool will put raw files ('.a' or '.so' files,
+#     not '-lNAME') before 'libgnuastro.la' (which depends on them). As a
+#     result, building shared programs with Gnuastro's library will
+#     fail. But this is desired for static libraries.
+#
+#   - LTLIB<NAME>: has '-lNAME', along with any necessary RPATH flags for
+#     the local operating system.
+#
+# Major environment variables:
+#
+#   - LIBS: is primarily used in the compilation checks of the configure
+#     script where the host's compiler has full control.
+#
+#   - LDADD: is passed to the rules that build Gnuastro's library and
+#     programs through the 'CONFIG_LDADD' variable.
+#
+# Since nothing complex is built during the configure script, we'll just
+# populate 'LIBS' with 'LTLIB<NAME>'. However, building the programs and
+# library is very complex and we have an even more complex BuildProgram in
+# Gnaustro. So we fill 'LDADD' conditionally: 1) for building static
+# library/programs, we'll use 'LIB<NAME>'. 2) for building shared
+# libraries, we'll use 'LTLIB<NAME>'.
+
+
+# Why C math library (for things like 'log')? Even though '-lm' is also
+# found for GSL below, on some systems (reported on Ubuntu), if we don't
+# add it explicitly here, the build will crash because of a failure to link
+# with the math functions.
 AC_LIB_HAVE_LINKFLAGS([m], [], [#include <math.h>])
-AS_IF([test "x$LIBM" = x],
-      [missing_mandatory=yes; has_cmath=no],
-      [LDADD="$LIBM $LDADD"; LIBS="$LIBM $LIBS"])
+AS_IF([test "x$LIBM" = x], [],
+      [LIBS="$LIBM $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBM   $LDADD"],
+             [LDADD="$LTLIBM $LDADD"]) ])
+
 
 AC_LIB_HAVE_LINKFLAGS([gsl], [gslcblas], [
 #include <gsl/gsl_rng.h>
 void junk(void) { gsl_rng_env_setup(); } ])
 AS_IF([test "x$LIBGSL" = x],
       [missing_mandatory=yes; has_gsl=no; has_gslcblas=no],
-      [LDADD="$LTLIBGSL $LDADD"; LIBS="$LIBGSL $LIBS"])
+      [LIBS="$LIBGSL $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBGSL   $LDADD"],
+             [LDADD="$LTLIBGSL $LDADD"]) ])
 
 
-# Since version 0.42, if `libcurl' is installed, CFITSIO will link with it
+# Since version 0.42, if 'libcurl' is installed, CFITSIO will link with it
 # and thus it will be necessary to explicitly link with libcurl also. If it
 # doesn't exist on the system, then CFITSIO won't link with it and there is
 # no problem for Gnuastro either. So there is no need to stop the configure
@@ -301,21 +353,33 @@ AS_IF([test "x$LIBGSL" = x],
 # librtmp, libldap). So if you intend to make Gnuastro statically, then
 # build Libcurl in static-only mode so you won't have to check for all
 # these extra libraries here.
+#
+# Similarly, if the Bzip2 library was activated when building CFITSIO, it
+# will be necessary in a static build to CFITSIO.
 AC_LIB_HAVE_LINKFLAGS([z], [], [#include <zlib.h>])
 AS_IF([test "x$LIBZ" = x], [],
-      [LDADD="$LTLIBZ $LDADD"; LIBS="$LIBZ $LIBS"])
+      [LIBS="$LIBZ $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBZ   $LDADD"],
+             [LDADD="$LTLIBZ $LDADD"]) ])
+
+
+AC_LIB_HAVE_LINKFLAGS([bz2], [], [#include <bzlib.h>])
+AS_IF([test "x$LIBBZ2" = x], [],
+      [LIBS="$LIBBZ2 $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBBZ2   $LDADD"],
+             [LDADD="$LTLIBBZ2 $LDADD"]) ])
 
 
 AC_LIB_HAVE_LINKFLAGS([curl], [], [#include <curl/curl.h>])
 AS_IF([test "x$LIBCURL" = x], [],
-      [LDADD="$LTLIBCURL $LDADD"; LIBS="$LIBCURL $LIBS"])
+      [LIBS="$LIBCURL $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBCURL   $LDADD"],
+             [LDADD="$LTLIBCURL $LDADD"]) ])
 
 
-# Older versions of CFITSIO don't install a shared library, only a static
-# one. Eventhough we add `-lm' to LDADD, on some systems, it complains
-# about not finding basic math libraries. Therefore the configure script
-# can't find CFITSIO, eventhough it exists. The solution is to manually add
-# the math-library as a dependency of CFITSIO.
 AC_LIB_HAVE_LINKFLAGS([cfitsio], [], [
 #include <fitsio.h>
 void junk(void) {
@@ -324,7 +388,10 @@ fitsfile *f;
 ffopen(&f, "junk", READONLY, &status);} ])
 AS_IF([test "x$LIBCFITSIO" = x],
       [missing_mandatory=yes; has_cfitsio=no],
-      [LDADD="$LTLIBCFITSIO $LDADD"; LIBS="$LIBCFITSIO $LIBS"])
+      [LIBS="$LIBCFITSIO $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBCFITSIO   $LDADD"],
+             [LDADD="$LTLIBCFITSIO $LDADD"]) ])
 
 
 AC_LIB_HAVE_LINKFLAGS([wcs], [], [
@@ -337,7 +404,10 @@ wcspih(header, 1, 0, 0, &nreject, &nwcs, &wcs);
 } ])
 AS_IF([test "x$LIBWCS" = x],
       [missing_mandatory=yes; has_wcslib=no],
-      [LDADD="$LTLIBWCS $LDADD"; LIBS="$LIBWCS $LIBS"])
+      [LIBS="$LIBWCS $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBWCS   $LDADD"],
+             [LDADD="$LTLIBWCS $LDADD"]) ])
 
 
 AC_ARG_WITH([libjpeg],
@@ -355,15 +425,13 @@ void junk(void) {
 } ]) ])
 AS_IF([test "x$LIBJPEG" = x],
       [missing_optional_lib=yes; has_libjpeg=no; anywarnings=yes],
-      [LDADD="$LTLIBJPEG $LDADD"; LIBS="$LIBJPEG $LIBS"])
+      [LIBS="$LIBJPEG $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBJPEG   $LDADD"],
+             [LDADD="$LTLIBJPEG $LDADD"]) ])
 AM_CONDITIONAL([COND_HASLIBJPEG], [test "x$has_libjpeg" = "xyes"])
 
 
-# Check libtiff: If an LZMA libray (part of the XZ Utils) is present,
-# libtiff has probably been built with it. So we'll also need to link with
-# the LZMA library. But if libtiff hasn't been linked with it and its
-# present, there is no problem, the linker will just pass over it. So we
-# don't need to stop the build if this fails.
 AC_ARG_WITH([libtiff],
             [AS_HELP_STRING([--without-libtiff],
                             [disable support for libtiff])],
@@ -374,16 +442,16 @@ AS_IF([test "x$with_libtiff" != xno],
 #include <tiffio.h>
 void junk(void) {TIFF *tif=TIFFOpen("junk", "r");} ])
       ])
-AS_IF([test "x$LIBLZMA" = x], [],
-      [LDADD="$LTLIBLZMA $LDADD"; LIBS="$LIBLZMA $LIBS"])
 AS_IF([test "x$LIBTIFF" = x],
       [missing_optional_lib=yes; has_libtiff=no; anywarnings=yes],
-      [LDADD="$LTLIBTIFF $LDADD"; LIBS="$LIBTIFF $LIBS"])
+      [LIBS="$LIBTIFF $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBTIFF   $LDADD"],
+             [LDADD="$LTLIBTIFF $LDADD"]) ])
 AM_CONDITIONAL([COND_HASLIBTIFF], [test "x$has_libtiff" = "xyes"])
 
 
-# Check libgit2. Note that very old versions of libgit2 don't have the
-# `git_libgit2_init' function.
+# libgit2 (very old versions of libgit2 don't have the 'git_libgit2_init').
 AC_ARG_WITH([libgit2],
             [AS_HELP_STRING([--without-libgit2],
                             [disable support for libgit2])],
@@ -395,12 +463,31 @@ void junk(void) {git_libgit2_init();} ])
       ])
 AS_IF([test "x$LIBGIT2" = x],
       [missing_optional_lib=yes; has_libgit2=0],
-      [LDADD="$LTLIBGIT2 $LDADD"; LIBS="$LIBGIT2 $LIBS"])
+      [LIBS="$LIBGIT2 $LIBS"
+       AS_IF([ test "x$enable_shared" = "xno" ],
+             [LDADD="$LIBGIT2   $LDADD"],
+             [LDADD="$LTLIBGIT2 $LDADD"]) ])
 AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_LIBGIT2], [$has_libgit2],
                    [libgit2 is installed on the system])
 AS_IF([test "x$has_libgit2" = "x1"], [], [anywarnings=yes])
 
 
+# End of library linking list
+# ---------------------------
+#
+# If we are in static mode, convert any string ending in 'libpthread.a' to
+# '-lpthread' (because pthread is part of the C library and atleast in
+# Gnuastro's usage so far, can't be statically linked (gives errors on
+# undefined symbols like '_dl_pagesize' or '_dl_init_static_tls').
+AS_IF([test "x$enable_shared" = "xno"],
+      [ LDADD=$(AS_ECHO(["$LDADD"]) \
+           | $AWK '{for(i=1; i<=NF; ++i) { \
+                      if( $i ~ /libpthread.a/ ) \
+                        $i="-lpthread" } \
+                    print $0}')
+      ])
+
+
 
 
 
@@ -409,12 +496,12 @@ AS_IF([test "x$has_libgit2" = "x1"], [], 
[anywarnings=yes])
 #
 # Once we know that a library exsits, we need to check if it has some
 # features or not. This must be done _after_ checking the existance of
-# _all_ the libraries, because they may add elements to `LIBS'/`LDADD' that
+# _all_ the libraries, because they may add elements to 'LIBS'/'LDADD' that
 # causes possibly different versions of the libraries to be read.
 
-# GSL's `gsl_interp_steffen' isn't a function. So we'll need to use
-# `AC_LINK_IFELSE'. However, AC_LINK_IFELSE doesn't use `LDADD', so we'll
-# have to temporarily add `LDADD' to LIBS, then set it back to the
+# GSL's 'gsl_interp_steffen' isn't a function. So we'll need to use
+# 'AC_LINK_IFELSE'. However, AC_LINK_IFELSE doesn't use 'LDADD', so we'll
+# have to temporarily add 'LDADD' to LIBS, then set it back to the
 # original.
 AC_MSG_CHECKING(if GSL supports Steffen splines)
 AC_LINK_IFELSE([AC_LANG_PROGRAM(
@@ -426,7 +513,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM(
                 gsl_version_old=yes; anywarnings=yes;])
 
 
-# If the CFITSIO library has the `fits_is_reentrant' function (it was added
+# If the CFITSIO library has the 'fits_is_reentrant' function (it was added
 # since version 3.30 of April 2012).
 AC_CHECK_LIB([cfitsio], [fits_is_reentrant], [has_fits_is_reentrant=1],
              [has_fits_is_reentrant=0], [-lm])
@@ -436,7 +523,7 @@ AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_FITS_IS_REENTRANT],
 AC_SUBST(HAVE_FITS_IS_REENTRANT, [$has_fits_is_reentrant])
 
 
-# If the WCS library has the `wcslib_version' function.
+# If the WCS library has the 'wcslib_version' function.
 AC_CHECK_LIB([wcs], [wcslib_version], [has_wcslib_version=1],
              [has_wcslib_version=0], [-lcfitsio -lm])
 AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_WCSLIB_VERSION], [$has_wcslib_version],
@@ -444,7 +531,32 @@ AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_WCSLIB_VERSION], 
[$has_wcslib_version],
 AC_SUBST(HAVE_WCSLIB_VERSION, [$has_wcslib_version])
 
 
-# If the pthreads library has `pthread_barrier_wait'.
+# If the WCS library supports distortion
+AC_CHECK_HEADER([wcslib/dis.h], [has_wcslib_dis_h=1],
+                [has_wcslib_dis_h=0; anywarnings=yes])
+AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_WCSLIB_DIS_H], [$has_wcslib_dis_h],
+                   [WCSLIB has distortion header in dis.h])
+AC_SUBST(HAVE_WCSLIB_DIS_H, [$has_wcslib_dis_h])
+AM_CONDITIONAL([COND_HASWCSDIS_H], [test "x$has_wcslib_dis_h" = "x1"])
+
+
+# If the WCS library has the 'mjdref' element.
+AC_CHECK_MEMBER([struct wcsprm.mjdref], [has_wcslib_mjdref=1],
+                [has_wcslib_mjdref=0], [[#include <wcslib/wcs.h>]])
+AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_WCSLIB_MJDREF], [$has_wcslib_mjdref],
+                   [WCSLIB comes with wcsprm.mjdref])
+AC_SUBST(HAVE_WCSLIB_MJDREF, [$has_wcslib_mjdref])
+
+
+# If the WCS library has the OBSFIX macro.
+AC_CHECK_DECL(OBSFIX, [has_wcslib_obsfix=1],
+              [has_wcslib_obsfix=0], [[#include <wcslib/wcsfix.h>]])
+AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_WCSLIB_OBSFIX], [$has_wcslib_obsfix],
+                   [WCSLIB comes with OBSFIX macro])
+AC_SUBST(HAVE_WCSLIB_OBSFIX, [$has_wcslib_obsfix])
+
+
+# If the pthreads library has 'pthread_barrier_wait'.
 AC_CHECK_LIB([pthread], [pthread_barrier_wait], [has_pthread_barrier=1],
              [has_pthread_barrier=0])
 AC_DEFINE_UNQUOTED([GAL_CONFIG_HAVE_PTHREAD_BARRIER], [$has_pthread_barrier],
@@ -462,6 +574,10 @@ AC_SUBST(HAVE_PTHREAD_BARRIER, [$has_pthread_barrier])
 AC_CHECK_PROG(has_help2man, help2man, [yes], [no])
 AM_CONDITIONAL([COND_HASHELP2MAN], [test "x$has_help2man" = "xyes"])
 
+# cURL:
+AC_CHECK_PROG(has_curl, curl, [yes], [no])
+AS_IF([test "x$has_curl" = "xno"], [anywarnings=yes])
+
 # Check the libtool executable on the system. Note that Gnuastro also ships
 # with a version of Libtool. We don't want Gnuastro's Libtool, here we want
 # to see if the system has libtool independent of Gnuastro so BuildProgram
@@ -479,8 +595,8 @@ AS_IF([test "x$has_libtool" = "xyes"],
         AC_MSG_RESULT( $has_gnulibtool )],
       [ has_gnulibtool=no ])
 
-# When either the `libtool' executable isn't GNU or it doesn't exist, then
-# look for `glibtool'.
+# When either the 'libtool' executable isn't GNU or it doesn't exist, then
+# look for 'glibtool'.
 AS_IF([test "x$has_gnulibtool" = "xyes"],
       [ gnulibtool_exec=libtool ],
       [ AC_CHECK_PROG(has_glibtool, glibtool, [yes], [no])
@@ -488,10 +604,10 @@ AS_IF([test "x$has_gnulibtool" = "xyes"],
                [has_gnulibtool=yes; gnulibtool_exec=glibtool],
                [has_gnulibtool=no; anywarnings=yes] ) ])
 
-# Older versions of GNU Libtool have problems with the `dash' shell (a
-# minimalist shell) and will crash (due to not having the `+=' operator),
+# Older versions of GNU Libtool have problems with the 'dash' shell (a
+# minimalist shell) and will crash (due to not having the '+=' operator),
 # see bug #54430. So we need to check if the system's libtool and shell
-# (called by `sh' as in C's `system' function within BuildProgram) can work
+# (called by 'sh' as in C's 'system' function within BuildProgram) can work
 # with each other. If not, we need to search for Bash and tell BuildProgram
 # to use Bash instead of the default. But older versions of Bash also don't
 # support this operator, so we'll have to check with Bash is well.
@@ -509,7 +625,7 @@ AS_IF([test "x$has_gnulibtool" = "xyes"],
         ltargs="--quiet --tag=CC --mode=link $CC $cprog -O3 -o $outname"
 
         # Check the shells, starting with known shells and ultimately
-        # trying with `sh' (can be any shell).
+        # trying with 'sh' (can be any shell).
         AS_IF([test "x$has_bash" = "xyes"],
               [AS_IF(bash -c "$gnulibtool_exec $ltargs" > /dev/null 2>&1,
                      [libtool_shell="bash"],
@@ -522,7 +638,7 @@ AS_IF([test "x$has_gnulibtool" = "xyes"],
 
         # Clean up: note that no output might have been generated (when no
         # proper shell was found). Therefore, for deleting the output file,
-        # we'll call `rm' with `-f' so it doesn't complain with an error in
+        # we'll call 'rm' with '-f' so it doesn't complain with an error in
         # such cases.
         rm $cprog
         rm -f $outname
@@ -564,8 +680,8 @@ AS_IF([test "x$has_ghostscript" = "xyes"],
        gsversion=$(gs --version)
        AX_COMPARE_VERSION([9.10], [gt], [$gsversion], [has_ghostscript=no])
        AC_MSG_RESULT( $gsversion )])
-# Note: `has_ghostscript' can be set to `no' within the AS_IF above, so
-# `anywarnings' cannot be an [RUN-IF-FALSE] argument to the AS_IF above.
+# Note: 'has_ghostscript' can be set to 'no' within the AS_IF above, so
+# 'anywarnings' cannot be an [RUN-IF-FALSE] argument to the AS_IF above.
 AS_IF([test "x$has_ghostscript" = "xno"], [anywarnings=yes])
 AM_CONDITIONAL([COND_HASGHOSTSCRIPT], [test "x$has_ghostscript" = "xyes"])
 
@@ -607,6 +723,8 @@ AS_IF([test "x$missing_mandatory" = "xyes"],
                       [ AS_ECHO([" - Missing Libtiff (TIFF files): 
http://libtiff.maptools.org";]) ])
                 AS_IF([test "x$has_libgit2" = "x0"],
                       [ AS_ECHO([" - Missing Libgit2: https://libgit2.org";])   
                   ])
+                AS_IF([test "x$has_curl" = "x0"],
+                      [ AS_ECHO([" - Missing cURL: https://curl.haxx.se";])     
                   ])
                 AS_IF([test "x$usable_libtool" = "xno"],
                       [ AS_ECHO([" - Unusable GNU Libtool: 
https://www.gnu.org/s/libtool";])
                         AS_IF([test "x$has_gnulibtool" = "xyes"],
@@ -624,15 +742,15 @@ AS_IF([test "x$missing_mandatory" = "xyes"],
         AS_ECHO(["dependencies in one command. See the link below:"])
         AS_ECHO(["  
https://www.gnu.org/s/gnuastro/manual/html_node/Dependencies-from-package-managers.html";])
         AS_ECHO([""])
-        AS_ECHO(["If you have already installed a dependency (for example in 
\`/install/path'),"])
+        AS_ECHO(["If you have already installed a dependency (for example in 
'/install/path'),"])
         AS_ECHO(["but this script can't link with it, add the path to the 
LDFLAGS, CPPFLAGS and"])
         AS_ECHO(["LD_LIBRARY_PATH environment variables before running 
configure. For example"])
-        AS_ECHO(["with the following commands (just correct the 
\`/install/path' part)."])
+        AS_ECHO(["with the following commands (just correct the 
'/install/path' part)."])
         AS_ECHO(["  $ export LDFLAGS=\"\$LDFLAGS -L/install/path/lib\""])
         AS_ECHO(["  $ export CPPFLAGS=\"\$CPPFLAGS -I/install/path/include\""])
         AS_ECHO(["  $ export 
LD_LIBRARY_PATH=\"\$LD_LIBRARY_PATH:/install/path/lib\""])
         AS_ECHO([""])
-        AS_ECHO(["[TIP:] Put these commands in your startup file (for example 
\`~/.bashrc') to"])
+        AS_ECHO(["[TIP:] Put these commands in your startup file (for example 
'~/.bashrc') to"])
         AS_ECHO(["avoid similar problems later. See the link below to learn 
more:"])
         AS_ECHO(["  
https://www.gnu.org/s/gnuastro/manual/html_node/Installation-directory.html";])
         AS_ECHO([""])
@@ -654,7 +772,7 @@ gl_INIT
 # Check if Gnulib tests should be done:
 AC_ARG_ENABLE([gnulibcheck],
               [AS_HELP_STRING([--enable-gnulibcheck],
-                   [In `make check', also test GNU Gnulib.])],
+                   [In 'make check', also test GNU Gnulib.])],
              [enable_gnulibcheck=yes], [enable_gnulibcheck=no])
 AM_CONDITIONAL([COND_GNULIBCHECK], [test $enable_gnulibcheck = yes])
 
@@ -662,10 +780,10 @@ AM_CONDITIONAL([COND_GNULIBCHECK], [test 
$enable_gnulibcheck = yes])
 
 
 
-# Gnulib checks for the proper name for the C99 equivalent `restrict'
-# keyword and puts it in the `ac_cv_c_restrict' variable. If none exists,
-# it will put a `no' inside of this variable. As described in the output
-# `bootstrapped/m4/gnulib-common.m4', this is only necessary until Autoconf
+# Gnulib checks for the proper name for the C99 equivalent 'restrict'
+# keyword and puts it in the 'ac_cv_c_restrict' variable. If none exists,
+# it will put a 'no' inside of this variable. As described in the output
+# 'bootstrapped/m4/gnulib-common.m4', this is only necessary until Autoconf
 # 2.70 is released. Afterwards, we can use AC_C_RESTRICT.
 AS_IF([test "x$ac_cv_c_restrict" = "xno"],
       [gal_restrict_replace=], [gal_restrict_replace=$ac_cv_c_restrict])
@@ -693,103 +811,109 @@ ayes=false
 AC_ARG_ENABLE([arithmetic],
               [AS_HELP_STRING([--enable-arithmetic],
                     [Install Arithmetic and other enabled programs.])],
-             [AS_IF([test "x$enable_arithmetic" != xno],
+              [AS_IF([test "x$enable_arithmetic" != xno],
                      [enable_arithmetic=yes; ayes=true])],
               [enable_arithmetic=notset])
 AC_ARG_ENABLE([buildprog],
               [AS_HELP_STRING([--enable-buildprog],
                     [Install BuildProgram and other enabled programs.])],
-             [AS_IF([test "x$enable_buildprog" != xno],
+              [AS_IF([test "x$enable_buildprog" != xno],
                      [enable_buildprog=yes; ayes=true])],
               [enable_buildprog=notset])
 AC_ARG_ENABLE([convertt],
               [AS_HELP_STRING([--enable-convertt],
                     [Install ConvertType and other enabled programs.])],
-             [AS_IF([test "x$enable_convertt" != xno],
+              [AS_IF([test "x$enable_convertt" != xno],
                      [enable_convertt=yes; ayes=true])],
               [enable_convertt=notset])
 AC_ARG_ENABLE([convolve],
               [AS_HELP_STRING([--enable-convolve],
                     [Install Convolve and other enabled programs.])],
-             [AS_IF([test "x$enable_convolve" != xno],
+              [AS_IF([test "x$enable_convolve" != xno],
                      [enable_cognvolve=yes; ayes=true])],
               [enable_convolve=notset])
 AC_ARG_ENABLE([cosmiccal],
               [AS_HELP_STRING([--enable-cosmiccal],
                     [Install CosmicCalculator and other enabled programs.])],
-             [AS_IF([test "x$enable_cosmiccal" != xno],
+              [AS_IF([test "x$enable_cosmiccal" != xno],
                      [enable_cosmiccal=yes; ayes=true])],
               [enable_cosmiccal=notset])
 AC_ARG_ENABLE([crop],
               [AS_HELP_STRING([--enable-crop],
                     [Install Crop and other enabled programs.])],
-             [AS_IF([test "x$enable_crop" != xno],
+              [AS_IF([test "x$enable_crop" != xno],
                      [enable_crop=yes; ayes=true])],
               [enable_crop=notset])
 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],
+              [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.])],
-             [AS_IF([test "x$enable_mkcatalog" != xno],
+              [AS_IF([test "x$enable_mkcatalog" != xno],
                      [enable_mkcatalog=yes; ayes=true])],
               [enable_mkcatalog=notset])
 AC_ARG_ENABLE([mknoise],
               [AS_HELP_STRING([--enable-mknoise],
                     [Install MakeNoise and other enabled programs.])],
-             [AS_IF([test "x$enable_mknoise" != xno],
+              [AS_IF([test "x$enable_mknoise" != xno],
                      [enable_mknoise=yes; ayes=true])],
               [enable_mknoise=notset])
 AC_ARG_ENABLE([mkprof],
               [AS_HELP_STRING([--enable-mkprof],
                     [Install MakeProfile and other enabled programs.])],
-             [AS_IF([test "x$enable_mkprof" != xno],
+              [AS_IF([test "x$enable_mkprof" != xno],
                      [enable_mkprof=yes; ayes=true])],
               [enable_mkprof=notset])
 AC_ARG_ENABLE([noisechisel],
               [AS_HELP_STRING([--enable-noisechisel],
                     [Install NoiseChisel and other enabled programs.])],
-             [AS_IF([test "x$enable_noisechisel" != xno],
+              [AS_IF([test "x$enable_noisechisel" != xno],
                      [enable_noisechisel=yes; ayes=true])],
               [enable_noisechisel=notset])
+AC_ARG_ENABLE([query],
+              [AS_HELP_STRING([--enable-query],
+                    [Install query and other enabled packages.])],
+              [AS_IF([test "x$enable_query" != xno],
+                     [enable_query=yes; ayes=true])],
+              [enable_query=notset])
 AC_ARG_ENABLE([segment],
               [AS_HELP_STRING([--enable-segment],
                     [Install Segment and other enabled programs.])],
-             [AS_IF([test "x$enable_segment" != xno],
+              [AS_IF([test "x$enable_segment" != xno],
                      [enable_segment=yes; ayes=true])],
               [enable_segment=notset])
 AC_ARG_ENABLE([statistics],
               [AS_HELP_STRING([--enable-statistics],
                     [Install Statistics and other enabled programs.])],
-             [AS_IF([test "x$enable_statistics" != xno],
+              [AS_IF([test "x$enable_statistics" != xno],
                      [enable_statistics=yes; ayes=true])],
               [enable_statistics=notset])
 AC_ARG_ENABLE([table],
               [AS_HELP_STRING([--enable-table],
                     [Install Table and other enabled programs.])],
-             [AS_IF([test "x$enable_table" != xno],
+              [AS_IF([test "x$enable_table" != xno],
                      [enable_table=yes; ayes=true])],
               [enable_table=notset])
 #AC_ARG_ENABLE([TEMPLATE],
 #              [AS_HELP_STRING([--enable-TEMPLATE],
 #                    [Install TEMPLATE and other enabled packages.])],
-#            [AS_IF([test "x$enable_TEMPLATE" != xno],
+#              [AS_IF([test "x$enable_TEMPLATE" != xno],
 #                     [enable_TEMPLATE=yes; ayes=true])],
 #              [enable_TEMPLATE=notset])
 AC_ARG_ENABLE([warp],
               [AS_HELP_STRING([--enable-warp],
                     [Install Warp and other enabled programs.])],
-             [AS_IF([test "x$enable_warp" != xno],
+              [AS_IF([test "x$enable_warp" != xno],
                      [enable_warp=yes; ayes=true])],
               [enable_warp=notset])
 
@@ -797,7 +921,7 @@ AC_ARG_ENABLE([warp],
 
 
 
-# If we had a "ayes" variable to be "true" if there was a `yes'. So any
+# If we had a "ayes" variable to be "true" if there was a 'yes'. So any
 # program that is not explicitly requested must be ignored and vice versa
 # (if no programs were explicitly requested, then enable all that weren't
 # disabled).
@@ -815,6 +939,7 @@ AS_IF([test $ayes = true ],
        AS_IF([test $enable_mknoise = notset],     [enable_mknoise=no])
        AS_IF([test $enable_mkprof = notset],      [enable_mkprof=no])
        AS_IF([test $enable_noisechisel = notset], [enable_noisechisel=no])
+       AS_IF([test $enable_query = notset],       [enable_query=no])
        AS_IF([test $enable_segment = notset],     [enable_segment=no])
        AS_IF([test $enable_statistics = notset],  [enable_statistics=no])
        AS_IF([test $enable_table = notset],       [enable_table=no])
@@ -835,6 +960,7 @@ AS_IF([test $ayes = true ],
        AS_IF([test $enable_mknoise = notset],     [enable_mknoise=yes])
        AS_IF([test $enable_mkprof = notset],      [enable_mkprof=yes])
        AS_IF([test $enable_noisechisel = notset], [enable_noisechisel=yes])
+       AS_IF([test $enable_query = notset],       [enable_query=yes])
        AS_IF([test $enable_segment = notset],     [enable_segment=yes])
        AS_IF([test $enable_statistics = notset],  [enable_statistics=yes])
        AS_IF([test $enable_table = notset],       [enable_table=yes])
@@ -868,6 +994,7 @@ AM_CONDITIONAL([COND_MKCATALOG],   [test $enable_mkcatalog 
= yes])
 AM_CONDITIONAL([COND_MKNOISE],     [test $enable_mknoise = yes])
 AM_CONDITIONAL([COND_MKPROF],      [test $enable_mkprof = yes])
 AM_CONDITIONAL([COND_NOISECHISEL], [test $enable_noisechisel = yes])
+AM_CONDITIONAL([COND_QUERY],       [test $enable_query = yes])
 AM_CONDITIONAL([COND_SEGMENT],     [test $enable_segment = yes])
 AM_CONDITIONAL([COND_STATISTICS],  [test $enable_statistics = yes])
 AM_CONDITIONAL([COND_TABLE],       [test $enable_table = yes])
@@ -883,7 +1010,7 @@ AM_CONDITIONAL([COND_WARP],        [test $enable_warp = 
yes])
 # linking flags and put them in the Makefiles.
 LIBS="$orig_LIBS"
 AC_SUBST(CONFIG_LDADD, [$LDADD])
-AM_CONDITIONAL([COND_NORPATH], [test "x$enable_rpath" = "xno"])
+AC_SUBST(ENABLE_SHARED, [$enable_shared])
 AS_ECHO(["linking flags (LDADD) ... $LDADD"])
 
 
@@ -896,12 +1023,13 @@ AS_ECHO(["linking flags (LDADD) ... $LDADD"])
 AC_CONFIG_FILES([Makefile
                  doc/Makefile
                  lib/Makefile
-                tests/Makefile
+                 tests/Makefile
                  bin/crop/Makefile
                  bin/fits/Makefile
                  bin/warp/Makefile
                  bin/table/Makefile
                  bin/match/Makefile
+                 bin/query/Makefile
                  bin/mkprof/Makefile
                  bin/script/Makefile
                  bin/mknoise/Makefile
@@ -910,9 +1038,9 @@ AC_CONFIG_FILES([Makefile
                  bin/convolve/Makefile
                  bin/buildprog/Makefile
                  bin/cosmiccal/Makefile
-                bin/mkcatalog/Makefile
+                 bin/mkcatalog/Makefile
                  bin/arithmetic/Makefile
-                bin/statistics/Makefile
+                 bin/statistics/Makefile
                  bin/noisechisel/Makefile
                  bootstrapped/lib/Makefile
                  bootstrapped/tests/Makefile
@@ -925,14 +1053,14 @@ AC_CONFIG_FILES([Makefile
 # Printing guiding messages. Autoconf will make the variable
 # enable_guide_message from the first argument to AC_ARG_ENABLE. It will
 # also give it a value. From the Autoconf manual, we see that
-# `--disable-guide-message' is equivalent to a value of `no', while with no
-# argument, the value will default to `yes'. In the last argument to
+# '--disable-guide-message' is equivalent to a value of 'no', while with no
+# argument, the value will default to 'yes'. In the last argument to
 # AC_ARG_ENABLE, we also specify the default behavior (when it isn't given
-# at all), here we want the default to be `yes'.
+# at all), here we want the default to be 'yes'.
 AC_ARG_ENABLE([guide-message],
               [AS_HELP_STRING([--disable-guide-message],
                    [No messages after each build step.])],
-             [], [enable_guide_message=yes])
+              [], [enable_guide_message=yes])
 AC_SUBST(GUIDEMESSAGE, [$enable_guide_message])
 
 
@@ -978,6 +1106,15 @@ AS_IF([test x$enable_guide_message = xyes],
                AS_ECHO(["    released in October 2015)."])
                AS_ECHO([]) ])
 
+        AS_IF([test "x$has_wcslib_dis_h" = "x0"],
+              [dependency_notice=yes
+               AS_ECHO(["  - WCSLIB 
(https://www.atnf.csiro.au/people/mcalabre/WCS) version"])
+               AS_ECHO(["    on this system doesn't support distortions (i.e., 
it doesn't"])
+               AS_ECHO(["    have 'dis.h'. This build won't crash but Gnuastro 
will not be able"])
+               AS_ECHO(["    to do distortion-related operations. If you don't 
need such"])
+               AS_ECHO(["    operations you can ignore this warning."])
+               AS_ECHO([]) ])
+
         AS_IF([test "x$has_libjpeg" = "xno"],
               [dependency_notice=yes
                AS_ECHO(["  - libjpeg (http://ijg.org), could not be linked 
with in your library"])
@@ -1013,8 +1150,8 @@ AS_IF([test x$enable_guide_message = xyes],
                AS_ECHO(["    harm the rest of Gnuastro's building and 
installation. Gnuastro has"])
                AS_ECHO(["    its own internal implementation of GNU Libtool to 
build its self. This"])
                AS_ECHO(["    warning is only to let you know that BuildProgram 
will not be"])
-               AS_ECHO(["    part of this build. The executable names searched 
were \`libtool'"])
-               AS_ECHO(["    and \`glibtool'. The shells searched were \`sh', 
\`bash' and \`zsh'."])
+               AS_ECHO(["    part of this build. The executable names searched 
were 'libtool'"])
+               AS_ECHO(["    and 'glibtool'. The shells searched were 'sh', 
'bash' and 'zsh'."])
                AS_ECHO([])
                AS_IF([test "x$has_gnulibtool" = "xyes"],
                      [AS_ECHO(["    -- GNU Libtool is present, but couldn't be 
run in tested shells."])
@@ -1029,11 +1166,21 @@ AS_IF([test x$enable_guide_message = xyes],
         AS_IF([test "x$has_ghostscript" = "xno"],
               [dependency_notice=yes
                AS_ECHO(["  - GPL GhostScript (https://www.ghostscript.com) 
version 9.10 or later,"])
-               AS_ECHO(["    with the executable name \`gs', was not found in 
your PATH environment"])
+               AS_ECHO(["    with the executable name 'gs', was not found in 
your PATH environment"])
                AS_ECHO(["    variable. If PDF outputs are desired, the 
respective tool it will abort"])
                AS_ECHO(["    with an EPS output which you can convert to PDF 
by other means."])
                AS_ECHO([]) ])
 
+        AS_IF([test "x$has_curl" = "xno"],
+              [dependency_notice=yes
+               AS_ECHO(["  - cURL (https://curl.haxx.se) with the executable 
name 'curl' was not"])
+               AS_ECHO(["    found in your PATH environment variable. 
'astquery' uses it to access"])
+               AS_ECHO(["    remote databases at run-time. So not having 
'curl' won't affect this"])
+               AS_ECHO(["    build of Gnuastro, you can continue for now. But 
to use 'astquery',"])
+               AS_ECHO(["    don't forget to install cURL later (before using 
'astquery' for the"])
+               AS_ECHO(["    first time)."])
+               AS_ECHO([]) ])
+
         # Notice for obtaining the optional dependencies using a package
         # manager.
         AS_IF([test "x$dependency_notice" = "xyes"],
@@ -1059,7 +1206,7 @@ AS_IF([test x$enable_guide_message = xyes],
                AS_ECHO(["    a command and install a matching one, someone 
might install a"])
                AS_ECHO(["    fake command with the name of one that is not 
installed. You can"])
                AS_ECHO(["    always run a program in the current directory by 
explicity adding"])
-               AS_ECHO(["    a \`./' before it's name. Run the following 
command after"])
+               AS_ECHO(["    a './' before it's name. Run the following 
command after"])
                AS_ECHO(["    installing Gnuastro to learn more about PATH:"])
                AS_ECHO(["        $ info gnuastro \"Installation directory\""])
                AS_ECHO([]) ])
diff --git a/developer-build b/developer-build
index f528b8b..70c96b4 100755
--- a/developer-build
+++ b/developer-build
@@ -2,14 +2,14 @@
 
 # This script will configure and build Gnuastro in parallel inside another
 # directory (to keep the source and build directories separate). By default
-# it is in the tmpfs directory of the RAM. Run with `--help' for a more
+# it is in the tmpfs directory of the RAM. Run with '--help' for a more
 # complete description.
 #
 # Original author:
 #   Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
 #   Mosè Giordano <mose@gnu.org>
-# Copyright (C) 2016-2019, Free Software Foundation, Inc.
+# Copyright (C) 2016-2021, 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
@@ -36,7 +36,7 @@ set -e
 
 
 # Default values for variables.
-jobs=8
+jobs=0
 dist=0
 debug=0
 clean=0
@@ -306,19 +306,27 @@ done
 
 
 
-# Check if top_build_dir exists
-if [ ! -d $top_build_dir ]; then
-    echo "$top_build_dir doesn't exist. Aborted."
-    exit 1
-fi
+# Keep the address of this source directory (where this script is being run
+# from) which we will need later.
+srcdir=$(pwd)
 
 
 
 
 
-# Keep the address of this source directory (where this script is being run
-# from) which we will need later.
-srcdir=$(pwd)
+# Set the number of jobs.
+if [ $jobs = 0 ]; then
+    jobs=$(nproc)
+fi
+
+
+
+
+# Check if top_build_dir exists
+if [ ! -d $top_build_dir ]; then
+    echo "$top_build_dir doesn't exist. Aborted."
+    exit 1
+fi
 
 
 
@@ -420,7 +428,7 @@ fi
 
 
 # Build Gnuastro in that directory with the specified number of threads
-make -kj$jobs
+make -k -j$jobs
 
 
 
@@ -428,7 +436,7 @@ make -kj$jobs
 
 # If requested, also run 'make check'.
 if [ x$check = x1 ]; then
-    make check -kj$jobs
+    make check -k -j$jobs
 fi
 
 
@@ -449,7 +457,7 @@ fi
 # Upload the tarball to the requested server.
 if [ x$upload = x1 ]; then
     # Get the base package name, and use it to make a generic tarball
-    # name. Note that with the `--upload' option, `--dist' is also
+    # name. Note that with the '--upload' option, '--dist' is also
     # activated, so the tarball is already built and ready by this
     # step.
     tarball=$(ls *.tar.lz)
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 8e85d02..12dc957 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -6,7 +6,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, Free Software Foundation, Inc.
 ##
 ## GNU Astronomy Utilities is free software: you can redistribute it
 ## and/or modify it under the terms of the GNU General Public License
@@ -26,14 +26,20 @@
 ## Set the shell
 SHELL=/bin/sh
 
-BUILT_SOURCES = $(srcdir)/authors.texi
 
+# Built files that should be treated as sources for the main job.
+# imgrepresentative: This is just one of the images that is used in the
+# book as a representative for all of them because they are all built with
+# one script.
+imgrepresentative = $(top_srcdir)/doc/gnuastro-figures/done.txt
+BUILT_SOURCES = $(srcdir)/authors.texi $(imgrepresentative)
 
-## fdl.texi (The GNU Free documentation license in Texinfo format) is
-## bootstrapped from Gnulib. Therefore it is not in this
-## directory. However, it is needed by gnuastro.texi to build the
-## documentation. It is the job of AM_MAKEINFOFLAGS, to pass options to the
-## documentation build programs. BOOTSTRPDOC is defined in `configure.ac'.
+
+## gpl-3.0.texi (which is just the GPL in Texinfo format) is bootstrapped
+## from Gnulib. Therefore it is not in this directory. However, it is
+## needed by gnuastro.texi to build the documentation. It is the job of
+## AM_MAKEINFOFLAGS, to pass options to the documentation build
+## programs. BOOTSTRPDOC is defined in 'configure.ac'.
 bootstrpdoc = $(top_srcdir)/bootstrapped/doc
 AM_MAKEINFOFLAGS = -I $(bootstrpdoc) -I $(srcdir)
 
@@ -41,14 +47,14 @@ AM_MAKEINFOFLAGS = -I $(bootstrpdoc) -I $(srcdir)
 # This is just a temporary work-around since Automake does not pass the
 # AM_MAKEINFOFLAGS to texi2dvi (which builds DVI and PDF outputs). This was
 # reported in Automake's bug 23599. Note that a space is necessary between
-# the `-I' and directory for texi2dvi.
+# the '-I' and directory for texi2dvi.
 TEXI2DVI = texi2dvi -I $(bootstrpdoc) -I $(srcdir)
 
 
 ## Commands to make the texinfo tools.
 info_TEXINFOS = gnuastro.texi
-gnuastro_TEXINFOS = $(bootstrpdoc)/fdl.texi $(bootstrpdoc)/gpl-3.0.texi \
-                    $(srcdir)/authors.texi formath.texi
+gnuastro_TEXINFOS = fdl.texi formath.texi $(srcdir)/authors.texi \
+                    $(bootstrpdoc)/gpl-3.0.texi
 
 
 ## Files not predefined by Automake, and not in dependencies that must
@@ -56,14 +62,20 @@ gnuastro_TEXINFOS = $(bootstrpdoc)/fdl.texi 
$(bootstrpdoc)/gpl-3.0.texi \
 EXTRA_DIST = genauthors README
 
 
-## We want to build the authors.texi file only when we are building in the
-## version controlled source. For the non version controlled souce,
-## deleting this file is like somehow deleting formath.texi, it will not be
-## rebuilt and result in an error.
+## We want to build 'imgrepresentative' and 'authors.texi' only when we are
+## building in the version controlled source (in other words: when
+## 'configure' has been re-built). For the non version controlled souce,
+## deleting these files is like somehow deleting formath.texi (a core
+## file), it will not be rebuilt and result in an error.
 $(srcdir)/authors.texi: $(top_srcdir)/configure
        $(top_srcdir)/doc/genauthors $(top_srcdir)
 
-## Images:
+## Build the images of the book (with one chosen as representative to act
+## as a target in Make).
+$(imgrepresentative):
+       cd $(top_srcdir)/doc/plotsrc; make
+
+## Images and their directories.
 infognuastrodir=$(infodir)/gnuastro-figures/
 dist_infognuastro_DATA = $(top_srcdir)/doc/gnuastro-figures/*
 
@@ -117,6 +129,9 @@ endif
 if COND_NOISECHISEL
   MAYBE_NOISECHISEL_MAN = man/astnoisechisel.1
 endif
+if COND_QUERY
+  MAYBE_QUERY_MAN = man/astquery.1
+endif
 if COND_SEGMENT
   MAYBE_SEGMENT_MAN = man/astsegment.1
 endif
@@ -132,12 +147,13 @@ endif
 #if COND_TEMPLATE
 #  MAYBE_TEMPLATE_MAN = man/astTEMPLATE.1
 #endif
-dist_man_MANS = $(MAYBE_ARITHMETIC_MAN) $(MAYBE_BUILDPROG_MAN)          \
-  $(MAYBE_CONVERTT_MAN) $(MAYBE_CONVOLVE_MAN) $(MAYBE_COSMICCAL_MAN)    \
-  $(MAYBE_CROP_MAN) $(MAYBE_FITS_MAN) $(MAYBE_MATCH_MAN)                \
-  $(MAYBE_MKCATALOG_MAN) $(MAYBE_MKNOISE_MAN) $(MAYBE_MKPROF_MAN)       \
-  $(MAYBE_NOISECHISEL_MAN) $(MAYBE_SEGMENT_MAN) $(MAYBE_STATISTICS_MAN) \
-  $(MAYBE_TABLE_MAN) $(MAYBE_WARP_MAN) man/astscript-sort-by-night.1
+dist_man_MANS = $(MAYBE_ARITHMETIC_MAN) $(MAYBE_BUILDPROG_MAN) \
+  $(MAYBE_CONVERTT_MAN) $(MAYBE_CONVOLVE_MAN) $(MAYBE_COSMICCAL_MAN) \
+  $(MAYBE_CROP_MAN) $(MAYBE_FITS_MAN) $(MAYBE_MATCH_MAN) \
+  $(MAYBE_MKCATALOG_MAN) $(MAYBE_MKNOISE_MAN) $(MAYBE_MKPROF_MAN) \
+  $(MAYBE_NOISECHISEL_MAN) $(MAYBE_QUERY_MAN) $(MAYBE_SEGMENT_MAN) \
+  $(MAYBE_STATISTICS_MAN) $(MAYBE_TABLE_MAN) $(MAYBE_WARP_MAN) \
+  man/astscript-sort-by-night.1
 
 
 ## See if help2man is present or not. When help2man doesn't exist, we don't
@@ -151,7 +167,7 @@ else
 endif
 
 
-# Build the `man' directory and then put all the man pages in
+# Build the 'man' directory and then put all the man pages in
 # it. Unfortunately as far as I know, pattern rules are not portable in all
 # implementations of Make, so we have to list all the utilities manually.
 toputildir=$(top_builddir)/bin
@@ -204,6 +220,10 @@ man/astnoisechisel.1: $(top_srcdir)/bin/noisechisel/args.h 
 $(ALLMANSDEP)
        $(MAYBE_HELP2MAN) -n "detect signal in a noisy image"              \
                          --libtool $(toputildir)/noisechisel/astnoisechisel
 
+man/astquery.1: $(top_srcdir)/bin/query/args.h  $(ALLMANSDEP)
+       $(MAYBE_HELP2MAN) -n "query remote data servers and download"      \
+                         --libtool $(toputildir)/query/astquery
+
 man/astscript-sort-by-night.1: $(top_srcdir)/bin/script/sort-by-night.in   \
                                $(ALLMANSDEP)
        $(MAYBE_HELP2MAN) -n "Sort input FITS files by night"              \
diff --git a/doc/README b/doc/README
index f1693e5..c4e8dc5 100644
--- a/doc/README
+++ b/doc/README
@@ -1,7 +1,7 @@
 Documentation of GNU Astronomy Utilities
 ========================================
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
 This directory contains the documentation (manual) of GNU Astronomy
@@ -13,7 +13,7 @@ Documentation (manual):
 -----------------------
 
 The documentation of Gnuastro is written in Texinfo. The main source
-file is `gnuastro.texi'.
+file is 'gnuastro.texi'.
 
 Webpage (only for maintainer)
 -----------------------------
@@ -30,18 +30,18 @@ in a specified directory anywhere you like (specified by 
the TOPWEBCHECKOUT
 shell variable). You don't have to version control any of the files in that
 directory, but it is easiest to have a fixed place since CVS needs a local
 copy. Unfortunately the GNU webpage runs on CVS! Read the top comments of
-the `forwebpage' script for instructions on what to do for the first time
+the 'forwebpage' script for instructions on what to do for the first time
 and later times.
 
 Update MathJax (for the webpage, only for maintainer)
 -----------------------------------------------------
 
-There is a script in the `MathJax' directory on the Gnuastro webpage
-directory to update MathJax (`addmissing.sh'). You can get a recent
+There is a script in the 'MathJax' directory on the Gnuastro webpage
+directory to update MathJax ('addmissing.sh'). You can get a recent
 version of MathJax from its webpage and put it in the correct place
 (see the comments in the script). That script will then add all the
 new files from MathJax to the checked out files and then you can use
-`cvs -nq update' and `cvs add' to add all the new files and
+'cvs -nq update' and 'cvs add' to add all the new files and
 directories. Just note that CVS is an antique(!) and so you have to
 add all the files in separate directories separately, first add the
 directory, then CVS will find the untracked files inside it and add
@@ -50,14 +50,14 @@ them one by one!
 Image copyright
 ---------------
 
-This directory also contains the `gnuastrologo.xcf' image which is a crude
+This directory also contains the 'gnuastrologo.xcf' image which is a crude
 logo for Gnuastro in case we need it. It is released under the GNU Free
 Documentation License, Version 1.3 or later, just like this README
 (statement is at the bottom of this file)
 
 Copyright
 ---------
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
diff --git a/doc/announce-acknowledge.txt b/doc/announce-acknowledge.txt
index 1edfc78..8ff3cba 100644
--- a/doc/announce-acknowledge.txt
+++ b/doc/announce-acknowledge.txt
@@ -1,13 +1,19 @@
 Alphabetically ordered list to acknowledge in the next release.
 
-Hamed Altafi
-Alexey Dokuchaev
-Raúl Infante Sainz
-Sebastián Luna Valero
+Mark Calabretta
+Raul Infante-Sainz
+Alberto Madrigal
+Sylvain Mottet
+Francois Ochsenbein
+Samane Raji
+Zahra Sharbaf
+Ignacio Trujillo
 
 
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+
+
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
diff --git a/doc/coming-soon.html b/doc/coming-soon.html
new file mode 100644
index 0000000..844019c
--- /dev/null
+++ b/doc/coming-soon.html
@@ -0,0 +1,99 @@
+<!--#include virtual="/server/html5-header.html" -->
+
+<!-- Parent-Version: 1.84 -->
+<title>Gnuastro features comming soon</title>
+
+<style type="text/css"><!--
+.reduced-width { width: 50em; }
+.summary {
+  font-size: 1em;
+  box-sizing: border-box;
+  border: 1px solid #bbb;
+  margin-top: 1.3em;
+}
+.summary, .pict { width: 15.8em; max-width: 100%; }
+@media (min-width:45em) { .left-column { margin-right: 17.5em; }}
+--></style>
+<!--#include virtual="gnuastro.translist" -->
+<!--#include virtual="/server/banner.html" -->
+<div class="reduced-width">
+<h2>Gnuastro features that are coming soon (in version 0.15)</h2>
+<div class="thin"></div>
+
+<p>
+  <a href="gnuastro.html">GNU Astronomy Utilities</a> (Gnuastro) is an 
official GNU package consisting of various programs and library functions for 
the manipulation and analysis of astronomical data.
+</p>
+
+<p>
+  The current stable Gnuastro release is version 0.14.
+  In this page, you can some demonstrations of ongoing work for the next 
release.
+  You can always download the tarball containing <a 
href="http://git.savannah.gnu.org/cgit/gnuastro.git/log";>the most recent 
work</a> (which includes the features shown here) from this URL: <a 
href="http://akhlaghi.org/gnuastro-latest.tar.lz";>http://akhlaghi.org/gnuastro-latest.tar.lz</a>.
+  We do encourage using this most recent tarball and testing the features to 
report any possible bugs that have not yet been found.
+</p>
+
+
+
+
+
+
+<h3 id="license">Licensing</h3>
+
+<p>GNU Astronomy Utilities (Gnuastro) is free software; you can
+redistribute it and/or modify it under the terms of
+the <a href="/licenses/gpl.html" rel="license">GNU
+General Public License</a> as published by the Free Software
+Foundation; either version&nbsp;3 of the License, or (at your option)
+any later version.</p>
+
+</div>
+</div><!-- for id="content", starts in the include above -->
+<!--#include virtual="/server/footer.html" -->
+<div id="footer">
+<div class="unprintable">
+
+<p>Please send general FSF &amp; GNU inquiries to
+&lt;<a href="mailto:gnu@gnu.org";>gnu@gnu.org</a>&gt;.
+There are also <a href="/contact/">other ways to contact</a> the FSF.
+Broken links and other corrections or suggestions can be sent to &lt;<a
+href="mailto:bug-gnuastro=at=gnu.org";>bug-gnuastro::at::gnu.org</a>&gt;.</p>
+
+<p>Please see the <a
+href="/server/standards/README.translations.html">Translations
+README</a> for information on coordinating and contributing translations
+of this article.</p>
+</div>
+
+<!-- Regarding copyright, in general, standalone pages (as opposed to
+     files generated as part of manuals) on the GNU web server should
+     be under CC BY-ND 4.0.  Please do NOT change or remove this
+     without talking with the webmasters or licensing team first.
+     Please make sure the copyright date is consistent with the
+     document.  For web pages, it is ok to list just the latest year the
+     document was modified, or published.
+
+     If you wish to list earlier years, that is ok too.
+     Either "2001, 2002, 2003" or "2001-2003" are ok for specifying
+     years, as long as each year in the range is in fact a copyrightable
+     year, i.e., a year in which the document was published (including
+     being publicly visible on the web or in a revision control system).
+
+     There is more detail about copyright years in the GNU Maintainers
+     Information document, www.gnu.org/prep/maintain. -->
+
+<p>Copyright &copy; 2015-2016 Free Software Foundation, Inc.</p>
+
+<p>This page is licensed under a <a rel="license"
+href="http://creativecommons.org/licenses/by-nd/4.0/";>Creative
+Commons Attribution-NoDerivs 4.0 International License</a>.</p>
+
+<!--#include virtual="/server/bottom-notes.html" -->
+
+<p class="unprintable">Updated:
+<!-- timestamp start -->
+$Date: 2019/01/22 19:14:35 $
+<!-- timestamp end -->
+</p>
+</div>
+</div>
+</body>
+</html>
diff --git a/doc/fdl.texi b/doc/fdl.texi
new file mode 100644
index 0000000..eaf3da0
--- /dev/null
+++ b/doc/fdl.texi
@@ -0,0 +1,505 @@
+@c The GNU Free Documentation License.
+@center Version 1.3, 3 November 2008
+
+@c This file is intended to be included within another document,
+@c hence no sectioning command or @node.
+
+@display
+Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, 
Inc.
+@uref{https://fsf.org/}
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@enumerate 0
+@item
+PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document @dfn{free} in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of ``copyleft'', which means that derivative
+works of the document must themselves be free in the same sense.  It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does.  But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book.  We recommend this License
+principally for works whose purpose is instruction or reference.
+
+@item
+APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License.  Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein.  The ``Document'', below,
+refers to any such manual or work.  Any member of the public is a
+licensee, and is addressed as ``you''.  You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A ``Modified Version'' of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A ``Secondary Section'' is a named appendix or a front-matter section
+of the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall
+subject (or to related matters) and contains nothing that could fall
+directly within that overall subject.  (Thus, if the Document is in
+part a textbook of mathematics, a Secondary Section may not explain
+any mathematics.)  The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The ``Invariant Sections'' are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License.  If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant.  The Document may contain zero
+Invariant Sections.  If the Document does not identify any Invariant
+Sections then there are none.
+
+The ``Cover Texts'' are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License.  A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A ``Transparent'' copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters.  A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text.  A copy that is not ``Transparent'' is called ``Opaque''.
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, La@TeX{} input
+format, SGML or XML using a publicly available
+DTD, and standard-conforming simple HTML,
+PostScript or PDF designed for human modification.  Examples
+of transparent image formats include PNG, XCF and
+JPG@.  Opaque formats include proprietary formats that can be
+read and edited only by proprietary word processors, SGML or
+XML for which the DTD and/or processing tools are
+not generally available, and the machine-generated HTML,
+PostScript or PDF produced by some word processors for
+output purposes only.
+
+The ``Title Page'' means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page.  For works in
+formats which do not have any title page as such, ``Title Page'' means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+The ``publisher'' means any person or entity that distributes copies
+of the Document to the public.
+
+A section ``Entitled XYZ'' means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language.  (Here XYZ stands for a
+specific section name mentioned below, such as ``Acknowledgements'',
+``Dedications'', ``Endorsements'', or ``History''.)  To ``Preserve the Title''
+of such a section when you modify the Document means that it remains a
+section ``Entitled XYZ'' according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document.  These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+@item
+VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License.  You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute.  However, you may accept
+compensation in exchange for copies.  If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+@item
+COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover.  Both covers must also clearly and legibly identify
+you as the publisher of these copies.  The front cover must present
+the full title with all words of the title equally prominent and
+visible.  You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+@item
+MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it.  In addition, you must do these things in the Modified Version:
+
+@enumerate A
+@item
+Use in the Title Page (and on the covers, if any) a title distinct
+from that of the Document, and from those of previous versions
+(which should, if there were any, be listed in the History section
+of the Document).  You may use the same title as a previous version
+if the original publisher of that version gives permission.
+
+@item
+List on the Title Page, as authors, one or more persons or entities
+responsible for authorship of the modifications in the Modified
+Version, together with at least five of the principal authors of the
+Document (all of its principal authors, if it has fewer than five),
+unless they release you from this requirement.
+
+@item
+State on the Title page the name of the publisher of the
+Modified Version, as the publisher.
+
+@item
+Preserve all the copyright notices of the Document.
+
+@item
+Add an appropriate copyright notice for your modifications
+adjacent to the other copyright notices.
+
+@item
+Include, immediately after the copyright notices, a license notice
+giving the public permission to use the Modified Version under the
+terms of this License, in the form shown in the Addendum below.
+
+@item
+Preserve in that license notice the full lists of Invariant Sections
+and required Cover Texts given in the Document's license notice.
+
+@item
+Include an unaltered copy of this License.
+
+@item
+Preserve the section Entitled ``History'', Preserve its Title, and add
+to it an item stating at least the title, year, new authors, and
+publisher of the Modified Version as given on the Title Page.  If
+there is no section Entitled ``History'' in the Document, create one
+stating the title, year, authors, and publisher of the Document as
+given on its Title Page, then add an item describing the Modified
+Version as stated in the previous sentence.
+
+@item
+Preserve the network location, if any, given in the Document for
+public access to a Transparent copy of the Document, and likewise
+the network locations given in the Document for previous versions
+it was based on.  These may be placed in the ``History'' section.
+You may omit a network location for a work that was published at
+least four years before the Document itself, or if the original
+publisher of the version it refers to gives permission.
+
+@item
+For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve
+the Title of the section, and preserve in the section all the
+substance and tone of each of the contributor acknowledgements and/or
+dedications given therein.
+
+@item
+Preserve all the Invariant Sections of the Document,
+unaltered in their text and in their titles.  Section numbers
+or the equivalent are not considered part of the section titles.
+
+@item
+Delete any section Entitled ``Endorsements''.  Such a section
+may not be included in the Modified Version.
+
+@item
+Do not retitle any existing section to be Entitled ``Endorsements'' or
+to conflict in title with any Invariant Section.
+
+@item
+Preserve any Warranty Disclaimers.
+@end enumerate
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant.  To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled ``Endorsements'', provided it contains
+nothing but endorsements of your Modified Version by various
+parties---for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version.  Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity.  If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+@item
+COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy.  If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled ``History''
+in the various original documents, forming one section Entitled
+``History''; likewise combine any sections Entitled ``Acknowledgements'',
+and any sections Entitled ``Dedications''.  You must delete all
+sections Entitled ``Endorsements.''
+
+@item
+COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+@item
+AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an ``aggregate'' if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+@item
+TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections.  You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers.  In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled ``Acknowledgements'',
+``Dedications'', or ``History'', the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+@item
+TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense, or distribute it is void, and
+will automatically terminate your rights under this License.
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, receipt of a copy of some or all of the same material does
+not give you any rights to use it.
+
+@item
+FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time.  Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.  See
+@uref{https://www.gnu.org/licenses/}.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License ``or any later version'' applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation.  If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.  If the Document
+specifies that a proxy can decide which future versions of this
+License can be used, that proxy's public statement of acceptance of a
+version permanently authorizes you to choose that version for the
+Document.
+
+@item
+RELICENSING
+
+``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any
+World Wide Web server that publishes copyrightable works and also
+provides prominent facilities for anybody to edit those works.  A
+public wiki that anybody can edit is an example of such a server.  A
+``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the
+site means any set of copyrightable works thus published on the MMC
+site.
+
+``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
+published by that same organization.
+
+``Incorporate'' means to publish or republish a Document, in whole or
+in part, as part of another Document.
+
+An MMC is ``eligible for relicensing'' if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole
+or in part into the MMC, (1) had no cover texts or invariant sections,
+and (2) were thus incorporated prior to November 1, 2008.
+
+The operator of an MMC Site may republish an MMC contained in the site
+under CC-BY-SA on the same site at any time before August 1, 2009,
+provided the MMC is eligible for relicensing.
+
+@end enumerate
+
+@page
+@heading ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+@smallexample
+@group
+  Copyright (C)  @var{year}  @var{your name}.
+  Permission is granted to copy, distribute and/or modify this document
+  under the terms of the GNU Free Documentation License, Version 1.3
+  or any later version published by the Free Software Foundation;
+  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+  Texts.  A copy of the license is included in the section entitled ``GNU
+  Free Documentation License''.
+@end group
+@end smallexample
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the ``with@dots{}Texts.''@: line with this:
+
+@smallexample
+@group
+    with the Invariant Sections being @var{list their titles}, with
+    the Front-Cover Texts being @var{list}, and with the Back-Cover Texts
+    being @var{list}.
+@end group
+@end smallexample
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+
+@c Local Variables:
+@c ispell-local-pdict: "ispell-dict"
+@c End:
diff --git a/doc/fonts.css b/doc/fonts.css
index 9145272..889d75b 100644
--- a/doc/fonts.css
+++ b/doc/fonts.css
@@ -3,7 +3,7 @@
 Original author:
     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019 Free Software Foundation, Inc.
+Copyright (C) 2016-2021, Free Software Foundation, Inc.
 
 This stylesheet is free software: you can redistribute it and/or
 modify it under the terms of the GNU General Public Licence as
diff --git a/doc/formath.texi b/doc/formath.texi
index ce9c276..495e604 100644
--- a/doc/formath.texi
+++ b/doc/formath.texi
@@ -12,7 +12,7 @@ For the other formats, everything is normal, except the 
display math
 equations that don't have any proper solution, so I have just put a $$
 $$ around the math.
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Copying and distribution of this file, with or without modification,
 are permitted in any medium without royalty provided the copyright
diff --git a/doc/forwebpage b/doc/forwebpage
index cc42dae..89cc9c4 100755
--- a/doc/forwebpage
+++ b/doc/forwebpage
@@ -4,13 +4,13 @@
 # files to the destination directory (local CVS checkout) and commit the
 # changes to the gnu.org/software/gnuastro webpage. Finally, delete the
 # temporary directory. See below for setting up the CVS checkout for the
-# first time. NOTE: This script is defined to be run in Gnuastro's `doc/'
+# first time. NOTE: This script is defined to be run in Gnuastro's 'doc/'
 # directory. It should not be called from anywhere else.
 #
-#    # IMPORTANT: do not end the destination directory with a `/'.
+#    # IMPORTANT: do not end the destination directory with a '/'.
 #    ./forwebpage /destination/directory
 #
-# The temporary directory will be named `/destination/directory_tmp'.
+# The temporary directory will be named '/destination/directory_tmp'.
 #
 # This script will first run gendocs.sh on the Texinfo source which creates
 # the standrard GNU webpage style documentation page. Then it does some
@@ -37,7 +37,7 @@
 # be enough for the initial setup. Afterwards, you can just run this
 # script.
 #
-#   1. Add `export CVS_RSH=ssh' to your ~/.bashrc
+#   1. Add 'export CVS_RSH=ssh' to your ~/.bashrc
 #
 #   2. Choose the top directory to keep your local copy of the webpage
 #      (TOPWEBCHECKOUT). Note that especially because of MathJax,
@@ -63,7 +63,7 @@
 #
 # When a new page is added, after all the "cvs commit: Examining ..."
 # reports, you will see a line for each new file starting with a
-# '?'. Assume FILES is a string of all these files (that have a `?' in
+# '?'. Assume FILES is a string of all these files (that have a '?' in
 # the CVS commit of this script). Run these commands to go into your
 # local checkout and add those files to the repository.
 #
@@ -76,7 +76,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2016-2019, Free Software Foundation, Inc.
+# Copyright (C) 2016-2021, 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
@@ -128,10 +128,10 @@ fi
 
 
 # The date that is printed on the manual is generated by looking at the
-# modification time of gnuastro.texi, not `../configure' (which was used to
+# modification time of gnuastro.texi, not '../configure' (which was used to
 # generate the version number). This can cause confusion, since the version
 # number (coming from the commit) will have a different date in the
-# revision history. To avoid this, here we check if the `../configure'
+# revision history. To avoid this, here we check if the '../configure'
 # modification date is similar to that on 'gnuastro.texi' or not. If it
 # isn't a warning error is printed to let the user know, so they take the
 # appropriate action.
@@ -222,10 +222,10 @@ IFS=''
 echo
 echo %%%%% Correcting the HTMLs %%%%%
 
-# Correct the address of the `(dir)' links on the top pages of both
-# HTML outputs. In the $tmpdir/gnuastro.html, it is `dir.html#top'
+# Correct the address of the '(dir)' links on the top pages of both
+# HTML outputs. In the $tmpdir/gnuastro.html, it is 'dir.html#top'
 # which should be change to index.html. In
-# $tmpdir/html_node/index.html, it is `../dir/index.html' which
+# $tmpdir/html_node/index.html, it is '../dir/index.html' which
 # should become ../index.html
 cat $tmpdir/gnuastro.html | sed s/dir\.html\#Top/index.html/g > tmp.txt
 mv tmp.txt $tmpdir/gnuastro.html
@@ -328,12 +328,12 @@ echo %%%%% DONE %%%%%
 
 
 
-# Copy `gnuastro.en.html' page into `index.html'. Then copy the other
+# Copy 'gnuastro.en.html' page into 'index.html'. Then copy the other
 # necessary webpage files.
 cp gnuastro.en.html $topwebcheckout/gnuastro/gnuastro.html
 cp gnuastro.fr.html gnuastro.translist $topwebcheckout/gnuastro/
 
-# `index.html' must be just a symbolic to `gnuastro.html'. In case this is
+# 'index.html' must be just a symbolic to 'gnuastro.html'. In case this is
 # the first time, CVS might have downloaded a full file and not the
 # link. To be safe, we'll just delete index.html and build a new symbolic
 # link.
@@ -345,7 +345,7 @@ ln -s $topwebcheckout/gnuastro/gnuastro.html 
$topwebcheckout/gnuastro/index.html
 
 
 # Copy the generated files to the proper directory. Note that the majority
-# of the HTML files in `html_node' have not changed, so CVS doesn't check
+# of the HTML files in 'html_node' have not changed, so CVS doesn't check
 # for the file contents, and so will update everything that is copied,
 # therefore we only want to copy the files that have actually changed. But
 # first, reset the IFS variable to its default value.
diff --git a/doc/genauthors b/doc/genauthors
index 42806e9..c9734d8 100755
--- a/doc/genauthors
+++ b/doc/genauthors
@@ -13,7 +13,7 @@
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
 #     Mosè Giordano <mose@gnu.org>
-# Copyright (C) 2016-2019, Free Software Foundation, Inc.
+# Copyright (C) 2016-2021, 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
@@ -38,15 +38,15 @@ echo "Generating authors list for documentation."
 # directory and might be run from there)
 if [ -d $1/.git ]; then
 
-    # We will need to import the `.mailmap' file from the source directory
+    # We will need to import the '.mailmap' file from the source directory
     # temporarily to correct the changing emails (see the comments in
-    # `.mailmap'). Note that this script is run from within the `doc/'
-    # directory. The original `.mailmap' is in the `TOP_SRCDIR', so even
+    # '.mailmap'). Note that this script is run from within the 'doc/'
+    # directory. The original '.mailmap' is in the 'TOP_SRCDIR', so even
     # when the source and build directories are the same, there is no
     # problem.
     #
-    # But in case `.mailmap' already exists (for example the script is run
-    # in the top source directory not from the `doc' directory, or if a
+    # But in case '.mailmap' already exists (for example the script is run
+    # in the top source directory not from the 'doc' directory, or if a
     # symbolic link was already created), we won't do any copying.
     if [ -e .mailmap ]; then keepmailmap=1;
     else                     keepmailmap=0; ln -s $1/.mailmap .mailmap;
@@ -57,8 +57,12 @@ if [ -d $1/.git ]; then
     # (in particular "make -jN" with N > 1), so authors.texi needs to be
     # recreated anyway.
     git --git-dir=$1/.git shortlog --numbered --summary --email --no-merges \
-        | sed -e 's/</ /' -e 's/>/ /' -e 's/@/@@/' \
-              -e "s/è/@\`e/" -e "s/é/@\'e/" \
+        | sed -e 's/</ /' \
+              -e 's/>/ /' \
+              -e 's/@/@@/' \
+              -e "s/è/@\`e/" \
+              -e "s/é/@\'e/" \
+              -e "s/ç/@,{c}/" \
         | awk '{for(i=2;i<NF;++i) printf("%s ", $i); \
                 printf("(%s, %s)@*\n", $NF, $1)}' \
         > $1/doc/authors.texi
diff --git a/doc/gnuastro-figures/README b/doc/gnuastro-figures/README
index 44b56fe..b6c3bbb 100644
--- a/doc/gnuastro-figures/README
+++ b/doc/gnuastro-figures/README
@@ -21,9 +21,9 @@ epicycles.png: This is a combined image from two images on 
Wikipedia (links
 
 Copyright information
 ---------------------
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
 version published by the Free Software Foundation; with no Invariant
-Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
\ No newline at end of file
+Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
diff --git a/doc/gnuastro.en.html b/doc/gnuastro.en.html
index 5da54f4..646e166 100644
--- a/doc/gnuastro.en.html
+++ b/doc/gnuastro.en.html
@@ -5,51 +5,22 @@
   - GNU Project - Free Software Foundation</title>
 
 <style type="text/css"><!--
-#content {
-   max-width: 48em;
-   margin: 0 auto 2.5em;
-}
-#table-of-contents {
-   float: left;
-   margin: 1.3em 0 1.5em 0;
-}
-#table-of-contents h4 {
-   text-align: center;
-   margin-top: .8em;
-}
-#table-of-contents a { text-decoration: none; }
-#table-of-contents a:hover { text-decoration: underline; }
-h3 { clear: both; }
-@media (min-width: 40em) {
-   h3 { clear: none; }
-   #table-of-contents {
-      float: right;
-      margin: 1.3em 0 1em 1.5em;
-   }
+.reduced-width { width: 50em; }
+.summary {
+  font-size: 1em;
+  box-sizing: border-box;
+  border: 1px solid #bbb;
+  margin-top: 1.3em;
 }
+.summary, .pict { width: 15.8em; max-width: 100%; }
+@media (min-width:45em) { .left-column { margin-right: 17.5em; }}
 --></style>
 <!--#include virtual="gnuastro.translist" -->
 <!--#include virtual="/server/banner.html" -->
+<div class="reduced-width">
 <h2>GNU Astronomy Utilities</h2>
+<div class="thin"></div>
 
-<hr class="thin" />
-<div id="table-of-contents" class="emph-box">
-<h4>Table of Contents</h4>
-<ul>
-  <li><a href="#introduction">Introduction</a></li>
-  <li><a href="#download">Download</a></li>
-  <li><a href="#installation">Installation</a></li>
-  <li><a href="#documentation">Documentation</a></li>
-  <li><a href="#mail">Mailing lists</a></li>
-  <li><a href="#bug">Report a Bug</a></li>
-  <li><a href="#contribute">Getting involved</a></li>
-  <li><a href="#cite">How to cite</a></li>
-  <li><a href="#license">Licensing</a></li>
-</ul>
-<hr class="no-display"/>
-</div>
-
-<h3 id="introduction">Introduction</h3>
 <p>The GNU Astronomy Utilities (Gnuastro) is an official GNU package
 consisting of various programs and library functions for the manipulation
 and analysis of astronomical data. All the programs share the same basic
@@ -78,27 +49,43 @@ for entertaining and easy to read real world examples of 
using
 </ul>
 
 
+<div id="table-of-contents" class="summary rounded-corners">
+<hr class="no-display" />
+<h3 class="no-display">Contents</h3>
+<ul>
+  <li><a href="#download">Download</a></li>
+  <li><a href="#installation">Installation</a></li>
+  <li><a href="#documentation">Documentation</a></li>
+  <li><a href="#mail">Mailing lists</a></li>
+  <li><a href="#bug">Report a Bug</a></li>
+  <li><a href="#contribute">Getting involved</a></li>
+  <li><a href="#cite">How to cite</a></li>
+  <li><a href="#recentcite">Recent citations</a></li>
+  <li><a href="#license">Licensing</a></li>
+</ul>
+<hr class="no-display" />
+</div>
 
 
-
+<div class="left-column">
 <h3 id="download">Download</h3>
 
 <p>
   The current stable release
-  is <a href="http://ftp.gnu.org/gnu/gnuastro/gnuastro-0.10.tar.gz";>Gnuastro
-  0.10</a> (August 3rd, 2019).
-  Use <a href="http://ftpmirror.gnu.org/gnuastro/gnuastro-0.10.tar.gz";>a
+  is <strong><a 
href="https://ftp.gnu.org/gnu/gnuastro/gnuastro-0.14.tar.gz";>Gnuastro
+  0.14</a></strong> (January 25th, 2021).
+  Use <a href="https://ftpmirror.gnu.org/gnuastro/gnuastro-0.14.tar.gz";>a
   mirror</a> if possible.
 
   <!-- Comment the test release notice when the test release is not more
        recent than the stable release -->
 
-  <br />New releases are announced
+  <br /><em>New releases are announced
   in <a 
href="https://lists.gnu.org/mailman/listinfo/info-gnuastro";>info-gnuastro</a>.
-  To stay up to date, please subscribe.</p>
+  To stay up to date, please subscribe.</em></p>
 
 <p>For details of the significant changes in this release, please see the
-  <a 
href="https://git.savannah.gnu.org/cgit/gnuastro.git/plain/NEWS?id=gnuastro_v0.10";>NEWS</a>
+  <a 
href="https://git.savannah.gnu.org/cgit/gnuastro.git/plain/NEWS?id=gnuastro_v0.14";>NEWS</a>
   file.</p>
 
 <p>The
@@ -110,14 +97,16 @@ for entertaining and easy to read real world examples of 
using
 <p>Previous versions and PGP signatures are always available
   at <a 
href="https://ftp.gnu.org/gnu/gnuastro";>https://ftp.gnu.org/gnu/gnuastro</a>. 
Use <a href="https://ftpmirror.gnu.org/gnuastro";>a
   mirror</a> if possible. </p>
+</div>
 
 
 
 
-
+<div class="pict narrow">
 <a href="https://repology.org/metapackage/gnuastro/versions"; target="_blank">
-    <img 
src="https://repology.org/badge/vertical-allrepos/gnuastro.svg?header=Gnuastro 
packaging status" alt="Packaging status" align="right">
+    <img 
src="https://repology.org/badge/vertical-allrepos/gnuastro.svg?header=Gnuastro 
packaging status" alt="Packaging status" />
 </a>
+</div>
 
 
 
@@ -162,23 +151,23 @@ for more. As a summary, here are the ways you can 
immediately navigate
 to any major part of the book on the command-line:</p>
 
 <ul>
-  <li><code>info gnuastro</code>: To view the complete Gnuastro book from
+  <li><kbd>info&nbsp;gnuastro</kbd>: To view the complete Gnuastro book from
   the beginning.</li>
-  <li><code>info ProgramName</code>: To view the complete section about a
-    specific program. For example <code>info NoiseChisel</code>
-    or <code>info Crop</code>.</li>
-  <li><code>info astprogname</code>: To only view the "Invoking
-  ProgramName" sub-section of the manual. Each program has this
+  <li><kbd>info&nbsp;<var>ProgramName</var></kbd>: To view the complete 
section about a
+    specific program. For example <kbd>info&nbsp;NoiseChisel</kbd>
+    or <kbd>info&nbsp;Crop</kbd>.</li>
+  <li><kbd>info&nbsp;<var>astprogname</var></kbd>: To only view the "Invoking
+  <var>ProgramName</var>" sub-section of the manual. Each program has this
   subsection which explains the input(s), output(s) and command-line
-  options for that particular program. For example <code>info
-  astnoisechisel</code> or <code>info astcrop</code>.</li>
-  <li><code>astprogname --help</code>: A description and full list of
+  options for that particular program. For example
+  <kbd>info&nbsp;astnoisechisel</kbd> or <kbd>info&nbsp;astcrop</kbd>.</li>
+  <li><kbd><var>astprogname</var>&nbsp;--help</kbd>: A description and full 
list of
   options (classified by context) for this program will be printed. For
-  example <code>astnoisechisel --help</code> or <code>astcrop
-  --help</code>.</li>
-  <li><code>man astprogname</code>: A man page listing the options and
-  arguments of this program. For example <code>man astnoisechisel</code>,
-  or <code>man astcrop</code>.</li>
+  example <kbd>astnoisechisel&nbsp;--help</kbd> or
+  <kbd>astcrop&nbsp;  --help</kbd>.</li>
+  <li><kbd>man&nbsp;<var>astprogname</var></kbd>: A man page listing the 
options and
+  arguments of this program. For example <kbd>man&nbsp;astnoisechisel</kbd>,
+  or <kbd>man&nbsp;astcrop</kbd>.</li>
 </ul>
 
 
@@ -273,14 +262,28 @@ mailing list for advice.</p>
   the full GNU project.</p>
 
 <dl>
+
+<!--
+<dt>Google Summer of Code 2020</dt>
+<dd>Gnuastro has some <a 
href="/software/soc-projects/ideas-2020.html#gnuastro">suggested projects</a> 
for Google Summer of Code (GSoC) 2020.
+  Generally, if you are interested in any of the Gnuastro <a 
href="https://savannah.gnu.org/task/?group=gnuastro";>open tasks</a> for the 
GSoC please get in touch.
+  We are also open to any new suggestion to contribute to Gnuastro through 
GSoC 2020.</dd>
+-->
+
 <dt>Test releases</dt>
 
 <dd>Trying the latest test release (when available) is always appreciated.
-Test releases of Gnuastro can be found at
-<a 
href="https://alpha.gnu.org/gnu/gnuastro/";>http://alpha.gnu.org/gnu/gnuastro/</a>
-(via HTTP) and
-<a 
href="ftp://alpha.gnu.org/gnu/gnuastro/";>ftp://alpha.gnu.org/gnu/gnuastro/</a>
-(via FTP).</dd>
+Official alpha-releases of Gnuastro can be found on the GNU server via
+<a href="https://alpha.gnu.org/gnu/gnuastro/";>HTTPS</a>,&nbsp;
+<a href="http://alpha.gnu.org/gnu/gnuastro/";>HTTP</a> or
+<a href="ftp://alpha.gnu.org/gnu/gnuastro/";>FTP</a>.
+Only one or two of these will be available between the offical releases, but 
they are also archived in the URLs above.
+<br />
+Alternatively, the tarball corresponding to the <a 
href="https://git.savannah.gnu.org/cgit/gnuastro.git/log";>most recent 
commit</a> is also available at <a 
href="https://akhlaghi.org/gnuastro-latest.tar.lz";>https://akhlaghi.org/gnuastro-latest.tar.lz</a>,
 along with its <a href="https://akhlaghi.org/gnuastro.pdf";>PDF manual</a>.
+For the changes compared to the last major release, see the <a 
href="https://git.savannah.gnu.org/cgit/gnuastro.git/tree/NEWS";>NEWS</a> file 
(also available in the tarball).
+Just note that the contents of this tarball constantly change with every 
commit in the Gnuastro history.
+So the tarball can be reproduced by <a 
href="manual/html_node/Bootstrapping.html">bootstrapping</a> that commit, but 
it will not be archived for future direct downloads.
+</dd>
 
 
 <dt>Development</dt>
@@ -314,7 +317,7 @@ mailing lists.</dd>
   system with any one of the following commands (if it works, the first one
   is recommended).
   <pre class="emph-box">git clone git://git.sv.gnu.org/gnuastro
-git clone http://git.sv.gnu.org/r/gnuastro.git</pre>
+git clone https://git.sv.gnu.org/r/gnuastro.git</pre>
 </dd>
 
 <dd>To configure and build the version controlled source, you will need to
@@ -327,11 +330,9 @@ git clone http://git.sv.gnu.org/r/gnuastro.git</pre>
 <dt>Maintainer</dt>
 
 <dd>Gnuastro was created and is currently maintained by
-  <a href="http://akhlaghi.org";>Mohammad Akhlaghi</a>
+  <a href="https://akhlaghi.org";>Mohammad Akhlaghi</a>
 &lt;<a 
href="mailto:mohammad=at=akhlaghi.org";>mohammad::at::akhlaghi.org</a>&gt;.
 Please use the mailing lists for contact. </dd>
-
-
 </dl>
 
 
@@ -352,15 +353,15 @@ Please use the mailing lists for contact. </dd>
 <p>Gnuastro is still new, thus there is no paper fully devoted to
   introducing it and its capabilities yet. Until then, the paper to cite
   for Gnuastro in general is the paper which introduced NoiseChisel
-  (<a href="http://adsabs.harvard.edu/abs/2015ApJS..220....1A";>ApJS 220,
+  (<a href="https://adsabs.harvard.edu/abs/2015ApJS..220....1A";>ApJS 220,
   1</a>). It was the first published paper using Gnuastro. Upon reaching a
   certain point, a paper completely devoted to Gnuastro will be published,
-  see <a 
href="https://www.gnu.org/software/gnuastro/manual/html_node/GNU-Astronomy-Utilities-1_002e0.html";>GNU
+  see <a href="manual/html_node/GNU-Astronomy-Utilities-1_002e0.html">GNU
   Astronomy Utilities 1.0</a>.</p>
 
 <p>Gnuastro is also registered on the Astrophysics Source Code Library
-  (<a href="http://ascl.net";>ASCL</a>)
-  with <a href="http://ascl.net/1801.009";>ascl.net/1801.009</a>. Please use
+  (<a href="https://ascl.net";>ASCL</a>)
+  with <a href="https://ascl.net/1801.009";>ascl.net/1801.009</a>. Please use
   this identifier somewhere in the body of your paper (for example in the
   acknowledgments or first mention of Gnuastro). For the
   bibliography/references list at the end of the paper, please only use the
@@ -370,6 +371,55 @@ Please use the mailing lists for contact. </dd>
 
 
 
+<h3 id="recentcite">Recent citations of Gnuastro</h3>
+<p>Below you can see a list of the 20 recent papers that have cited Gnuastro 
(any of its programs or libraries) as of September 7th, 2020. For the full, and 
up-to-date, list please see the respective <a 
href="https://scholar.google.com/scholar?hl=en&amp;as_sdt=0,5&amp;sciodt=0,5&amp;cites=4207494987138368989&amp;scipsc=&amp;q=&amp;scisbd=1";>Google
 Scholar</a> or <a 
href="https://ui.adsabs.harvard.edu/search/q=citations(bibcode%3A2015ApJS..220....1A)&amp;sort=date%20desc%2C%20bibcode%20de
 [...]
+
+<ol>
+  <li><small>Bilek, M. Duc, P.A., et al. (<b>2020</b>) <i>Census and 
classification of low-surface-brightness structures in nearby early-type 
galaxies from the MATLAS survey</i>. Monthly Notices of the Royal Astronomical 
Society (MNRAS), August 2020. DOI:<a 
href="https://doi.org/10.1093/mnras/staa2248";>10.1093/mnras/staa2248</a>. 
arXiv:<a href="https://arxiv.org/abs/2007.13772";>2007.13772</a>.</small></li>
+
+  <li><small>Sachdeva, S., Ho, L.C., et al. (<b>2020</b>) <i>Correlation of 
Structure and Stellar Properties of Galaxies in Stripe 82</i>. Astrophysical 
Journal. DOI:<a 
href="https://doi.org/10.3847/1538-4357/aba82d";>10.3847/1538-4357/aba82d</a>. 
arXiv:<a href="https://arxiv.org/abs/2007.08328";>2007.08328</a>.</small></li>
+
+  <li><small>Rampazzo, R., Omizzolo, A., et al. (<b>2020</b>) <i>Morphology 
and surface photometry of a sample of isolated early-type galaxies from deep 
imaging</i>. Astronomy &amp; Astrophysics 640, 38. DOI:<a 
href="https://doi.org/10.1051/0004-6361/202038156";>10.1051/0004-6361/202038156</a>.
 arXiv:<a href="https://arxiv.org/abs/2006.05323";>2006.05323</a>.</small></li>
+
+  <li><small> Popescu, M., de León, J., et al. (<b>2020</b>) <i>Physical 
characterization of 2020 AV2, the first known asteroid orbiting inside Venus 
orbit </i>. Monthly Notices of the Royal Astronomical Society (MNRAS), 496, 3, 
3572. DOI:<a 
href="https://doi.org/10.1093/mnras/staa1728";>10.1093/mnras/staa1728</a>. 
arXiv:<a href="https://arxiv.org/abs/2006.08304";>2006.08304</a>.</small></li>
+
+  <li><small>Brough, S., Collins, C., et al. (<b>2020</b>) <i>The Vera Rubin 
Observatory Legacy Survey of Space and Time and the Low Surface Brightness 
Universe</i>. IAU Symposium 355 (in press). arXiv:<a 
href="https://arxiv.org/abs/2001.11067";>2001.11067</a>.</small></li>
+
+  <li><small>Duc, P.A (<b>2020</b>) <i>MATLAS: a deep exploration of the 
surroundings of massive early-type galaxies</i>. IAU Symposium 355 (in press). 
hal-<a 
href="https://hal.archives-ouvertes.fr/hal-02382682";>02382682</a>.</small></li>
+
+  <li><small>Kaviraj, S. (<b>2020</b>) <i>The low-surface-brightness Universe: 
a new frontier in the study of galaxy evolution</i>. IAU Symposium 355 (in 
press). arXiv:<a 
href="https://arxiv.org/abs/2001.01728";>2001.01728</a>.</small></li>
+
+  <li><small>Akhlaghi M., (<b>2020</b>) <i>Carving out the low surface 
brightness universe with NoiseChisel</i>. IAU Symposium 355 (in press). 
arXiv:<a href="https://arxiv.org/abs/1909.11230";>1909.11230</a>.</small></li>
+
+  <li><small>Infante-Sainz, R., Trujillo, I., Román, J., (<b>2020</b>) <i>The 
Sloan Digital Sky Survey extended Point Spread Functions</i>. Monthly Notices 
of the Royal Astronomical Society (MNRAS), 491, 4, 5317. DOI:<a 
href="https://doi.org/10.1093/mnras/stz3111";>10.1093/mnras/stz3111</a>. 
arXiv:<a href="https://arxiv.org/abs/1911.01430";>1911.01430</a>.</small></li>
+
+  <li><small>Habas, R., Marleau, F.R., et al. (<b>2019</b>) <i>Newly 
discovered dwarf galaxies in the MATLAS low density fields</i>. Monthly Notices 
of the Royal Astronomical Society (MNRAS), 491, 2, 1901. DOI:<a 
href="https://doi.org/10.1093/mnras/stz3045";>10.1093/mnras/stz3045</a>. 
arXiv:<a href="https://arxiv.org/abs/1910.13462";>1910.13462</a>.</small></li>
+
+  <li><small>Jones, M.G., Verdes-Montenegro, L., et al. (<b>2019</b>) 
<i>Evolution of compact groups from intermediate to final stages. A case study 
of the H I content of HCG 16</i>. Astronomy &amp; Astrophysics 632, 78. DOI:<a 
href="https://doi.org/10.1051/0004-6361/201936349";>10.1051/0004-6361/201936349</a>.
 arXiv:<a href="https://arxiv.org/abs/1910.03420";>1910.03420</a>.</small></li>
+
+  <li><small>Calvi, R., Rodríguez Espinosa, J.M., et al. (<b>2019</b>) <i>MOS 
spectroscopy of protocluster candidate galaxies at z = 6.5</i>. Monthly Notices 
of the Royal Astronomical Society 489, 329. DOI:<a 
href="https://doi.org/10.1093/mnras/stz2177";>10.1093/mnras/stz2177</a>, 
arXiv:<a href="https://arxiv.org/abs/1908.01827";>1908.01827</a>.</small></li>
+
+  <li><small>Boogaard, L.A., Decarli, R., et al. (<b>2019</b>) <i>The ALMA 
Spectroscopic Survey in the HUDF: Nature and Physical Properties of Gas-mass 
Selected Galaxies Using MUSE Spectroscopy</i>. The Astrophysical Journal 882, 
140B. DOI:<a 
href="https://doi.org/10.3847/1538-4357/ab3102";>10.3847/1538-4357/ab3102</a>, 
arXiv:<a href="https://arxiv.org/abs/1903.09167";>1903.09167</a>.</small></li>
+
+  <li><small>Sachdeva, S., Gogoi, R., et al. (<b>2019</b>) <i>Formation of 
disc galaxies around z~2 </i>. Monthly Notices of the Royal Astronomical 
Society 487, 1795. DOI:<a 
href="https://doi.org/10.1093/mnras/stz1417";>10.1093/mnras/stz1417</a>, 
arXiv:<a href="https://arxiv.org/abs/1905.08432";>1905.08432</a>.</small></li>
+
+  <li><small>de La Vieuville, G., Bina, D., et al. (<b>2019</b>) <i>Faint end 
of the z~3-7 luminosity function of Lyman-alpha emitters behind lensing 
clusters observed with MUSE</i>. Astronomy &amp; Astrophysics, 628, A3. DOI:<a 
href="https://doi.org/10.1051/0004-6361/201834471";>10.1051/0004-6361/201834471</a>,
 arXiv:<a href="https://arxiv.org/abs/1905.13696";>1905.13696</a>.</small></li>
+
+  <li><small>Román, J., Trujillo, I., Montes, M. (<b>2019</b>) <i>Galactic 
cirri in deep optical imaging</i>. Astronomy &amp; Astrophysics (Submitted). 
arXiv:<a href="https://arxiv.org/abs/1907.00978";>1907.00978</a>.</small></li>
+
+  <li><small>Trujillo, I., Beasley, M.A.,  et al. (<b>2019</b>) <i>A distance 
of 13 Mpc resolves the claimed anomalies of the galaxy lacking dark matter</i>. 
Monthly Notices of the Royal Astronomical Society, 486, 1192. DOI:<a 
href="https://doi.org/10.1093/mnras/stz771";>10.1093/mnras/stz771</a>, arXiv:<a 
href="https://arxiv.org/abs/1806.10141";>1806.10141</a>.</small></li>
+
+  <li><small>Hsieh, H.H.; Bannister, M.T., et al. (<b>2019</b>) <i>Maximizing 
LSST Solar System Science: Approaches, Software Tools, and Infrastructure 
Needs</i>. White paper. arXiv:<a 
href="https://arxiv.org/abs/1906.11346";>1906.11346</a>.</small></li>
+
+  <li><small>Martin, G., Kaviraj, S.,  et al. (<b>2019</b>) <i>The formation 
and evolution of low-surface-brightness galaxies</i>. Monthly Notices of the 
Royal Astronomical Society, 485, 796. DOI:<a 
href="https://doi.org/10.1093/mnras/stz356";>10.1093/mnras/stz356</a>, arXiv:<a 
href="https://arxiv.org/abs/1902.04580";>1902.04580</a>.</small></li>
+
+  <li><small>Chandler, C.O., Kueny, J., et al. (<b>2019</b>) <i>Six Years of 
Sustained Activity in (6478) Gault</i>. The Astrophysical Journal Letters 877, 
L12. DOI:<a 
href="https://doi.org/10.3847/2041-8213/ab1aaa";>10.3847/2041-8213/ab1aaa</a>, 
arXiv:<a href="https://arxiv.org/abs/1904.10530";>1904.10530</a>.</small></li>
+</ol>
+
+
+
+
+
 <h3 id="license">Licensing</h3>
 
 <p>GNU Astronomy Utilities (Gnuastro) is free software; you can
@@ -379,6 +429,7 @@ General Public License</a> as published by the Free Software
 Foundation; either version&nbsp;3 of the License, or (at your option)
 any later version.</p>
 
+</div>
 </div><!-- for id="content", starts in the include above -->
 <!--#include virtual="/server/footer.html" -->
 <div id="footer">
@@ -390,22 +441,9 @@ There are also <a href="/contact/">other ways to 
contact</a> the FSF.
 Broken links and other corrections or suggestions can be sent to &lt;<a
 href="mailto:bug-gnuastro=at=gnu.org";>bug-gnuastro::at::gnu.org</a>&gt;.</p>
 
-<p><!-- TRANSLATORS: Ignore the original text in this paragraph,
-        replace it with the translation of these two:
-
-        We work hard and do our best to provide accurate, good quality
-        translations.  However, we are not exempt from imperfection.
-        Please send your comments and general suggestions in this regard
-        to <a href="mailto:web-translators@gnu.org";>
-        &lt;web-translators@gnu.org&gt;</a>.</p>
-
-        <p>For information on coordinating and submitting translations of
-        our web pages, see <a
-        href="/server/standards/README.translations.html">Translations
-        README</a>. -->
-Please see the <a
+<p>Please see the <a
 href="/server/standards/README.translations.html">Translations
-README</a> for information on coordinating and submitting translations
+README</a> for information on coordinating and contributing translations
 of this article.</p>
 </div>
 
diff --git a/doc/gnuastro.fr.html b/doc/gnuastro.fr.html
index 8ba8ceb..14caea5 100644
--- a/doc/gnuastro.fr.html
+++ b/doc/gnuastro.fr.html
@@ -1,61 +1,30 @@
 <!--#set var="ENGLISH_PAGE" value="gnuastro.html" -->
 
 <!--#include virtual="/server/html5-header.fr.html" -->
-<!-- Parent-Version: 1.84 -->
+<!-- Parent-Version: 1.95 -->
 
 <title>Utilitaires GNU pour l'astronomie - Gnuastro -
   Projet GNU - Free Software Foundation</title>
 
 <style type="text/css"><!--
-#content {
-   max-width: 48em;
-   margin: 0 auto 2.5em;
-}
-#table-of-contents {
-   float: left;
-   margin: 1.3em 0 1.5em 0;
-}
-#table-of-contents h4 {
-   text-align: center;
-   margin-top: .8em;
-}
-#table-of-contents a { text-decoration: none; }
-#table-of-contents a:hover { text-decoration: underline; }
-h3 { clear: both; }
-@media (min-width: 40em) {
-   h3 { clear: none; }
-   #table-of-contents {
-      float: right;
-      margin: 1.3em 0 1em 1.5em;
-   }
+.reduced-width { width: 50em; }
+.summary {
+  font-size: 1em;
+  box-sizing: border-box;
+  border: 1px solid #bbb;
+  margin-top: 1.3em;
 }
+.summary, .pict { width: 15.8em; max-width: 100%; }
+@media (min-width:45em) { .left-column { margin-right: 17.5em; }}
 --></style>
 <!--#include virtual="gnuastro.translist" -->
 <!--#include virtual="/server/banner.fr.html" -->
+<div class="reduced-width">
 <h2>Utilitaires GNU pour l'astronomie</h2>
+<div class="thin"></div>
 
-<hr class="thin" />
-<div id="table-of-contents" class="emph-box">
-<h4>Table des Matières</h4>
-<ul>
-  <li><a href="#introduction">Introduction</a></li>
-  <li><a href="#download">Téléchargement</a></li>
-  <li><a href="#installation">Installation</a></li>
-  <li><a href="#documentation">Documentation</a></li>
-  <li><a href="#mail">Listes de diffusion</a></li>
-  <li><a href="#bug">Signalement de bogues</a></li>
-  <li><a href="#contribute">Participer</a></li>
-  <li><a href="#cite">Citations</a></li>
-  <li><a href="#license">Licence</a></li>
-</ul>
-<hr class="no-display"/>
-</div>
-
-
-<h3 id="introduction">Introduction</h3>
-
-<p> Les utilitaires GNU pour l'astronomie (Gnuastro – <cite>GNU Astronomy
-  Utilities</cite>) font partie des logiciels GNU officiels. Il s'agit d'un
+<p> Les utilitaires GNU pour l'astronomie (Gnuastro &ndash; <i>GNU Astronomy
+  Utilities</i>) font partie des logiciels GNU officiels. Il s'agit d'un
   ensemble de programmes séparés pour la manipulation et l'analyse des
   données astronomiques. Les différents utilitaires utilisent tous la même
   interface en ligne de commande pour le confort des utilisateurs et des
@@ -71,29 +40,44 @@ h3 { clear: both; }
   vous être utiles&nbsp;:</p>
 
 <ul>
-  <li><a href="manual/html_node/Quick-start.html">Démarrage rapide</a> –
+  <li><a href="manual/html_node/Quick-start.html">démarrage rapide</a> &ndash;
     installer Gnuastro&nbsp;;</li>
 
-  <li><a href="manual/html_node/GNU-Astronomy-Utilities-list.html">Liste
+  <li><a href="manual/html_node/GNU-Astronomy-Utilities-list.html">liste
     complète</a> des utilitaires GNU pour l'astronomie&nbsp;;</li>
 
-  <li><a href="manual/html_node/Tutorials.html#Tutorials">Tutoriels</a> –
+  <li><a href="manual/html_node/Tutorials.html#Tutorials">tutoriels</a> &ndash;
     exemples concrets d'utilisation, amusants et faciles à lire.</li>
 </ul>
 
+<div id="table-of-contents" class="summary rounded-corners">
+<hr class="no-display" />
+<h3 class="no-display">Contents</h3>
+<ul>
+  <li><a href="#download">Téléchargement</a></li>
+  <li><a href="#installation">Installation</a></li>
+  <li><a href="#documentation">Documentation</a></li>
+  <li><a href="#mail">Listes de diffusion</a></li>
+  <li><a href="#bug">Signalement de bogues</a></li>
+  <li><a href="#contribute">Participer</a></li>
+  <li><a href="#cite">Citations</a></li>
+  <li><a href="#license">Licence</a></li>
+</ul>
+<hr class="no-display" />
+</div>
 
+<div class="left-column">
 <h3 id="download">Téléchargement</h3>
 
 <p>La version stable actuelle
-  est <a href="https://ftp.gnu.org/gnu/gnuastro/gnuastro-0.10.tar.gz";>Gnuastro
-  0.10</a> (sortie le 3 août
-  2019). Utilisez <a 
href="https://ftpmirror.gnu.org/gnuastro/gnuastro-0.10.tar.gz";>un
-  miroir</a> si possible.  <br />Les nouvelles publications sont annoncées
+  est <strong><a 
href="https://ftp.gnu.org/gnu/gnuastro/gnuastro-0.14.tar.gz";>Gnuastro
+  0.14</a></strong> (sortie le 25 janvier 2021). Utilisez <a 
href="https://ftpmirror.gnu.org/gnuastro/gnuastro-0.14.tar.gz";>un
+  miroir</a> si possible.  <br /><em>Les nouvelles versions sont annoncées
   sur <a 
href="https://lists.gnu.org/mailman/listinfo/info-gnuastro";>info-gnuastro</a>.
-  Abonnez-vous pour rester au courant.</p>
+  Abonnez-vous pour rester au courant.</em></p>
 
 <p>Les changements importants sont décrits dans le
-  fichier <a 
href="https://git.savannah.gnu.org/cgit/gnuastro.git/plain/NEWS?id=gnuastro_v0.10";>
+  fichier <a 
href="https://git.savannah.gnu.org/cgit/gnuastro.git/plain/NEWS?id=gnuastro_v0.14";>
   NEWS</a>.</p>
 
 <p>Le lien
@@ -107,32 +91,35 @@ h3 { clear: both; }
   <a 
href="https://ftp.gnu.org/gnu/gnuastro";>https://ftp.gnu.org/gnu/gnuastro</a>.
   Utilisez un <a href="https://ftpmirror.gnu.org/gnuastro";>miroir</a> si
   possible.</p>
+</div>
 
-
+<div class="pict narrow">
 <a href="https://repology.org/metapackage/gnuastro/versions"; target="_blank">
-    <img 
src="https://repology.org/badge/vertical-allrepos/gnuastro.svg?header=Gnuastro 
packaging status" alt="Packaging status" align="right">
+    <img 
src="https://repology.org/badge/vertical-allrepos/gnuastro.svg?header=Gnuastro 
packaging status" alt="Packaging status" />
 </a>
-
+</div>
 
 <h3 id="installation">Installation</h3>
 
-<p>Le chapitre <cite><a href="manual/html_node/Quick-start.html">Quick
-  start</a></cite> du <a href="manual/">Livre de Gnuastro</a> résume les
+<p>Le chapitre <i><a href="manual/html_node/Quick-start.html">Quick
+  start</a></i> (démarrage rapide) du <cite><a href="manual/">Livre de
+  Gnuastro</a></cite> résume les
   commandes de compilation et d'installation.</p>
 
 <p>Gnuastro n'a que trois dépendances obligatoires&nbsp;:
   <a href="manual/html_node/GNU-Scientific-Library.html">GNU Scientific
   Library</a>, <a href="manual/html_node/CFITSIO.html">CFITSIO</a> et
   <a href="manual/html_node/WCSLIB.html">WCSLIB</a>.<br />Dans le
-  chapitre <a 
href="manual/html_node/Dependencies-from-package-managers.html">Dépendances
-  des gestionnaires de paquets</a> vous trouverez les commandes pour une
+  chapitre <i><a 
href="manual/html_node/Dependencies-from-package-managers.html">
+  Dependencies from package  managers</a></i> (dépendances des gestionnaires
+  de paquets), vous trouverez les commandes pour une
   installation facile des dépendances de Gnuastro en utilisant des
   logiciels comme <code>apt-get</code>, <code>dnf</code>
   (ou <code>yum</code>),
   <code>pacman</code>, <code>zypper</code> ou <code>brew</code>.</p>
 
-<p>Dans le chapitre <cite><a href="manual/html_node/Installation.html">
-  Installation</a></cite>, vous trouverez des explications complètes sur
+<p>Dans le chapitre <i><a href="manual/html_node/Installation.html">
+  Installation</a></i>, vous trouverez des explications complètes sur
   les
   <a href="manual/html_node/Dependencies.html">dépendances</a>, le
   <a href="manual/html_node/Downloading-the-source.html">téléchargement</a>,
@@ -140,45 +127,46 @@ h3 { clear: both; }
   l'installation</a> de Gnuastro.</p>
 
 
-<h3 id="documentation">Documentation – le Livre</h3>
+<h3 id="documentation">Documentation &ndash; le Livre</h3>
 
-<p>Le <a href="manual/">Livre de Gnuastro</a> (documentation officielle, ou
+<p>Le <cite><a href="manual/">Livre de Gnuastro</a></cite> (documentation 
officielle, ou
    manuel) est disponible en ligne en différent formats, de même que la
    documentation de <a href="/manual/manual.html">la plupart des logiciels
    GNU</a>. Après installation, vous pouvez accéder en ligne de commande au
    livre complet ou aux parties se rapportant à des utilitaires
    particuliers (au
    format <a 
href="/software/texinfo/manual/texinfo/html_node/Info-Files.html#Info-Files">
-   Info</a>). Vous trouverez des information complètes
-   dans <cite><a href="manual/html_node/Getting-help.html">Getting
-   help</a></cite>. En résumé, voici comment vous pouvez ouvrir
+   Info</a>). Vous trouverez des informations complètes
+   dans <i><a href="manual/html_node/Getting-help.html">Getting
+   help</a></i> (obtenir de l'aide). En résumé, voici comment vous pouvez 
ouvrir
    immédiatement en ligne de commande les parties les plus importantes du
    livre&nbsp;:</p>
 
 <ul>
-  <li><code>info&nbsp;gnuastro</code> pour voir le livre complet
+  <li><kbd>info&nbsp;gnuastro</kbd> pour voir le livre complet
     depuis le début.</li>
 
-  <li><code>info&nbsp;ProgramName</code> pour voir l'ensemble de la section
-    concernant un utilitaire particulier. Par exemple
-    <code>info&nbsp;NoiseChisel</code> ou
-    <code>info&nbsp;ImageCrop</code>.</li>
+  <li><kbd>info&nbsp;<var>ProgramName</var></kbd> pour voir tout ce qui
+    concerne un utilitaire particulier. Par exemple
+    <kbd>info&nbsp;NoiseChisel</kbd> ou
+    <kbd>info&nbsp;ImageCrop</kbd>.</li>
 
-  <li><code>info&nbsp;astprogname</code> pour voir uniquement la sous-section
-    <cite>Invoking ProgramName</cite> du manuel. Chaque programme a une
-    sous-section de ce type qui explique les entrées/sorties et les options
-    de la ligne de commande pour ce programme. Par exemple <code>info
-    astnoisechisel</code> ou <code>info&nbsp;astimgcrop</code>.</li>
+  <li><kbd>info&nbsp;<var>astprogname</var></kbd> pour voir uniquement le 
sous-chapitre
+    « Invoking <var>ProgramName</var> » (invoquer
+    <var>nom_du_programme</var>) du manuel. Chaque programme a une
+    sous-chapitre de ce type qui explique les entrées/sorties et les options
+    de la ligne de commande pour ce programme. Par exemple
+    <kbd>info&nbsp;astnoisechisel</kbd> ou 
<kbd>info&nbsp;astimgcrop</kbd>.</li>
 
-  <li><code>astprogname&nbsp;--help</code> affichera une description de ce
+  <li><kbd><var>astprogname</var>&nbsp;--help</kbd> affichera une description 
de ce
     programme ainsi que la liste complète des options (classées par
-    contexte).  Par exemple <code>astnoisechisel&nbsp;--help</code> ou
-    <code>astimgcrop&nbsp;--help</code>.</li>
+    contexte).  Par exemple <kbd>astnoisechisel&nbsp;--help</kbd> ou
+    <kbd>astimgcrop&nbsp;--help</kbd>.</li>
 
-  <li><code>man&nbsp;astprogname</code> affichera une page de manuel
+  <li><kbd>man&nbsp;<var>astprogname</var></kbd> affichera une page de manuel
     donnant la liste des options et des arguments du programme. Par exemple
-    <code>man&nbsp;astnoisechisel</code>, ou
-    <code>man&nbsp;astimgcrop</code>.</li>
+    <kbd>man&nbsp;astnoisechisel</kbd>, ou
+    <kbd>man&nbsp;astimgcrop</kbd>.</li>
 </ul>
 
 
@@ -188,24 +176,24 @@ h3 { clear: both; }
 <ul>
   <li><a
     
href="https://lists.gnu.org/mailman/listinfo/info-gnuastro";>info-gnuastro</a>
-    pour toutes les annonces officielles.</li>
+    pour toutes les annonces officielles&nbsp;;</li>
 
   <li><a 
href="https://lists.gnu.org/mailman/listinfo/help-gnuastro";>help-gnuastro</a>
     pour contacter les utilisateurs expérimentés et les développeurs de
     Gnuastro à propos de problèmes d'utilisation, et leur demander conseil
     (il serait utile que vous lisiez d'abord <a href="manual/">la
-    documentation</a>).</li>
+    documentation</a>)&nbsp;;</li>
 
   <li><a 
href="https://lists.gnu.org/mailman/listinfo/bug-gnuastro";>bug-gnuastro</a>
     pour signaler un bogue ou suggérer de nouvelles fonctions
-    (voir <cite><a href="manual/html_node/Report-a-bug.html">Report a
-    bug</a></cite> et
-    <cite><a href="manual/html_node/Suggest-new-feature.html">Suggest new
-    feature</a></cite> dans la documentation).</li>
+    (voir <i><a href="manual/html_node/Report-a-bug.html">Report a
+    bug</a></i> et
+    <i><a href="manual/html_node/Suggest-new-feature.html">Suggest new
+    feature</a></i> dans la documentation)&nbsp;;</li>
 
   <li><a 
href="https://lists.gnu.org/mailman/listinfo/gnuastro-devel";>gnuastro-devel</a>
     pour discuter du développement (cette liste est surtout destinée aux
-    développeurs de Gnuastro).</li>
+    développeurs de Gnuastro)&nbsp;;</li>
 
   <li><a 
href="https://lists.gnu.org/mailman/listinfo/gnuastro-commits";>gnuastro-commits</a>
     pour annoncer les commits faits dans le dépôt officiel de Gnuastro
@@ -221,16 +209,16 @@ h3 { clear: both; }
 
 <h3 id="bug">Signalement de bogues</h3>
 
-<p>La section <cite><a href="manual/html_node/Report-a-bug.html">Report a
-  bug</a></cite> explique en détail la marche à suivre pour signaler un
-  bogue. Lisez cette section s'il vous plaît&nbsp;! En résumé, allez voir
+<p>Le chapitre <i><a href="manual/html_node/Report-a-bug.html">Report a
+  bug</a></i> explique en détail la marche à suivre pour signaler un
+  bogue. Lisez-le s'il vous plaît&nbsp;! En résumé, allez voir
   en premier lieu
   les <a href="https://savannah.gnu.org/bugs/?group=gnuastro";
   target="_blank">archives des bogues de Gnuastro</a>. Cliquez sur
-  <cite>Display Criteria</cite> (au-dessus de la liste), puis dans le menu
-  déroulant <cite>Open/Closed</cite> choisissez <cite>Any</cite>, et
-  dans le menu <cite>Category</cite> choisissez la section de Gnuastro qui
-  correspond à votre bogue . En cliquant sur le bouton <cite>Apply</cite>,
+  <i>Display Criteria</i> (au-dessus de la liste), puis dans le menu
+  déroulant <i>Open/Closed</i> choisissez « Any », et
+  dans le menu <i>Category</i> choisissez le chapitre de Gnuastro qui
+  correspond à votre bogue. En cliquant sur le bouton « Apply »,
   seuls les bogues pertinents s'afficheront. Les rubriques en vert sont
   fermées et correspondent à des bogues résolus, alors que les rubriques
   en rouge sont toujours ouvertes. Si votre bogue n'est pas dans la liste,
@@ -243,10 +231,10 @@ h3 { clear: both; }
   <li><a 
href="https://savannah.gnu.org/support/?func=additem&amp;group=gnuastro";
     target="_blank">Soumettez votre bogue</a> sur la page principale de
     gestion du projet. C'est le méthode que nous recommandons. Remplissez
-    aussi les métadonnées autant que possible (<cite>Category, Item
-    Group,</cite> etc.)</li>
+    aussi les métadonnées autant que possible (<i>Category, Item
+    Group,</i> etc.)</li>
 
-  <li>Écrivez à
+  <li>Ou bien, écrivez à
     &lt;<a 
href="mailto:bug-gnuastro=at=gnu.org";>bug-gnuastro::at::gnu.org</a>&gt;,
     qui est l'une des <a href="#mail">listes de diffusion</a> de Gnuastro.
     Ce sera moins rapide que la première option&nbsp;: à cause du grand
@@ -262,8 +250,8 @@ h3 { clear: both; }
   pour tout un chacun de le hacker facilement (d'ajouter une nouvelle
   fonctionnalité, de changer une fonctionnalité préexistante, de corriger
   un problème, ou de comprendre ce qui se passe sous le capot)&nbsp;; voir
-  <cite><a href="manual/html_node/Science-and-its-tools.html">Science and
-  its tools</a></cite> (la science et ses outils). Vous êtes donc
+  <i><a href="manual/html_node/Science-and-its-tools.html">Science and
+  its tools</a></i> (la science et ses outils). Vous êtes donc
   cordialement invité à apporter votre contribution&nbsp;; vous y êtes même
   vivement encouragé&nbsp;! Un chapitre complet est dédié au
   <a href="manual/html_node/Developing.html">développement</a> pour que
@@ -272,6 +260,14 @@ h3 { clear: both; }
   également <a href="/help/help.html">Comment aider GNU</a>.</p>
 
 <dl>
+
+<!--
+<dt>Google Summer of Code 2020</dt>
+<dd>Gnuastro <a 
href="https://www.gnu.org/software/soc-projects/ideas-2020.html#gnuastro";>propose
 des projets</a> pour Google Summer of Code (GSoC) 2020.
+  En général, si vous êtes intéressé par <a 
href="https://savannah.gnu.org/task/?group=gnuastro";>l'une des tâches 
ouvertes</a> de Gnuastro pour le GSoC, veuillez nous contacter.
+  Nous sommes également ouverts à toute nouvelle suggestion de contribuer à 
Gnuastro via GSoC 2020.</dd>
+-->
+
 <dt>Versions de test</dt>
 
 <dd>L'essai de la dernière version de test (quand elle est disponible) est
@@ -284,7 +280,7 @@ h3 { clear: both; }
 
 <dd>Gnuastro est toujours en phase de développement actif. Donc, si cela
   vous intéresse, jetez un œil au
-  chapitre <cite><a 
href="manual/html_node/Developing.html">Developing</a></cite>
+  chapitre <i><a href="manual/html_node/Developing.html">Developing</a></i>
   de la documentation et commencez à hacker, ou même créez vous-même un
   programme à l'intérieur de Gnuastro (il y a un modèle pour vous aider).
   Vous trouverez les sources de développement, les trackers de
@@ -293,7 +289,7 @@ h3 { clear: both; }
   (fonctionnalités dont l'ajout est prévu), ainsi que d'autres
   renseignements,
   sur <a href="https://savannah.gnu.org/projects/gnuastro/";>la page du
-  project Gnuastro</a> (hébergé
+  projet Gnuastro</a> (hébergé
   par <a href="https://savannah.gnu.org";>savannah.gnu.org</a>). Les
   trackers peuvent être un bon point de départ si vous voulez vous mettre à
   coder.  Pour vous tenir au courant du développement de Gnuastro,
@@ -314,21 +310,21 @@ h3 { clear: both; }
 
   <pre class="emph-box">
 git clone git://git.sv.gnu.org/gnuastro.git
-git clone http://git.sv.gnu.org/r/gnuastro.git</pre>
+git clone https://git.sv.gnu.org/r/gnuastro.git</pre>
 </dd>
 
 <dd>Pour configurer et compiler les sources sous contrôle de version, vous
   allez avoir besoin de
   <a href="manual/html_node/Bootstrapping.html">bootstrap</a> (voir également
   les <a href="manual/html_node/Bootstrapping-dependencies.html">dépendances
-  de bootstrap</a>). Vous trouverez dans <cite><a
-  href="manual/html_node/Forking-tutorial.html">Forking tutorial</a></cite>
+  de bootstrap</a>). Vous trouverez dans <i><a
+  href="manual/html_node/Forking-tutorial.html">Forking tutorial</a></i>
   la description d'une méthode de travail utilisée par le projet Gnuastro.
 </dd>
 
 <dt>Maintenance</dt>
 
-<dd>Gnuastro a été créé par <a href="http://akhlaghi.org";>Mohammad
+<dd>Gnuastro a été créé par <a href="https://akhlaghi.org";>Mohammad
   Akhlaghi</a>
   &lt;<a 
href="mailto:mohammad=at=akhlaghi.org";>mohammad::at::akhlaghi.org</a>&gt;,
   qui en assure actuellement la maintenance. Merci d'utiliser les listes de
@@ -354,17 +350,17 @@ git clone http://git.sv.gnu.org/r/gnuastro.git</pre>
   fonctionnalités n'ont pas encore fait l'objet d'un article complet. Tant que
   cet article n'est pas paru, la référence à donner pour Gnuastro dans son
   ensemble est la présentation de NoiseChisel (<a
-  href="http://adsabs.harvard.edu/abs/2015ApJS..220....1A";>ApJS 220, 1</a>). Il
+  href="https://adsabs.harvard.edu/abs/2015ApJS..220....1A";>ApJS 220, 1</a>). 
Il
   s'agit de la première publication utilisant Gnuastro. Lorsque ce dernier aura
   atteint une maturité suffisante, une publication lui sera entièrement dédiée.
   Voir <a
-  
href="https://www.gnu.org/software/gnuastro/manual/html_node/GNU-Astronomy-Utilities-1_002e0.html";>
+  href="manual/html_node/GNU-Astronomy-Utilities-1_002e0.html">
   GNU Astronomy Utilities 1.0</a>.</p>
 
 <p>Gnuastro est également répertorié dans la Bibliothèque de code source pour
-  l'astrophysique (<a href="http://ascl.net";>ASCL</a> — <cite>Astrophysics
+  l'astrophysique (<a href="https://ascl.net";>ASCL</a> &ndash; 
<cite>Astrophysics
   Source Code Library</cite>) sous le numéro <a
-  href="http://ascl.net/1801.009";>ascl.net/1801.009</a>. Veuillez utiliser cet
+  href="https://ascl.net/1801.009";>ascl.net/1801.009</a>. Veuillez utiliser cet
   identifiant quelque part dans votre article (par exemple dans les
   remerciements ou lorsque vous mentionnerez Gnuastro pour la première fois).
   Dans la bibliographie ou la liste de références en fin d'article, utilisez
@@ -372,48 +368,30 @@ git clone http://git.sv.gnu.org/r/gnuastro.git</pre>
 
 <h3 id="license">Licence</h3>
 
-<p><cite>GNU Astronomy Utilities</cite> (Gnuastro) est un logiciel
+<p><i>GNU Astronomy Utilities</i> (Gnuastro) est un logiciel
   libre&nbsp;; vous pouvez le redistribuer et/ou le modifier selon les
   termes de la <a href="/licenses/gpl.html" rel="license">licence publique
-  générale GNU</a> telle que publiée par la <cite>Free Software
-  Foundation</cite> (Fondation pour le logiciel libre)&nbsp;; soit en
+  générale GNU</a> telle que publiée par la <i>Free Software
+  Foundation</i> (Fondation pour le logiciel libre)&nbsp;; soit en
   version&nbsp;3 de la licence, soit (à votre convenance) en version
   ultérieure.</p>
 
+</div>
 </div><!-- for id="content", starts in the include above -->
 <!--#include virtual="/server/footer.fr.html" -->
 <div id="footer">
+
 <div class="unprintable">
 
 <p>Veuillez envoyer les requêtes concernant la FSF et GNU à
 &lt;<a href="mailto:gnu@gnu.org";>gnu@gnu.org</a>&gt;. Il existe
 aussi <a href="/contact/">d'autres moyens de contacter</a> la FSF. Les
-liens orphelins et autres corrections ou suggestions peuvent être signalés
-à
-&lt;<a 
href="mailto:bug-gnuastro=at=gnu.org";>bug-gnuastro::at::gnu.org</a>&gt;.</p>
-
-<p><!-- TRANSLATORS: Ignore the original text in this paragraph,
-        replace it with the translation of these two:
-
-        We work hard and do our best to provide accurate, good quality
-        translations.  However, we are not exempt from imperfection.
-        Please send your comments and general suggestions in this regard
-        to <a href="mailto:web-translators@gnu.org";>
-        &lt;web-translators@gnu.org&gt;</a>.</p>
-
-        <p>For information on coordinating and submitting translations of
-        our web pages, see <a
-        href="/server/standards/README.translations.html">Translations
-            README</a>. -->
-
-Nous faisons le maximum pour proposer des traductions fidèles et de bonne
-qualité, mais nous ne sommes pas parfaits. Merci d'adresser vos
-commentaires sur cette page, ainsi que vos suggestions d'ordre général sur
-les traductions, à
-&lt;<a 
href="mailto:web-translators@gnu.org";>web-translators@gnu.org</a>&gt;.</p>
-<p>Pour tout renseignement sur la coordination et la soumission des
-traductions de nos pages web, reportez-vous
-au <a href="/server/standards/README.translations.html">guide de
+liens orphelins et autres corrections ou suggestions peuvent être signalés à
+&lt;<a href="mailto:bug-gnuastro=at=gnu.org";>bug-gnuastro::at::gnu.org</a>&gt;.
+</p>
+
+<p>Si vous souhaitez nous aider à traduire les pages web de gnu.org, merci
+de consulter le <a href="/server/standards/README.translations.html">guide de
 traduction</a>.</p>
 </div>
 
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index 8b4dbc7..54efea1 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -36,7 +36,7 @@
 This book documents version @value{VERSION} of the GNU Astronomy Utilities 
(Gnuastro).
 Gnuastro provides various programs and libraries for astronomical data 
manipulation and analysis.
 
-Copyright @copyright{} 2015-2019 Free Software Foundation, Inc.
+Copyright @copyright{} 2015-2021, Free Software Foundation, Inc.
 
 @quotation
 Permission is granted to copy, distribute and/or modify this document under 
the terms of the GNU Free Documentation License, Version 1.3 or any later 
version published by the Free Software Foundation; with no Invariant Sections, 
no Front-Cover Texts, and no Back-Cover Texts.
@@ -93,6 +93,9 @@ A copy of the license is included in the section entitled 
``GNU Free Documentati
 * Segment: (gnuastro)Segment. Segment detections based on signal structure.
 * astsegment: (gnuastro)Invoking astsegment. Options to Segment.
 
+* Query: (gnuastro)Query. Access remote databases for downloading data.
+* astquery: (gnuastro)Invoking astquery. Options to Query.
+
 * Statistics: (gnuastro)Statistics. Get image Statistics.
 * aststatistics: (gnuastro)Invoking aststatistics. Options to Statistics.
 
@@ -104,6 +107,8 @@ A copy of the license is included in the section entitled 
``GNU Free Documentati
 
 * astscript-sort-by-night: (gnuastro)Invoking astscript-sort-by-night. Options 
to this script
 
+* astscript-make-ds9-reg: (gnuastro)Invoking astscript-make-ds9-reg. Options 
to this script
+
 @end direntry
 
 
@@ -133,7 +138,7 @@ A copy of the license is included in the section entitled 
``GNU Free Documentati
 @author Mohammad Akhlaghi
 
 @page
-Gnuastro (source code, book and webpage) authors (sorted by number of
+Gnuastro (source code, book and web page) authors (sorted by number of
 commits):
 @quotation
 @include authors.texi
@@ -251,17 +256,26 @@ General program usage tutorial
 * Building custom programs with the library::  Easy way to build new programs.
 * Option management and configuration files::  Dealing with options and 
configuring them.
 * Warping to a new pixel grid::  Transforming/warping the dataset.
-* Multiextension FITS files NoiseChisel's output::  Using extensions in FITS 
files.
+* NoiseChisel and Multiextension FITS files::  Running NoiseChisel and having 
multiple HDUs.
 * NoiseChisel optimization for detection::  Check NoiseChisel's operation and 
improve it.
 * NoiseChisel optimization for storage::  Dramatically decrease output's 
volume.
 * Segmentation and making a catalog::  Finding true peaks and creating a 
catalog.
 * Working with catalogs estimating colors::  Estimating colors using the 
catalogs.
+* Column statistics color-magnitude diagram::  Visualizing column correlations.
 * Aperture photometry::         Doing photometry on a fixed aperture.
+* Matching catalogs::           Easily find corresponding rows from two 
catalogs.
 * Finding reddest clumps and visual inspection::  Selecting some targets and 
inspecting them.
+* Writing scripts to automate the steps::  Scripts will greatly help in 
re-doing things fast.
 * Citing and acknowledging Gnuastro::  How to cite and acknowledge Gnuastro in 
your papers.
 
 Detecting large extended targets
 
+* Downloading and validating input data::  How to get and check the input data.
+* NoiseChisel optimization::    Detect the extended and diffuse wings.
+* Achieved surface brightness level::  Calculate the outer surface brightness.
+
+Downloading and validating input data
+
 * NoiseChisel optimization::    Optimize NoiseChisel to dig very deep.
 * Achieved surface brightness level::  Measure how much you detected.
 
@@ -317,6 +331,7 @@ Common program behavior
 * Installed scripts::           Installed Bash scripts, not compiled programs.
 * Multi-threaded operations::   How threads are managed in Gnuastro.
 * Numeric data types::          Different types and how to specify them.
+* Memory management::           How memory is allocated (in RAM or HDD/SSD).
 * Tables::                      Recognized table formats.
 * Tessellation::                Tile the dataset into non-overlapping bins.
 * Automatic output::            About automatic output names.
@@ -372,9 +387,9 @@ Recognized table formats
 Data containers
 
 * Fits::                        View and manipulate extensions and keywords.
-* Sort FITS files by night::    Installed script to sort FITS files by obs 
night.
 * ConvertType::                 Convert data to various formats.
 * Table::                       Read and Write FITS tables to plain text.
+* Query::                       Import data from external databases.
 
 Fits
 
@@ -382,12 +397,8 @@ Fits
 
 Invoking Fits
 
-* HDU manipulation::            Manipulate HDUs within a FITS file.
-* Keyword manipulation::        Manipulate metadata keywords in a HDU
-
-Sort FITS files by night
-
-* Invoking astscript-sort-by-night::  Inputs and outputs to this script.
+* HDU information and manipulation::  Learn about the HDUs and move them.
+* Keyword inspection and manipulation::  Manipulate metadata keywords in a HDU
 
 ConvertType
 
@@ -400,6 +411,11 @@ Table
 * Column arithmetic::           How to do operations on table columns.
 * Invoking asttable::           Options and arguments to Table.
 
+Query
+
+* Available databases::         List of available databases to Query.
+* Invoking astquery::           Inputs, outputs and configuration of Query.
+
 Data manipulation
 
 * Crop::                        Crop region(s) from a dataset.
@@ -465,14 +481,22 @@ Data analysis
 * Segment::                     Segment detections based on signal structure.
 * MakeCatalog::                 Catalog from input and labeled images.
 * Match::                       Match two datasets.
+* Sort FITS files by night::    Sort and classify images in separate nights.
+* SAO DS9 region files from table::  Table's positional columns into DS9 
region file.
 
 Statistics
 
 * Histogram and Cumulative Frequency Plot::  Basic definitions.
+* 2D Histograms::               Plotting the distribution of two variables.
 * Sigma clipping::              Definition of @mymath{\sigma}-clipping.
 * Sky value::                   Definition and derivation of the Sky value.
 * Invoking aststatistics::      Arguments and options to Statistics.
 
+2D Histograms
+
+* 2D histogram as a table::     Format and usage in table format.
+* 2D histogram as an image::    Format and usage in image format
+
 Sky value
 
 * Sky value definition::        Definition of the Sky/reference value.
@@ -481,7 +505,7 @@ Sky value
 
 NoiseChisel
 
-* NoiseChisel changes after publication::  NoiseChisel updates after paper's 
publication.
+* NoiseChisel changes after publication::  Updates since published papers.
 * Invoking astnoisechisel::     Options and arguments for NoiseChisel.
 
 Invoking NoiseChisel
@@ -492,7 +516,6 @@ Invoking NoiseChisel
 
 Segment
 
-* Segment changes after publication::  Segment updates after paper's 
publication.
 * Invoking astsegment::         Inputs, outputs and options to Segment
 
 Invoking Segment
@@ -520,6 +543,14 @@ Match
 
 * Invoking astmatch::           Inputs, outputs and options of Match
 
+Sort FITS files by night
+
+* Invoking astscript-sort-by-night::  Inputs and outputs to this script.
+
+SAO DS9 region files from table
+
+* Invoking astscript-make-ds9-reg::  How to call astscript-make-ds9-reg
+
 Modeling and fitting
 
 * MakeProfiles::                Making mock galaxies and stars.
@@ -529,7 +560,7 @@ MakeProfiles
 
 * Modeling basics::             Astronomical modeling basics.
 * If convolving afterwards::    Considerations for convolving later.
-* Flux Brightness and magnitude::  About these measures of energy.
+* Brightness flux magnitude::   About these measures of energy.
 * Profile magnitude::           Definition of total profile magnitude.
 * Invoking astmkprof::          Inputs and Options for MakeProfiles.
 
@@ -574,7 +605,8 @@ CosmicCalculator
 Invoking CosmicCalculator
 
 * CosmicCalculator input options::  Options to specify input conditions.
-* CosmicCalculator specific calculations::  Requesting specific outputs.
+* CosmicCalculator basic cosmology calculations::  Like distance modulus, 
distances and etc.
+* CosmicCalculator spectral line calculations::  How they get affected by 
redshift.
 
 Library
 
@@ -613,6 +645,7 @@ Gnuastro library
 * Bounding box::                Finding the bounding box.
 * Polygons::                    Working with the vertices of a polygon.
 * Qsort functions::             Helper functions for Qsort.
+* K-d tree::                    Space partitioning in K dimensions.
 * Permutations::                Re-order (or permute) the values in a dataset.
 * Matching::                    Matching catalogs based on position.
 * Statistical operations::      Functions for basic statistics.
@@ -621,6 +654,7 @@ Gnuastro library
 * Convolution functions::       Library functions to do convolution.
 * Interpolation::               Interpolate (over blank values possibly).
 * Git wrappers::                Wrappers for functions in libgit2.
+* Unit conversion library (@file{units.h})::  Convert between units.
 * Spectral lines library::      Functions for operating on Spectral lines.
 * Cosmology library::           Cosmological calculations.
 
@@ -763,7 +797,7 @@ As discussed in @ref{Science and its tools} this is a 
founding principle of the
 The latest official release tarball is always available as 
@url{http://ftp.gnu.org/gnu/gnuastro/gnuastro-latest.tar.gz, 
@file{gnuastro-latest.tar.gz}}.
 For better compression (faster download), and robust archival features, an 
@url{http://www.nongnu.org/lzip/lzip.html, Lzip} compressed tarball is also 
available at @url{http://ftp.gnu.org/gnu/gnuastro/gnuastro-latest.tar.lz, 
@file{gnuastro-latest.tar.lz}}, see @ref{Release tarball} for more details on 
the tarball release@footnote{The Gzip library and program are commonly 
available on most systems.
 However, Gnuastro recommends Lzip as described above and the beta-releases are 
also only distributed in @file{tar.lz}.
-You can download and install Lzip's source (in @file{.tar.gz} format) from its 
webpage and follow the same process as below: Lzip has no dependencies, so 
simply decompress, then run @command{./configure}, @command{make}, 
@command{sudo make install}.}.
+You can download and install Lzip's source (in @file{.tar.gz} format) from its 
web page and follow the same process as below: Lzip has no dependencies, so 
simply decompress, then run @command{./configure}, @command{make}, 
@command{sudo make install}.}.
 
 Let's assume the downloaded tarball is in the @file{TOPGNUASTRO} directory.
 The first two commands below can be used to decompress the source.
@@ -1251,15 +1285,15 @@ There are generally two ways to inform us of bugs:
 Send a mail to @code{bug-gnuastro@@gnu.org}.
 Any mail you send to this address will be distributed through the bug-gnuastro 
mailing 
list@footnote{@url{https://lists.gnu.org/mailman/listinfo/bug-gnuastro}}.
 This is the simplest way to send us bug reports.
-The developers will then register the bug into the project webpage (next 
choice) for you.
+The developers will then register the bug into the project web page (next 
choice) for you.
 
 @cindex Gnuastro project page
 @cindex Support request manager
 @cindex Submit new tracker item
 @cindex Anonymous bug submission
 @item
-Use the Gnuastro project webpage at 
@url{https://savannah.gnu.org/projects/gnuastro/}: There are two ways to get to 
the submission page as listed below.
-Fill in the form as described below and submit it (see @ref{Gnuastro project 
webpage} for more on the project webpage).
+Use the Gnuastro project web page at 
@url{https://savannah.gnu.org/projects/gnuastro/}: There are two ways to get to 
the submission page as listed below.
+Fill in the form as described below and submit it (see @ref{Gnuastro project 
webpage} for more on the project web page).
 
 @itemize
 
@@ -1278,7 +1312,7 @@ In the main body of the page, under the ``Communication 
tools'' section, click o
 @cindex Bug tracker
 @cindex Task tracker
 @cindex Viewing trackers
-Once the items have been registered in the mailing list or webpage, the 
developers will add it to either the ``Bug Tracker'' or ``Task Manager'' 
trackers of the Gnuastro project webpage.
+Once the items have been registered in the mailing list or web page, the 
developers will add it to either the ``Bug Tracker'' or ``Task Manager'' 
trackers of the Gnuastro project web page.
 These two trackers can only be edited by the Gnuastro project developers, but 
they can be browsed by anyone, so you can follow the progress on your bug.
 You are most welcome to join us in developing Gnuastro and fixing the bug you 
have found maybe a good starting point.
 Gnuastro is designed to be easy for anyone to develop (see @ref{Science and 
its tools}) and there is a full chapter devoted to developing it: 
@ref{Developing}.
@@ -1291,7 +1325,7 @@ Gnuastro is designed to be easy for anyone to develop 
(see @ref{Science and its
 @cindex Additions to Gnuastro
 We would always be happy to hear of suggested new features.
 For every program there are already lists of features that we are planning to 
add.
-You can see the current list of plans from the Gnuastro project webpage at 
@url{https://savannah.gnu.org/projects/gnuastro/} and following 
@clicksequence{``Tasks''@click{}``Browse''} on the horizontal menu at the top 
of the page immediately under the title, see @ref{Gnuastro project webpage}.
+You can see the current list of plans from the Gnuastro project web page at 
@url{https://savannah.gnu.org/projects/gnuastro/} and following 
@clicksequence{``Tasks''@click{}``Browse''} on the horizontal menu at the top 
of the page immediately under the title, see @ref{Gnuastro project webpage}.
 If you want to request a feature to an existing program, click on the 
``Display Criteria'' above the list and under ``Category'', choose that 
particular program.
 Under ``Category'' you can also see the existing suggestions for new programs 
or other cases like installation, documentation or libraries.
 Also be sure to set the ``Open/Closed'' value to ``Any''.
@@ -1387,23 +1421,29 @@ Work on improving Gnuastro and making it mature is now 
continuing primarily in t
 In general, we would like to gratefully thank the following people for their 
useful and constructive comments and suggestions (in alphabetical order by 
family name):
 Valentina Abril-melgarejo,
 Marjan Akbari,
+Carlos Allende Prieto,
 Hamed Altafi,
 Roland Bacon,
-Roberto Baena Gall\'e,
+Roberto Baena Gall@'e,
 Zahra Bagheri,
 Karl Berry,
 Leindert Boogaard,
 Nicolas Bouch@'e,
+Stefan Br@"uns,
 Fernando Buitrago,
 Adrian Bunk,
 Rosa Calvi,
+Mark Calabretta
 Nushkia Chamba,
 Benjamin Clement,
 Nima Dehdilani,
 Antonio Diaz Diaz,
+Alexey Dokuchaev,
 Pierre-Alain Duc,
 Elham Eftekhari,
+Paul Eggert,
 Gaspar Galaz,
+Andr@'es García-Serra Romero,
 Th@'er@`ese Godefroy,
 Madusha Gunawardhana,
 Bruno Haible,
@@ -1418,21 +1458,35 @@ Brandon Kelly,
 Mohammad-Reza Khellat,
 Johan Knapen,
 Geoffry Krouchi,
+Martin Kuemmel,
 Floriane Leclercq,
 Alan Lefor,
+Javier Licandro,
+Sebasti@'an Luna Valero,
+Alberto Madrigal,
 Guillaume Mahler,
+Alireza Molaeinezhad,
+Javier Moldon,
 Juan Molina Tobar,
 Francesco Montanari,
+Raphael Morales,
+Carlos Morales Socorro,
 Dmitrii Oparin,
 Bertrand Pain,
 William Pence,
 Mamta Pommier,
+Marcel Popescu,
 Bob Proulx,
+Joseph Putko,
+Samane Raji,
+Francois Ochsenbein,
 Teymoor Saifollahi,
+Joanna Sakowska,
 Elham Saremi,
 Yahya Sefidbakht,
 Alejandro Serrano Borlaff,
 Zahra Sharbaf,
+David Shupe
 Jenny Sorce,
 Lee Spitler,
 Richard Stallman,
@@ -1447,12 +1501,13 @@ David Valls-Gabaud,
 Aaron Watkins,
 Michael H.F. Wilkinson,
 Christopher Willmer,
+Xiuqin Wu,
 Sara Yousefi Taemeh,
 Johannes Zabl.
-The GNU French Translation Team is also managing the French version of the top 
Gnuastro webpage which we highly appreciate.
+The GNU French Translation Team is also managing the French version of the top 
Gnuastro web page which we highly appreciate.
 Finally we should thank all the (sometimes anonymous) people in various online 
forums which patiently answered all our small (but imporant) technical 
questions.
 
-All work on Gnuastro has been voluntary, but the authors are most grateful to 
the following institutions (in chronological order) for hosting us in our 
research.
+All work on Gnuastro has been voluntary, but the authors are most grateful to 
the following institutions (in chronological order) for hosting/supporting us 
in our research.
 Where necessary, these institutions have disclaimed any ownership of the parts 
of Gnuastro that were developed there, thus insuring the freedom of Gnuastro 
for the future (see @ref{Copyright assignment}).
 We highly appreciate their support for free software, and thus free science, 
and therefore a free society.
 
@@ -1461,6 +1516,7 @@ Tohoku University Astronomical Institute, Sendai, Japan.@*
 University of Salento, Lecce, Italy.@*
 Centre de Recherche Astrophysique de Lyon (CRAL), Lyon, France.@*
 Instituto de Astrofisica de Canarias (IAC), Tenerife, Spain.@*
+Google Summer of Code 2020
 @end quotation
 
 
@@ -1593,8 +1649,8 @@ $ astmkprof -P
 [[[ ... Truncated lines ... ]]]
 
 # Columns, by info (see `--searchin'), or number (starting from 1):
- ccol         2           # Center along first FITS axis (horizontal).
- ccol         3           # Center along second FITS axis (vertical).
+ ccol         2           # Coordinate columns (one call for each dimension).
+ ccol         3           # Coordinate columns (one call for each dimension).
  fcol         4           # sersic (1), moffat (2), gaussian (3),
                           # point (4), flat (5), circumference (6).
  rcol         5           # Effective radius or FWHM in pixels.
@@ -1621,6 +1677,7 @@ He wants the output to be 499 pixels by 499 pixels, so he 
can put the center of
 Looking at his drawings of it, he decides a reasonable effective radius for it 
would be 40 pixels on this image pixel scale, he sets the axis ratio and 
position angle to approximately correct values too and finally he sets the 
total magnitude of the profile to 3.44 which he had accurately measured.
 Sufi also decides to truncate both the mock profile and PSF at 5 times the 
respective radius parameters.
 In the end he decides to put four stars on the four corners of the image at 
very low magnitudes as a visual scale.
+While he was preparing the catalog, one of his students approached him and was 
also following the steps.
 
 Using all the information above, he creates the catalog of mock profiles he 
wants in a file named @file{cat.txt} (short for catalog) using his favorite 
text editor and stores it in a directory named @file{simulationtest} in his 
home directory.
 [The @command{cat} command prints the contents of a file, short for 
``concatenation''.
@@ -1636,7 +1693,7 @@ $ emacs cat.txt
 $ ls
 cat.txt
 $ cat cat.txt
-# Column 4: PROFILE_NAME [,str7] Radial profile's functional name
+# Column 4: PROFILE_NAME [,str6] Radial profile's functional name
  1  0.0000   0.0000  moffat  5.000  4.765  0.0000  1.000  30.000  5.000
  2  250.00   250.00  sersic  40.00  1.000  -25.00  0.400  3.4400  5.000
  3  50.000   50.000  point   0.000  0.000  0.0000  0.000  6.0000  0.000
@@ -1646,7 +1703,7 @@ $ cat cat.txt
 @end example
 
 @noindent
-The zero-point magnitude for his observation was 18.
+The zero point magnitude for his observation was 18.
 Now he has all the necessary parameters and runs MakeProfiles with the 
following command:
 
 @example
@@ -1672,25 +1729,32 @@ $ls
 
 @cindex Oversample
 @noindent
-The file @file{0_cat.fits} is the PSF Sufi had asked for and @file{cat.fits} 
is the image containing the other 5 objects.
-The PSF is now available to him as a separate file for the convolution step.
-While he was preparing the catalog, one of his students approached him and was 
also following the steps.
-When he opened the image, the student was surprised to see that all the stars 
are only one pixel and not in the shape of the PSF as we see when we image the 
sky at night.
-So Sufi explained to him that the stars will take the shape of the PSF after 
convolution and this is how they would look if we didn't have an atmosphere or 
an aperture when we took the image.
-The size of the image was also surprising for the student, instead of 499 by 
499, it was 2615 by 2615 pixels (from the command below):
+The file @file{0_cat.fits} is the PSF Sufi had asked for, and @file{cat.fits} 
is the image containing the main objects in the catalog.
+The size of @file{cat.fits} was surprising for the student, instead of 499 by 
499 (as we had requested), it was 2615 by 2615 pixels (from the command below):
 
 @example
 $ astfits cat.fits -h1 | grep NAXIS
 @end example
 
 @noindent
-So Sufi explained why oversampling is important for parts of the image where 
the flux change is significant over a pixel.
-Sufi then explained to him that after convolving we will re-sample the image 
to get our originally desired size/resolution.
-To convolve the image, Sufi ran the following command:
+So Sufi explained why oversampling is important in modeling, especially for 
parts of the image where the flux change is significant over a pixel.
+Recall that when you oversample the model (for example by 5 times), for every 
desired pixel, you get 25 pixels (@mymath{5\times5}).
+Sufi then explained that after convolving (next step below) we will 
down-sample the image to get our originally desired size/resolution.
+
+Sufi then opened @code{cat.fits} [you can use any FITS viewer, for example, 
@command{ds9}].
+After seeing the image, the student complained that only the large elliptical 
model for the Andromeda nebula can be seen in the center.
+He couldn't see the four stars that we had also requested in the catalog.
+So Sufi had to explain that the stars are there in the image, but the reason 
that they aren't visible when looking at the whole image at once, is that they 
only cover a single pixel!
+To prove it, he centered the image around the coordinates 2308 and 2308, where 
one of the stars is located in the over-sampled image [you can do this in 
@command{ds9} by selecting ``Pan'' in the ``Edit'' menu, then clicking around 
that position].
+Sufi then zoomed in to that region and soon, the star's non-zero pixel could 
be clearly seen.
+
+Sufi explained that the stars will take the shape of the PSF (cover an area of 
more than one pixel) after convolution.
+If we didn't have an atmosphere and we didn't need an aperture, then stars 
would only cover a single pixel with normal CCD resolutions.
+So Sufi convolved the image with this command:
 
 @example
 $ astconvolve --kernel=0_cat.fits cat.fits
-Convolve started on Mon Apr  6 16:35:32 953
+Convolve started on Sat Oct  6 16:35:32 953
   - Using 8 CPU threads.
   - Input: cat.fits (hdu: 1)
   - Kernel: 0_cat.fits (hdu: 1)
@@ -1707,13 +1771,15 @@ $ls
 @end example
 
 @noindent
-When convolution finished, Sufi opened the @file{cat_convolved.fits} file and 
showed the effect of convolution to his student and explained to him how a PSF 
with a larger FWHM would make the points even wider.
+When convolution finished, Sufi opened @file{cat_convolved.fits} and the four 
stars could be easily seen now.
+It was interesting for the student that all the flux in that single pixel is 
now distributed over so many pixels (the sum of all the pixels in each 
convolved star is actually equal to the value of the single pixel before 
convolution).
+Sufi explained how a PSF with a larger FWHM would make the points even wider 
than this (distributing their flux in a larger area).
 With the convolved image ready, they were prepared to re-sample it to the 
original pixel scale Sufi had planned [from the @command{$ astmkprof -P} 
command above, recall that MakeProfiles had over-sampled the image by 5 times].
 Sufi explained the basic concepts of warping the image to his student and ran 
Warp with the following command:
 
 @example
 $ astwarp --scale=1/5 --centeroncorner cat_convolved.fits
-Warp started on Mon Apr  6 16:51:59 953
+Warp started on Sat Oct  6 16:51:59 953
  Using 8 CPU threads.
  Input: cat_convolved.fits (hdu: 1)
  matrix:
@@ -1768,13 +1834,13 @@ This is the same dimensions as Sufi had desired in the 
beginning.
 All this trouble was certainly worth it because now there is no dimming on the 
edges of the image and the profile centers are more accurately sampled.
 
 The final step to simulate a real observation would be to add noise to the 
image.
-Sufi set the zeropoint magnitude to the same value that he set when making the 
mock profiles and looking again at his observation log, he had measured the 
background flux near the nebula had a magnitude of 7 that night.
+Sufi set the zero point magnitude to the same value that he set when making 
the mock profiles and looking again at his observation log, he had measured the 
background flux near the nebula had a magnitude of 7 that night.
 So using these values he ran MakeNoise:
 
 @example
 $ astmknoise --zeropoint=18 --background=7 --output=out.fits    \
              cat_convolved_scaled_cropped.fits
-MakeNoise started on Mon Apr  6 17:05:06 953
+MakeNoise started on Sat Oct  6 17:05:06 953
   - Generator type: ranlxs1
   - Generator seed: 1428318100
 MakeNoise finished in:  0.033491 (seconds)
@@ -1869,7 +1935,7 @@ Until now he was using the graphical user interface which 
doesn't have such a fa
 So he left to go and try scripting on his own computer.
 
 Sufi could now get back to his own work and see if the simulated nebula which 
resembled the one in the Andromeda constellation could be detected or not.
-Although it was extremely faint@footnote{The brightness of a diffuse object is 
added over all its pixels to give its final magnitude, see @ref{Flux Brightness 
and magnitude}.
+Although it was extremely faint@footnote{The brightness of a diffuse object is 
added over all its pixels to give its final magnitude, see @ref{Brightness flux 
magnitude}.
 So although the magnitude 3.44 (of the mock nebula) is orders of magnitude 
brighter than 6 (of the stars), the central galaxy is much fainter.
 Put another way, the brightness is distributed over a large area in the case 
of a nebula.}, fortunately it passed his detection tests and he wrote it in the 
draft manuscript that would later become ``Book of fixed stars''.
 He still had to check the other nebula he saw from Yemen and several other 
such objects, but they could wait until tomorrow (thanks to the shell script, 
he only has to define a new catalog).
@@ -1921,13 +1987,16 @@ This will help simulate future situations when you are 
processing your own datas
 * Building custom programs with the library::  Easy way to build new programs.
 * Option management and configuration files::  Dealing with options and 
configuring them.
 * Warping to a new pixel grid::  Transforming/warping the dataset.
-* Multiextension FITS files NoiseChisel's output::  Using extensions in FITS 
files.
+* NoiseChisel and Multiextension FITS files::  Running NoiseChisel and having 
multiple HDUs.
 * NoiseChisel optimization for detection::  Check NoiseChisel's operation and 
improve it.
 * NoiseChisel optimization for storage::  Dramatically decrease output's 
volume.
 * Segmentation and making a catalog::  Finding true peaks and creating a 
catalog.
 * Working with catalogs estimating colors::  Estimating colors using the 
catalogs.
+* Column statistics color-magnitude diagram::  Visualizing column correlations.
 * Aperture photometry::         Doing photometry on a fixed aperture.
+* Matching catalogs::           Easily find corresponding rows from two 
catalogs.
 * Finding reddest clumps and visual inspection::  Selecting some targets and 
inspecting them.
+* Writing scripts to automate the steps::  Scripts will greatly help in 
re-doing things fast.
 * Citing and acknowledging Gnuastro::  How to cite and acknowledge Gnuastro in 
your papers.
 @end menu
 
@@ -2043,20 +2112,21 @@ $ mkdir download
 $ cd download
 $ xdfurl=http://archive.stsci.edu/pub/hlsp/xdf
 $ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f105w_v1_sci.fits
+$ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f125w_v1_sci.fits
 $ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits
 $ cd ..
 @end example
 
 @noindent
-In this tutorial, we'll just use these two filters.
+In this tutorial, we'll just use these three filters.
 Later, you may need to download more filters.
 To do that, you can use the shell's @code{for} loop to download them all in 
series (one after the other@footnote{Note that you only have one port to the 
internet, so downloading in parallel will actually be slower than downloading 
in series.}) with one command like the one below for the WFC3 filters.
-Put this command instead of the two @code{wget} commands above.
+Put this command instead of the three @code{wget} commands above.
 Recall that all the extra spaces, back-slashes (@code{\}), and new lines can 
be ignored if you are typing on the lines on the terminal.
 
 @example
-$ for f in f105w f125w f140w f160w; do                              \
-    wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_"$f"_v1_sci.fits;   \
+$ for f in f105w f125w f140w f160w; do \
+    wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_"$f"_v1_sci.fits; \
   done
 @end example
 
@@ -2088,46 +2158,53 @@ Recall that this is a combined/reduced image of many 
exposures, and the parts th
 In particular, the exposure time of the deep inner region is larger than 4 
times of the outer (more shallower) parts.
 
 To simplify the analysis in this tutorial, we'll only be working on the deep 
field, so let's crop it out of the full dataset.
-Fortunately the XDF survey webpage (above) contains the vertices of the deep 
flat WFC3-IR field.
+Fortunately the XDF survey web page (above) contains the vertices of the deep 
flat WFC3-IR field.
 With Gnuastro's Crop program@footnote{To learn more about the crop program see 
@ref{Crop}.}, you can use those vertices to cutout this deep region from the 
larger image.
 But before that, to keep things organized, let's make a directory called 
@file{flat-ir} and keep the flat (single-depth) regions in that directory (with 
a `@file{xdf-}' suffix for a shorter and easier filename).
 
 @example
 $ mkdir flat-ir
-$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f105w.fits              \
-          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 :    \
-                     53.134517,-27.787144 : 53.161906,-27.807208"     \
+$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f105w.fits \
+          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
+                     53.134517,-27.787144 : 53.161906,-27.807208" \
           download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f105w_v1_sci.fits
-$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f160w.fits              \
-          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 :    \
-                     53.134517,-27.787144 : 53.161906,-27.807208"     \
+
+$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f125w.fits \
+          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
+                     53.134517,-27.787144 : 53.161906,-27.807208" \
+          download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f125w_v1_sci.fits
+
+$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f160w.fits \
+          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
+                     53.134517,-27.787144 : 53.161906,-27.807208" \
           download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits
 @end example
 
-The only thing varying in the two calls to Gnuastro's Crop program is the 
filter name.
-Therefore, to simplify the command, and later allow work on more filters, we 
can use the shell's @code{for} loop.
-Notice how the two places where the filter names (@file{f105w} and 
@file{f160w}) are used above have been replaced with @file{$f} (the shell 
variable that @code{for} will update in every loop) below.
-In such cases, you should generally avoid repeating a command manually and use 
loops like below.
-To generalize this for more filters later, you can simply add the other filter 
names in the first line before the semi-colon (@code{;}).
+The only thing varying in the three calls to Gnuastro's Crop program is the 
filter name!
+Note how everything else is the same.
+In such cases, you should generally avoid repeating a command manually, it is 
prone to many bugs, and as you see, it is very hard to read (didn't you 
suddenly write a @code{7} as an @code{8}?).
+To simplify the command, and later allow work on more filters, we can use the 
shell's @code{for} loop as shown below.
+Notice how the place where the filter names (@file{f105w}, @file{f125w} and 
@file{f160w}) are used above, have been replaced with @file{$f} (the shell 
variable that @code{for} will update in every loop) below.
 
 @example
 $ rm flat-ir/*.fits
-$ for f in f105w f160w; do                                            \
-    astcrop --mode=wcs -h0 --output=flat-ir/xdf-$f.fits               \
-            --polygon="53.187414,-27.779152 : 53.159507,-27.759633 :  \
-                       53.134517,-27.787144 : 53.161906,-27.807208"   \
+$ for f in f105w f125w f160w; do \
+    astcrop --mode=wcs -h0 --output=flat-ir/xdf-$f.fits \
+            --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
+                       53.134517,-27.787144 : 53.161906,-27.807208" \
             download/hlsp_xdf_hst_wfc3ir-60mas_hudf_"$f"_v1_sci.fits; \
   done
 @end example
 
 Please open these images and inspect them with the same @command{ds9} command 
you used above.
 You will see how it is nicely flat now and doesn't have varying depths.
-Another important result of this crop is that regions with no data now have a 
NaN (Not-a-Number, or a blank value) value, not zero.
-Zero is a number, and is thus meaningful, especially when you later want to 
NoiseChisel@footnote{As you will see below, unlike most other detection 
algorithms, NoiseChisel detects the objects from their faintest parts, it 
doesn't start with their high signal-to-noise ratio peaks.
+Another important result of this crop is that regions with no data now have a 
NaN (Not-a-Number, or a blank value) value.
+In the downloaded files, such regions had a value of zero.
+However, zero is a number, and is thus meaningful, especially when you later 
want to NoiseChisel@footnote{As you will see below, unlike most other detection 
algorithms, NoiseChisel detects the objects from their faintest parts, it 
doesn't start with their high signal-to-noise ratio peaks.
 Since the Sky is already subtracted in many images and noise fluctuates around 
zero, zero is commonly higher than the initial threshold applied.
 Therefore not ignoring zero-valued pixels in this image, will cause them to 
part of the detections!}.
 Generally, when you want to ignore some pixels in a dataset, and avoid 
higher-level ambiguities or complications, it is always best to give them blank 
values (not zero, or some other absurdly large or small number).
-Gnuastro has the Arithmetic program for such cases, and we'll introduce it 
during this tutorial.
+Gnuastro has the Arithmetic program for such cases, and we'll introduce it 
later in this tutorial.
 
 @node Angular coverage on the sky, Cosmological coverage, Dataset inspection 
and cropping, General program usage tutorial
 @subsection Angular coverage on the sky
@@ -2137,18 +2214,58 @@ Gnuastro has the Arithmetic program for such cases, and 
we'll introduce it durin
 @cindex Scales, coordinate
 This is the deepest image we currently have of the sky.
 The first thing that comes to mind may be this: ``How large is this field on 
the sky?''.
-The FITS world coordinate system (WCS) meta data standard contains the key to 
answering this question: the @code{CDELT} keyword@footnote{In the FITS 
standard, the @code{CDELT} keywords (@code{CDELT1} and @code{CDELT2} in a 2D 
image) specify the scales of each coordinate.
-In the case of this image it is in units of degrees-per-pixel.
-See Section 8 of the 
@url{https://fits.gsfc.nasa.gov/standard40/fits_standard40aa-le.pdf, FITS 
standard} for more.
-In short, with the @code{CDELT} convention, rotation (@code{PC} or @code{CD} 
keywords) and scales (@code{CDELT}) are separated.
+You can get a fast and crude answer with Gnuastro's Fits program using this 
command:
+
+@example
+astfits flat-ir/xdf-f160w.fits --skycoverage
+@end example
+
+It will print the sky coverage in two formats (all numbers are in units of 
degrees for this image): 1) the image's central RA and Dec and full width 
around that center, 2) the range of RA and Dec covered by this image.
+You can use these values in various online query systems.
+You can also use this option to automatically calculate the area covered by 
this image.
+With the @option{--quiet} option, the printed output of @option{--skycoverage} 
will not contain human-readable text, making it easier for further processing:
+
+@example
+astfits flat-ir/xdf-f160w.fits --skycoverage --quiet
+@end example
+
+The second row is the coverage range along RA and Dec (compare with the 
outputs before using @option{--quiet}).
+We can thus simply subtract the second from the first column and multiply it 
with the difference of the fourth and third columns to calculate the image area.
+We'll also multiply each by 60 to have the area in arc-minutes squared.
+
+@example
+astfits flat-ir/xdf-f160w.fits --skycoverage --quiet \
+        | awk 'NR==2@{print ($2-$1)*60*($4-$3)*60@}'
+@end example
+
+The returned value is @mymath{9.06711} arcmin@mymath{^2}.
+@strong{However, this method ignores the fact many of the image pixels are 
blank!}
+In other words, the image does cover this area, but there is no data in more 
than half of the pixels.
+So let's calculate the area coverage over-which we actually have data.
+
+The FITS world coordinate system (WCS) meta data standard contains the key to 
answering this question.
+Run the following command to see all the FITS keywords (metadata) for one of 
the images (almost identical with the other images because they were are scaled 
to the same region of Sky):
+
+@example
+astfits flat-ir/xdf-f160w.fits -h1
+@end example
+
+Look into the keywords grouped under the `@code{World Coordinate System 
(WCS)}' title.
+These keywords define how the image relates to the outside world.
+In particular, the @code{CDELT*} keywords (or @code{CDELT1} and @code{CDELT2} 
in this 2D image) contain the ``Coordinate DELTa'' (or change in coordinate 
units) with a change in one pixel.
+But what is the units of each ``world'' coordinate?
+The @code{CUNIT*} keywords (for ``Coordinate UNIT'') have the answer.
+In this case, both @code{CUNIT1} and @code{CUNIT1} have a value of @code{deg}, 
so both ``world'' coordinates are in units of degrees.
+We can thus conclude that the value of @code{CDELT*} is in units of 
degrees-per-pixel@footnote{With the FITS @code{CDELT} convention, rotation 
(@code{PC} or @code{CD} keywords) and scales (@code{CDELT}) are separated.
 In the FITS standard the @code{CDELT} keywords are optional.
 When @code{CDELT} keywords aren't present, the @code{PC} matrix is assumed to 
contain @emph{both} the coordinate rotation and scales.
 Note that not all FITS writers use the @code{CDELT} convention.
 So you might not find the @code{CDELT} keywords in the WCS meta data of some 
FITS files.
 However, all Gnuastro programs (which use the default FITS keyword writing 
format of WCSLIB) write their output WCS with the @code{CDELT} convention, even 
if the input doesn't have it.
-If your dataset doesn't use the @code{CDELT} convention, you can feed it to 
any (simple) Gnuastro program (for example Arithmetic) and the output will have 
the @code{CDELT} keyword.}.
+If your dataset doesn't use the @code{CDELT} convention, you can feed it to 
any (simple) Gnuastro program (for example Arithmetic) and the output will have 
the @code{CDELT} keyword.
+See Section 8 of the 
@url{https://fits.gsfc.nasa.gov/standard40/fits_standard40aa-le.pdf, FITS 
standard} for more}.
 
-With the commands below, we'll use @code{CDELT} (along with the image size) to 
find the answer.
+With the commands below, we'll use @code{CDELT} (along with the image size) to 
find the answer of our initial question: ``how much of the sky does this image 
cover?''.
 The lines starting with @code{##} are just comments for you to read and 
understand each command.
 Don't type them on the terminal.
 The commands are intentionally repetitive in some places to better understand 
each step and also to demonstrate the beauty of command-line features like 
history, variables, pipes and loops (which you will commonly use as you master 
the command-line).
@@ -2161,6 +2278,9 @@ Press the ``up'' key on your keyboard (possibly multiple 
times) to see your prev
 @end cartouche
 
 @example
+## If your system language uses ',' (not '.') as decimal separator.
+$ export LANG=C
+
 ## See the general statistics of non-blank pixel values.
 $ aststatistics flat-ir/xdf-f160w.fits
 
@@ -2200,12 +2320,20 @@ $ echo $n $r
 ## Use the number of pixels (first number passed to AWK) and
 ## length of each pixel's edge (second number passed to AWK)
 ## to estimate the area of the field in arc-minutes squared.
-$ echo $n $r | awk '@{print $1 * ($2^2) * 3600@}'
+$ echo $n $r | awk '@{print $1 * ($2*60)^2@}'
 @end example
 
 The output of the last command (area of this field) is 4.03817 (or 
approximately 4.04) arc-minutes squared.
 Just for comparison, this is roughly 175 times smaller than the average moon's 
angular area (with a diameter of 30arc-minutes or half a degree).
 
+Some FITS writers don't use the @code{CDELT} convention, making it hard to use 
the steps above.
+In such cases, you can extract the pixel scale with the @option{--pixelscale} 
option of Gnuastro's Fits program like the command below.
+Like the @option{--skycoverage} option above, you can also use the 
@option{--quiet} option to allow easy usage of the values in scripts.
+
+@example
+$ astfits flat-ir/xdf-f160w.fits --pixelscale
+@end example
+
 @cindex GNU AWK
 @cartouche
 @noindent
@@ -2216,6 +2344,39 @@ Just like this manual, you can also access GNU AWK's 
manual on the command-line
 Just run @code{info awk}.
 @end cartouche
 
+@cartouche
+@noindent
+@cindex Locale
+@cindex @code{LANG}
+@cindex @code{LC_ALL}
+@cindex Decimal separator
+@cindex Language of command-line
+@strong{Your locale doesn't use `.' as decimal separator:} the input/output of 
some core operating system tools like @command{awk} or @command{seq} depend on 
the @url{https://en.wikipedia.org/wiki/Locale_(computer_software), system 
locale}.
+For example in Spanish and some other languages the decimal separator (symbol 
used to separate the integer and fractional part of a number), is a comma.
+Therefore in systems that have Spanish as their default Locale, @command{seq} 
will print half of unity as `@code{0,5}' (instead of `@code{0.5}').
+This can cause problems for parsing the printed numbers in other programs.
+You can check your current locale with the @code{locale} command.
+You can test your default decimal separator with this command:
+
+@example
+seq 0.5 1
+@end example
+
+To avoid these kinds of locale-specific problems (for example another program 
not being able to read `@code{0,5}' as half of unity), you can change the 
locale by setting the @code{LANG} environment variable (or the 
lower-level/generic @code{LC_ALL}).
+You can do it only for a single command (the first one below), or all commands 
within the running session (the second command below):
+
+@example
+## Change the locale to the standard, only for this 'seq' command.
+$ LANG=C seq 0.5 1
+
+## Change the locale to the standard, for all commands after it.
+$ export LANG=C
+@end example
+
+If you want to change it generally for all future sessions, you can put the 
second command in your shell's startup file.
+For more on startup files, please see @ref{Installation directory}.
+@end cartouche
+
 
 @node Cosmological coverage, Building custom programs with the library, 
Angular coverage on the sky, General program usage tutorial
 @subsection Cosmological coverage
@@ -2225,6 +2386,9 @@ In particular, you need the tangential distance covered 
by 1 arc-second as raw o
 Combined with the field's area that was measured before, we can calculate the 
tangential distance in Mega Parsecs squared (@mymath{Mpc^2}).
 
 @example
+## If your system language uses ',' (not '.') as decimal separator.
+$ export LANG=C
+
 ## Print general cosmological properties at redshift 2 (for example).
 $ astcosmiccal -z2
 
@@ -2274,9 +2438,13 @@ You can use it instead of typing all the different 
redshifts in this example.
 For example the loop below will calculate and print the tangential coverage of 
this field across a larger range of redshifts (0.1 to 5) and with finer 
increments of 0.1.
 
 @example
+## If your system language uses ',' (not '.') as decimal separator.
+$ export LANG=C
+
+## The loop over the redshifts
 $ for z in $(seq 0.1 0.1 5); do                                  \
     k=$(astcosmiccal -z$z --arcsectandist);                      \
-    echo $z $k $area | awk '@{print $1, ($2*60)^2 * $3 / 1e6@}';   \
+    echo $z $k $a | awk '@{print $1, ($2*60)^2 * $3 / 1e6@}';   \
   done
 @end example
 
@@ -2520,7 +2688,7 @@ $ rm -rf my-cosmology*
 @end example
 
 
-@node Warping to a new pixel grid, Multiextension FITS files NoiseChisel's 
output, Option management and configuration files, General program usage 
tutorial
+@node Warping to a new pixel grid, NoiseChisel and Multiextension FITS files, 
Option management and configuration files, General program usage tutorial
 @subsection Warping to a new pixel grid
 We are now ready to start processing the downloaded images.
 The XDF datasets we are using here are already aligned to the same pixel grid.
@@ -2579,8 +2747,8 @@ $ rm *.fits
 @end example
 
 
-@node Multiextension FITS files NoiseChisel's output, NoiseChisel optimization 
for detection, Warping to a new pixel grid, General program usage tutorial
-@subsection Multiextension FITS files (NoiseChisel's output)
+@node NoiseChisel and Multiextension FITS files, NoiseChisel optimization for 
detection, Warping to a new pixel grid, General program usage tutorial
+@subsection NoiseChisel and Multiextension FITS files
 Having completed a review of the basics in the previous sections, we are now 
ready to separate the signal (galaxies or stars) from the background noise in 
the image.
 We will be using the results of @ref{Dataset inspection and cropping}, so be 
sure you already have them.
 Gnuastro has NoiseChisel for this job.
@@ -2661,13 +2829,13 @@ $ astfits xdf-f160w_detected.fits --copy=DETECTIONS 
-odetections.fits
 @end example
 
 There are similar options to conveniently cut (@option{--cut}, copy, then 
remove from the input) or delete (@option{--remove}) HDUs from a FITS file also.
-See @ref{HDU manipulation} for more.
+See @ref{HDU information and manipulation} for more.
 
 
 
-@node NoiseChisel optimization for detection, NoiseChisel optimization for 
storage, Multiextension FITS files NoiseChisel's output, General program usage 
tutorial
+@node NoiseChisel optimization for detection, NoiseChisel optimization for 
storage, NoiseChisel and Multiextension FITS files, General program usage 
tutorial
 @subsection NoiseChisel optimization for detection
-In @ref{Multiextension FITS files NoiseChisel's output}, we ran NoiseChisel 
and reviewed NoiseChisel's output format.
+In @ref{NoiseChisel and Multiextension FITS files}, we ran NoiseChisel and 
reviewed NoiseChisel's output format.
 Now that you have a better feeling for multi-extension FITS files, let's 
optimize NoiseChisel for this particular dataset.
 
 One good way to see if you have missed any signal (small galaxies, or the 
wings of brighter galaxies) is to mask all the detected pixels and inspect the 
noise pixels.
@@ -2692,20 +2860,33 @@ To invert the result (only keep the detected pixels), 
you can flip the detection
 $ astarithmetic $in $det not nan where --output=mask-sky.fits
 @end example
 
-Looking again at the detected pixels, we see that there are thin connections 
between many of the smaller objects or extending from larger objects.
-This shows that we have dug in too deep, and that we are following correlated 
noise.
+@cindex Correlated noise
+@cindex Noise, correlated
+Look again at the @code{DETECTIONS} extension, in particular the long 
worm-like structure around @footnote{To find a particular coordiante easily in 
DS9, you can do this: Click on the ``Edit'' menu, and select ``Region''.
+Then click on any random part of the image to see a circle show up in that 
location (this is the ``region'').
+Double-click on the region and a ``Circle'' window will open.
+If you have celestial coordinates, keep the default ``fk5'' in the scroll-down 
menu after the ``Center''.
+But if you have pixel/image coordinates, click on the ``fk5'' and select 
``Image''.
+Now you can set the ``Center'' coordinates of the region (@code{1650} and 
@code{1470} in this case) by manually typing them in the two boxes in front of 
``Center''.
+Finally, when everything is ready, click on the ``Apply'' button and your 
region will go over your requested coordinates.
+You can zoom out (to see the whole image) and visually find it.} pixel 1650 
(X) and 1470 (Y).
+These types of long wiggly structures show that we have dug too deep into the 
noise, and are a signature of correlated noise.
+Correlated noise is created when we warp (for example rotate) individual 
exposures (that are each slightly offset compared to each other) into the same 
pixel grid before adding them into one deeper image.
+During the warping, nearby pixels are mixed and the effect of this mixing on 
the noise (which is in every pixel) is called ``correlated noise''.
+Correlated noise is a form of convolution and it slightly smooths the image.
 
-Correlated noise is created when we warp datasets from individual exposures 
(that are each slightly offset compared to each other) into the same pixel 
grid, then add them to form the final result.
-Because it mixes nearby pixel values, correlated noise is a form of 
convolution and it smooths the image.
 In terms of the number of exposures (and thus correlated noise), the XDF 
dataset is by no means an ordinary dataset.
+Therefore the default parameters need to be slightly customized.
 It is the result of warping and adding roughly 80 separate exposures which can 
create strong correlated noise/smoothing.
 In common surveys the number of exposures is usually 10 or less.
+See Figure 2 of @url{https://arxiv.org/abs/1909.11230, Akhlaghi [2019]} and 
the discussion on @option{--detgrowquant} there for more on how NoiseChisel 
``grow''s the detected objects and the patterns caused by correlated noise.
 
 Let's tweak NoiseChisel's configuration a little to get a better result on 
this dataset.
 Don't forget that ``@emph{Good statistical analysis is not a purely routine 
matter, and generally calls for more than one pass through the computer}'' 
(Anscombe 1973, see @ref{Science and its tools}).
 A good scientist must have a good understanding of her tools to make a 
meaningful analysis.
-So don't hesitate in playing with the default configuration and reviewing the 
manual when you have a new dataset in front of you.
+So don't hesitate in playing with the default configuration and reviewing the 
manual when you have a new dataset (from a new instrument) in front of you.
 Robust data analysis is an art, therefore a good scientist must first be a 
good artist.
+Once you have found the good configuration for that particular noise pattern 
(instrument) you can safely use it for all new data that have a similar noise 
pattern.
 
 NoiseChisel can produce ``Check images'' to help you visualize and inspect how 
each step is done.
 You can see all the check images it can produce with this command.
@@ -2715,8 +2896,7 @@ $ astnoisechisel --help | grep check
 @end example
 
 Let's check the overall detection process to get a better feeling of what 
NoiseChisel is doing with the following command.
-To learn the details of NoiseChisel in more detail, please see 
@url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]}.
-Also see @ref{NoiseChisel changes after publication}.
+To learn the details of NoiseChisel in more detail, please see 
@ref{NoiseChisel}, @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa 
[2015]} and @url{https://arxiv.org/abs/1909.11230, Akhlaghi [2019]}.
 
 @example
 $ astnoisechisel flat-ir/xdf-f160w.fits --checkdetection
@@ -2736,11 +2916,13 @@ $ ds9 -mecube xdf-f160w_detcheck.fits -zscale -zoom to 
fit
 In order to understand the parameters and their biases (especially as you are 
starting to use Gnuastro, or running it a new dataset), it is @emph{strongly} 
encouraged to play with the different parameters and use the respective check 
images to see which step is affected by your changes and how, for example see 
@ref{Detecting large extended targets}.
 
 @cindex FWHM
-The @code{OPENED_AND_LABELED} extension shows the initial detection step of 
NoiseChisel.
-We see these thin connections between smaller points are already present here 
(a relatively early stage in the processing).
-Such connections at the lowest surface brightness limits usually occur when 
the dataset is too smoothed.
-Because of correlated noise, the dataset is already artificially smoothed, 
therefore further smoothing it with the default kernel may be the problem.
-One solution is thus to use a sharper kernel (NoiseChisel's first step in its 
processing).
+Let's focus on one step: the @code{OPENED_AND_LABELED} extension shows the 
initial detection step of NoiseChisel.
+We see the seeds of that correlated noise structure with many small detections 
(a relatively early stage in the processing).
+Such connections at the lowest surface brightness limits usually occur when 
the dataset is too smoothed, the threshold is too low, or the final ``growth'' 
is too much.
+
+As you see from the 2nd (@code{CONVOLVED}) extension, the first operation that 
NoiseChisel does on the data is to slightly smooth it.
+However, the natural correlated noise of this dataset is already one level of 
artificial smoothing, so further smoothing it with the default kernel may be 
the culprit.
+To see the effect, let's use a sharper kernel as a first step to 
convolve/smooth the input.
 
 By default NoiseChisel uses a Gaussian with full-width-half-maximum (FWHM) of 
2 pixels.
 We can use Gnuastro's MakeProfiles to build a kernel with FWHM of 1.5 pixel 
(truncated at 5 times the FWHM, like the default) using the following command.
@@ -2752,18 +2934,22 @@ $ astmkprof --kernel=gaussian,1.5,5 --oversample=1
 
 @noindent
 Please open the output @file{kernel.fits} and have a look (it is very small 
and sharp).
-We can now tell NoiseChisel to use this instead of the default kernel with the 
following command (we'll keep checking the detection steps)
+We can now tell NoiseChisel to use this instead of the default kernel with the 
following command (we'll keep the @option{--checkdetection} to continue 
checking the detection steps)
 
 @example
 $ astnoisechisel flat-ir/xdf-f160w.fits --kernel=kernel.fits  \
                  --checkdetection
 @end example
 
-Looking at the @code{OPENED_AND_LABELED} extension, we see that the thin 
connections between smaller peaks has now significantly decreased.
+Open the output @file{xdf-f160w_detcheck.fits} as a multi-extension FITS file 
and go to the last extension (@code{DETECTIONS-FINAL}, it is the same pixels as 
the final NoiseChisel output without @option{--checkdetections}).
+Look again at that position mentioned above (1650,1470), you see that the long 
wiggly structure is gone.
+This shows we are making progress :-).
+
+Looking at the new @code{OPENED_AND_LABELED} extension, we see that the thin 
connections between smaller peaks has now significantly decreased.
 Going two extensions/steps ahead (in the first @code{HOLES-FILLED}), you can 
see that during the process of finding false pseudo-detections, too many holes 
have been filled: do you see how the many of the brighter galaxies are 
connected? At this stage all holes are filled, irrespective of their size.
 
 Try looking two extensions ahead (in the first @code{PSEUDOS-FOR-SN}), you can 
see that there aren't too many pseudo-detections because of all those extended 
filled holes.
-If you look closely, you can see the number of pseudo-detections in the result 
NoiseChisel prints (around 5000).
+If you look closely, you can see the number of pseudo-detections in the 
printed outputs of NoiseChisel (around 6400).
 This is another side-effect of correlated noise.
 To address it, we should slightly increase the pseudo-detection threshold 
(before changing @option{--dthresh}, run with @option{-P} to see the default 
value):
 
@@ -2772,15 +2958,19 @@ $ astnoisechisel flat-ir/xdf-f160w.fits 
--kernel=kernel.fits \
                  --dthresh=0.1 --checkdetection
 @end example
 
-Before visually inspecting the check image, you can already see the effect of 
this change in NoiseChisel's command-line output: notice how the number of 
pseudos has increased to more than 6000.
-Open the check image now and have a look, you can see how the 
pseudo-detections are distributed much more evenly in the image.
+Before visually inspecting the check image, you can already see the effect of 
this small change in NoiseChisel's command-line output: notice how the number 
of pseudo-detections has increased to more than 7100!
+Open the check image now and have a look, you can see how the 
pseudo-detections are distributed much more evenly in the blank sky regions of 
the @code{PSEUDOS-FOR-SN} extension.
 
 @cartouche
 @noindent
-@strong{Maximize the number of pseudo-detections:} For a new noise-pattern 
(different instrument), play with @code{--dthresh} until you get a maximal 
number of pseudo-detections (the total number of pseudo-detections is printed 
on the command-line when you run NoiseChisel).
+@strong{Maximize the number of pseudo-detections:} When using NoiseChisel on 
datasets with a new noise-pattern (for example going to a Radio astronomy 
image, or a shallow ground-based image), play with @code{--dthresh} until you 
get a maximal number of pseudo-detections: the total number of 
pseudo-detections is printed on the command-line when you run NoiseChisel, you 
don't even need to open a FITS viewer.
+
+In this particular case, try @option{--dthresh=0.2} and you will see that the 
total printed number decreases to around 6700 (recall that with 
@option{--dthresh=0.1}, it was roughly 7100).
+So for this type of very deep HST images, we should set @option{--dthresh=0.1}.
 @end cartouche
 
-The signal-to-noise ratio of pseudo-detections define NoiseChisel's reference 
for removing false detections, so they are very important to get right.
+As discussed in Section 3.1.5 of @url{https://arxiv.org/abs/1505.01664, 
Akhlaghi and Ichikawa [2015]}, the signal-to-noise ratio of pseudo-detections 
are critical to identifying/removing false detections.
+For an optimal detection they are very important to get right (where you want 
to detect the faintest and smallest objects in the image successfully).
 Let's have a look at their signal-to-noise distribution with 
@option{--checksn}.
 
 @example
@@ -2788,16 +2978,39 @@ $ astnoisechisel flat-ir/xdf-f160w.fits 
--kernel=kernel.fits  \
                  --dthresh=0.1 --checkdetection --checksn
 @end example
 
-The output (@file{xdf-f160w_detsn.fits}) contains two extensions for the 
pseudo-detections over the undetected (sky) regions and those over detections.
-The first column is the pseudo-detection label which you can see in the 
respective@footnote{The first @code{PSEUDOS-FOR-SN} in 
@file{xdf-f160w_detsn.fits} is for the pseudo-detections over the undetected 
regions and the second is for those over detected regions.} 
@code{PSEUDOS-FOR-SN} extension of @file{xdf-f160w_detcheck.fits}.
-You can see the table columns with the first command below and get a feeling 
for its distribution with the second command (the two Table and Statistics 
programs will be discussed later in the tutorial)
+The output (@file{xdf-f160w_detsn.fits}) contains two extensions for the 
pseudo-detections containing two-column tables over the undetected 
(@code{SKY_PSEUDODET_SN}) regions and those over detections 
(@code{DET_PSEUDODET_SN}).
+With the first command below you can see the HDUs of this file, and with the 
second you can see the information of the table in the first HDU (which is the 
default when you don't use @option{--hdu}):
 
 @example
-$ asttable xdf-f160w_detsn.fits -hSKY_PSEUDODET_SN
-$ aststatistics xdf-f160w_detsn.fits -hSKY_PSEUDODET_SN -c2
+$ astfits xdf-f160w_detsn.fits
+$ asttable xdf-f160w_detsn.fits -i
 @end example
 
-The correlated noise is again visible in this pseudo-detection signal-to-noise 
distribution: it is highly skewed.
+@noindent
+You can see the table columns with the first command below and get a feeling 
of the signal-to-noise value distribution with the second command (the two 
Table and Statistics programs will be discussed later in the tutorial):
+
+@example
+$ asttable xdf-f160w_detsn.fits -hSKY_PSEUDODET_SN
+$ aststatistics xdf-f160w_detsn.fits -hSKY_PSEUDODET_SN -c2
+... [output truncated] ...
+Histogram:
+ |           *
+ |          ***
+ |         ******
+ |        *********
+ |        **********
+ |       *************
+ |      *****************
+ |     ********************
+ |    **************************
+ |   ********************************
+ |*******************************************************   * **       *
+ |----------------------------------------------------------------------
+@end example
+
+The correlated noise is again visible in the signal-to-noise distribution of 
sky pseudo-detections!
+Do you see how skewed this distribution is?
+In an image with less correlated noise, this distribution would be much more 
symmetric.
 A small change in the quantile will translate into a big change in the S/N 
value.
 For example see the difference between the three 0.99, 0.95 and 0.90 quantiles 
with this command:
 
@@ -2806,6 +3019,7 @@ $ aststatistics xdf-f160w_detsn.fits -hSKY_PSEUDODET_SN 
-c2      \
                 --quantile=0.99 --quantile=0.95 --quantile=0.90
 @end example
 
+We get a change of almost 2 units (which is very significant).
 If you run NoiseChisel with @option{-P}, you'll see the default 
signal-to-noise quantile @option{--snquant} is 0.99.
 In effect with this option you specify the purity level you want 
(contamination by false detections).
 With the @command{aststatistics} command above, you see that a small number of 
extra false detections (impurity) in the final result causes a big change in 
completeness (you can detect more lower signal-to-noise true detections).
@@ -2820,22 +3034,27 @@ $ astarithmetic $in $det nan where 
--output=mask-det.fits
 @end example
 
 Overall it seems good, but if you play a little with the color-bar and look 
closer in the noise, you'll see a few very sharp, but faint, objects that have 
not been detected.
-This only happens for under-sampled datasets like HST (where the pixel size is 
larger than the point spread function FWHM).
+For example the object around pixel (456, 1662).
+Despite its high valued pixels, this object was lost because erosion ignores 
the precise pixel values.
+Loosing small/sharp objects like this only happens for under-sampled datasets 
like HST (where the pixel size is larger than the point spread function FWHM).
 So this won't happen on ground-based images.
-Because of this, sharp and faint objects will be very small and eroded too 
easily during NoiseChisel's erosion step.
 
 To address this problem of sharp objects, we can use NoiseChisel's 
@option{--noerodequant} option.
-All pixels above this quantile will not be eroded, thus allowing us to 
preserve faint and sharp objects.
+All pixels above this quantile will not be eroded, thus allowing us to 
preserve small/sharp objects (that cover a small area, but have a lot of signal 
in it).
 Check its default value, then run NoiseChisel like below and make the mask 
again.
-You will see many of those sharp objects are now detected.
 
 @example
 $ astnoisechisel flat-ir/xdf-f160w.fits --kernel=kernel.fits     \
                  --noerodequant=0.95 --dthresh=0.1 --snquant=0.95
 @end example
 
-This seems to be fine and we can continue with our analysis.
-To avoid having to write these options on every call to NoiseChisel, we'll 
just make a configuration file in a visible @file{config} directory.
+This seems to be fine and the object above is now detected.
+We'll stop the configuration here, but please feel free to keep looking into 
the data to see if you can improve it even more.
+
+Once you have found the proper customization for the type of images you will 
be using you don't need to change them any more.
+The same configuration can be used for any dataset that has been similarly 
produced (and has a similar noise pattern).
+But entering all these options on every call to NoiseChisel is annoying and 
prone to bugs (mistakenly typing the wrong value for example).
+To simply things, we'll make a configuration file in a visible @file{config} 
directory.
 Then we'll define the hidden @file{.gnuastro} directory (that all Gnuastro's 
programs will look into for configuration files) as a symbolic link to the 
@file{config} directory.
 Finally, we'll write the finalized values of the options into NoiseChisel's 
standard configuration file within that directory.
 We'll also put the kernel in a separate directory to keep the top directory 
clean of any files we later need.
@@ -2851,50 +3070,72 @@ $ echo "snquant      0.95"             >> 
config/astnoisechisel.conf
 @end example
 
 @noindent
-We are now ready to finally run NoiseChisel on the two filters and keep the
-output in a dedicated directory (@file{nc}).
+We are now ready to finally run NoiseChisel on the three filters and keep the 
output in a dedicated directory (which we'll call @file{nc} for simplicity).
 @example
 $ rm *.fits
 $ mkdir nc
-$ astnoisechisel flat-ir/xdf-f160w.fits --output=nc/xdf-f160w.fits
-$ astnoisechisel flat-ir/xdf-f105w.fits --output=nc/xdf-f105w.fits
+$ for f in f105w f125w f160w; do \
+    astnoisechisel flat-ir/xdf-$f.fits --output=nc/xdf-$f.fits
+  done
 @end example
 
 
 @node NoiseChisel optimization for storage, Segmentation and making a catalog, 
NoiseChisel optimization for detection, General program usage tutorial
 @subsection NoiseChisel optimization for storage
 
-As we showed before (in @ref{Multiextension FITS files NoiseChisel's output}), 
NoiseChisel's output is a multi-extension FITS file with several images the 
same size as the input.
+As we showed before (in @ref{NoiseChisel and Multiextension FITS files}), 
NoiseChisel's output is a multi-extension FITS file with several images the 
same size as the input.
 As the input datasets get larger this output can become hard to manage and 
waste a lot of storage space.
 Fortunately there is a solution to this problem (which is also useful for 
Segment's outputs).
-But first, let's have a look at the volume of NoiseChisel's output from 
@ref{NoiseChisel optimization for detection} (fast answer, its larger than 100 
mega-bytes):
+
+In this small section we'll take a short detour to show this feature.
+Please note that the outputs generated here are not needed for the rest of the 
tutorial.
+But first, let's have a look at the contents/HDUs and volume of NoiseChisel's 
output from @ref{NoiseChisel optimization for detection} (fast answer, its 
larger than 100 mega-bytes):
 
 @example
+$ astfits nc/xdf-f160w.fits
 $ ls -lh nc/xdf-f160w.fits
 @end example
 
 Two options can drastically decrease NoiseChisel's output file size: 1) With 
the @option{--rawoutput} option, NoiseChisel won't create a Sky-subtracted 
input.
-After all, it is redundant: you can always generate it by subtracting the Sky 
from the input image (which you have in your database) using the Arithmetic 
program.
+After all, it is redundant: you can always generate it by subtracting the 
@code{SKY} extension from the input image (which you have in your database) 
using the Arithmetic program.
 2) With the @option{--oneelempertile}, you can tell NoiseChisel to store its 
Sky and Sky standard deviation results with one pixel per tile (instead of many 
pixels per tile).
+So let's run NoiseChisel with these options, then have another look at the 
HDUs and the over-all file size:
 
 @example
-$ astnoisechisel flat-ir/xdf-f160w.fits --oneelempertile --rawoutput
+$ astnoisechisel flat-ir/xdf-f160w.fits --oneelempertile --rawoutput \
+                 --output=nc-for-storage.fits
+$ astfits nc-for-storage.fits
+$ ls -lh nc-for-storage.fits
 @end example
 
 @noindent
-The output is now just under 8 mega byes! But you can even be more efficient 
in space by compressing it.
+See how @file{nc-for-storage.fits} has four HDUs, while 
@file{nc/xdf-f160w.fits} had five HDUs?
+As explained above, the missing extension is @code{INPUT-NO-SKY}.
+Also, look at the sizes of the @code{SKY} and @code{SKY_STD} HDUs, unlike 
before, they aren't the same size as @code{DETECTIONS}, they only have one 
pixel for each tile (group of pixels in raw input).
+Finally, you see that @file{nc-for-storage.fits} is just under 8 mega byes 
(while @file{nc/xdf-f160w.fits} was 100 mega bytes)!
+
+But were are not finished!
+You can even be more efficient in storage, archival or transferring 
NoiseChisel's output by compressing this file.
 Try the command below to see how NoiseChisel's output has now shrunk to about 
250 kilo-byes while keeping all the necessary information as the original 100 
mega-byte output.
 
 @example
-$ gzip --best xdf-f160w_detected.fits
-$ ls -lh xdf-f160w_detected.fits.gz
+$ gzip --best nc-for-storage.fits
+$ ls -lh nc-for-storage.fits.gz
 @end example
 
 We can get this wonderful level of compression because NoiseChisel's output is 
binary with only two values: 0 and 1.
 Compression algorithms are highly optimized in such scenarios.
 
-You can open @file{xdf-f160w_detected.fits.gz} directly in SAO DS9 or feed it 
to any of Gnuastro's programs without having to uncompress it.
-Higher-level programs that take NoiseChisel's output can also deal with this 
compressed image where the Sky and its Standard deviation are one 
pixel-per-tile.
+You can open @file{nc-for-storage.fits.gz} directly in SAO DS9 or feed it to 
any of Gnuastro's programs without having to decompress it.
+Higher-level programs that take NoiseChisel's output (for example Segment or 
MakeCatalog) can also deal with this compressed image where the Sky and its 
Standard deviation are one pixel-per-tile.
+You just have to give the ``values'' image as a separate option, for more, see 
@ref{Segment} and @ref{MakeCatalog}.
+
+Segment (the program we will introduce in the next section for identifying 
sub-structure), also has similar features to optimize its output for storage.
+Since this file was only created for a fast detour demonstration, let's keep 
our top directory clean and move to the next step:
+
+@example
+rm nc-for-storage.fits.gz
+@end example
 
 
 
@@ -2902,12 +3143,14 @@ Higher-level programs that take NoiseChisel's output 
can also deal with this com
 @subsection Segmentation and making a catalog
 The main output of NoiseChisel is the binary detection map (@code{DETECTIONS} 
extension, see @ref{NoiseChisel optimization for detection}).
 which only has two values of 1 or 0.
-This is useful when studying the noise, but hardly of any use when you 
actually want to study the targets/galaxies in the image, especially in such a 
deep field where the detection map of almost everything is connected.
+This is useful when studying the noise or background properties, but hardly of 
any use when you actually want to study the targets/galaxies in the image, 
especially in such a deep field where almost everything is connected.
 To find the galaxies over the detections, we'll use Gnuastro's @ref{Segment} 
program:
 
 @example
 $ mkdir seg
 $ astsegment nc/xdf-f160w.fits -oseg/xdf-f160w.fits
+$ astsegment nc/xdf-f125w.fits -oseg/xdf-f125w.fits
+$ astsegment nc/xdf-f105w.fits -oseg/xdf-f105w.fits
 @end example
 
 Segment's operation is very much like NoiseChisel (in fact, prior to version 
0.6, it was part of NoiseChisel).
@@ -2920,49 +3163,84 @@ $ ds9 -mecube seg/xdf-f160w.fits -zscale -zoom to fit
 
 Like NoiseChisel, the first extension is the input.
 The @code{CLUMPS} extension shows the true ``clumps'' with values that are 
@mymath{\ge1}, and the diffuse regions labeled as @mymath{-1}.
+Please flip between the first extension and the clumps extension and zoom-in 
on some of the clumps to get a feeling of what they are.
 In the @code{OBJECTS} extension, we see that the large detections of 
NoiseChisel (that may have contained many galaxies) are now broken up into 
separate labels.
-See @ref{Segment} for more.
+Play with the color-bar and hover your mouse of the various detections to see 
their different labels.
+
+The clumps are not affected by the hard-to-deblend and low signal-to-noise 
diffuse regions, they are more robust for calculating the colors (compared to 
objects).
+From this step onward, we'll continue with clumps.
 
 Having localized the regions of interest in the dataset, we are ready to do 
measurements on them with @ref{MakeCatalog}.
 Besides the IDs, we want to measure (in this order) the Right Ascension (with 
@option{--ra}), Declination (@option{--dec}), magnitude (@option{--magnitude}), 
and signal-to-noise ratio (@option{--sn}) of the objects and clumps.
-The following command will make these measurements on Segment's F160W output:
+Furthermore, as mentioned above, we also want measurements on clumps, so we 
also need to call @option{--clumpscat}.
+The following command will make these measurements on Segment's F160W output 
and write them in a catalog for each object and clump in a FITS table.
 
-@c Keep the `--zeropoint' on a single line, because later, we'll add
-@c `--valuesfile' in that line also, and it would be more clear if both
-@c catalogs follow the same format.
 @example
 $ mkdir cat
 $ astmkcatalog seg/xdf-f160w.fits --ids --ra --dec --magnitude --sn \
-               --zeropoint=25.94                                    \
-               --clumpscat --output=cat/xdf-f160w.fits
+               --zeropoint=25.94 --clumpscat --output=cat/xdf-f160w.fits
 @end example
 
 @noindent
 From the printed statements on the command-line, you see that MakeCatalog read 
all the extensions in Segment's output for the various measurements it needed.
+To calculate colors, we also need magnitude measurements on the other filters.
+So let's repeat the command above on them, just changing the file names and 
zeropoint (which we got from the XDF survey web page):
+
+@example
+$ astmkcatalog seg/xdf-f125w.fits --ids --ra --dec --magnitude --sn \
+               --zeropoint=26.23 --clumpscat --output=cat/xdf-f125w.fits
+
+$ astmkcatalog seg/xdf-f105w.fits --ids --ra --dec --magnitude --sn \
+               --zeropoint=26.27 --clumpscat --output=cat/xdf-f105w.fits
+@end example
 
-To calculate colors, we also need magnitude measurements on the F105W filter.
-However, the galaxy properties might differ between the filters (which is the 
whole purpose behind measuring colors).
+However, the galaxy properties might differ between the filters (which is the 
whole purpose behind observing in different filters!).
 Also, the noise properties and depth of the datasets differ.
-Therefore, if we simply follow the same Segment and MakeCatalog calls above 
for the F105W filter, we are going to get a different number of objects and 
clumps.
-Matching the two catalogs is possible (for example with @ref{Match}), but the 
fact that the measurements will be done on different pixels, can bias the 
result.
-Since the Point spread function (PSF) of both images is very similar, an 
accurate color calculation can only be done when magnitudes are measured from 
the same pixels on both images.
+You can see the effect of these factors in the resulting clump catalogs, with 
Gnuastro's Table program.
+We'll go deep into working with tables in the next section, but in summary: 
the @option{-i} option will print information about the columns and number of 
rows.
+To see the column values, just remove the @option{-i} option.
+In the output of each command below, look at the @code{Number of rows:}, and 
note that they are different.
+
+@example
+$ asttable cat/xdf-f105w.fits -hCLUMPS -i
+$ asttable cat/xdf-f125w.fits -hCLUMPS -i
+$ asttable cat/xdf-f160w.fits -hCLUMPS -i
+@end example
+
+Matching the catalogs is possible (for example with @ref{Match}).
+However, the measurements of each column are also done on different pixels: 
the clump labels can/will differ from one filter to another for one object.
+Please open them and focus on one object to see for your self.
+This can bias the result, if you match catalogs.
+
+An accurate color calculation can only be done when magnitudes are measured 
from the same pixels on both images.
+Fortunately in these images, the Point spread function (PSF) are very similar, 
allowing us to do this directly@footnote{When the PSFs between two images 
differ largely, you would have to PSF-match the images before using the same 
pixels for measurements.}.
+You can do this with MakeCatalog and is one of the reasons that NoiseChisel or 
Segment don't generate a catalog at all (to give you the freedom of selecting 
the pixels to do catalog measurements on).
 
 The F160W image is deeper, thus providing better detection/segmentation, and 
redder, thus observing smaller/older stars and representing more of the mass in 
the galaxies.
-To generate the F105W catalog, we will thus use the pixel labels generated on 
the F160W filter, but do the measurements on the F105W filter (using 
MakeCatalog's @option{--valuesfile} option).
-Notice how the only difference between this call to MakeCatalog and the 
previous one is @option{--valuesfile}, the value given to @code{--zeropoint} 
and the output name.
+We will thus use the F160W filter as a reference and use its segment labels to 
identify which pixels to use for which objects/clumps.
+But we will do the measurements on the sky-subtracted F105W and F125W images 
(using MakeCatalog's @option{--valuesfile} option) as shown below:
+Notice that the only difference between these calls and the call to generate 
the raw F160W catalog (excluding the zero point and the output name) is the 
@option{--valuesfile}.
 
 @example
 $ astmkcatalog seg/xdf-f160w.fits --ids --ra --dec --magnitude --sn \
-               --valuesfile=nc/xdf-f105w.fits --zeropoint=26.27     \
-               --clumpscat --output=cat/xdf-f105w.fits
+               --valuesfile=nc/xdf-f125w.fits --zeropoint=26.23 \
+               --clumpscat --output=cat/xdf-f125w-on-f160w-lab.fits
+
+$ astmkcatalog seg/xdf-f160w.fits --ids --ra --dec --magnitude --sn \
+               --valuesfile=nc/xdf-f105w.fits --zeropoint=26.27 \
+               --clumpscat --output=cat/xdf-f105w-on-f160w-lab.fits
 @end example
 
-Look into what MakeCatalog printed on the command-line.
-You can see that (as requested) the object and clump labels were taken from 
the respective extensions in @file{seg/xdf-f160w.fits}, while the values and 
Sky standard deviation were done on @file{nc/xdf-f105w.fits}.
+After running the commands above, look into what MakeCatalog printed on the 
command-line.
+You can see that (as requested) the object and clump labels for both were 
taken from the respective extensions in @file{seg/xdf-f160w.fits}, while the 
values and Sky standard deviation were taken from @file{nc/xdf-f105w.fits} and 
@file{nc/xdf-f125w.fits}.
+Since we used the same labeled image on both filters, the number of rows in 
both catalogs are now identical.
+Let's have a look:
 
-Since we used the same labeled image on both filters, the number of rows in 
both catalogs are the same.
-The clumps are not affected by the hard-to-deblend and low signal-to-noise 
diffuse regions, they are more robust for calculating the colors (compared to 
objects).
-Therefore from this step onward, we'll continue with clumps.
+@example
+$ asttable cat/xdf-f105w-on-f160w-lab.fits -hCLUMPS -i
+$ asttable cat/xdf-f125w-on-f160w-lab.fits -hCLUMPS -i
+$ asttable cat/xdf-f160w.fits -hCLUMPS -i
+@end example
 
 Finally, the comments in MakeCatalog's output (@code{COMMENT} keywords in the 
FITS headers, or lines starting with @code{#} in plain text) contain some 
important information about the input datasets and other useful info (for 
example pixel area or per-pixel surface brightness limit).
 You can see them with this command:
@@ -2972,10 +3250,10 @@ $ astfits cat/xdf-f160w.fits -h1 | grep COMMENT
 @end example
 
 
-@node Working with catalogs estimating colors, Aperture photometry, 
Segmentation and making a catalog, General program usage tutorial
+@node Working with catalogs estimating colors, Column statistics 
color-magnitude diagram, Segmentation and making a catalog, General program 
usage tutorial
 @subsection Working with catalogs (estimating colors)
-The output of the MakeCatalog command above is a FITS table (see 
@ref{Segmentation and making a catalog}).
-The two clump and object catalogs are available in the two extensions of the 
single FITS file@footnote{MakeCatalog can also output plain text tables.
+In the previous step we generated catalogs of objects and clumps over our 
dataset (see @ref{Segmentation and making a catalog}).
+The catalogs are available in the two extensions of the single FITS 
file@footnote{MakeCatalog can also output plain text tables.
 However, in the plain text format you can only have one table per file.
 Therefore, if you also request measurements on clumps, two plain text tables 
will be created (suffixed with @file{_o.txt} and @file{_c.txt}).}.
 Let's see the extensions and their basic properties with the Fits program:
@@ -2984,8 +3262,9 @@ Let's see the extensions and their basic properties with 
the Fits program:
 $ astfits  cat/xdf-f160w.fits              # Extension information
 @end example
 
-Now, let's inspect the table in each extension with Gnuastro's Table program 
(see @ref{Table}).
-Note that we could have used @option{-hOBJECTS} and @option{-hCLUMPS} instead 
of @option{-h1} and @option{-h2} respectively.
+Let's inspect the table in each extension with Gnuastro's Table program (see 
@ref{Table}).
+We should have used @option{-hOBJECTS} and @option{-hCLUMPS} instead of 
@option{-h1} and @option{-h2} respectively.
+The numbers are just used here to convey that both names or numbers are 
possible, in the next commands, we'll just use names.
 
 @example
 $ asttable cat/xdf-f160w.fits -h1 --info   # Objects catalog info.
@@ -2994,160 +3273,393 @@ $ asttable cat/xdf-f160w.fits -h2 -i       # Clumps 
catalog info.
 $ asttable cat/xdf-f160w.fits -h2          # Clumps catalog columns.
 @end example
 
+@noindent
 As you see above, when given a specific table (file name and extension), Table 
will print the full contents of all the columns.
 To see the basic metadata about each column (for example name, units and 
comments), simply append a @option{--info} (or @option{-i}) to the command.
 
-To print the contents of special column(s), just specify the column number(s) 
(counting from @code{1}) or the column name(s) (if they have one).
-For example, if you just want the magnitude and signal-to-noise ratio of the 
clumps (in @option{-h2}), you can get it with any of the following commands
+To print the contents of special column(s), just give the column number(s) 
(counting from @code{1}) or the column name(s) (if they have one) to the 
@option{--column} (or @option{-c}) option.
+For example, if you just want the magnitude and signal-to-noise ratio of the 
clumps (in the clumps catalog), you can get it with any of the following 
commands
 
 @example
-$ asttable cat/xdf-f160w.fits -h2 -c5,6
-$ asttable cat/xdf-f160w.fits -h2 -c5,SN
-$ asttable cat/xdf-f160w.fits -h2 -c5         -c6
-$ asttable cat/xdf-f160w.fits -h2 -cMAGNITUDE -cSN
+$ asttable cat/xdf-f160w.fits -hCLUMPS --column=5,6
+$ asttable cat/xdf-f160w.fits -hCLUMPS -c5,SN
+$ asttable cat/xdf-f160w.fits -hCLUMPS -c5         -c6
+$ asttable cat/xdf-f160w.fits -hCLUMPS -cMAGNITUDE -cSN
 @end example
 
+@noindent
+Similar to HDUs, when the columns have names, always use the name: it is so 
common to mis-write numbers or forget the order later!
 Using column names instead of numbers has many advantages:
-1) you don't have to worry about the order of columns in the table.
-2) It acts as a documentation in the script.
+@enumerate
+@item
+You don't have to worry about the order of columns in the table.
+@item
+It acts as a documentation in the script.
+@item
 Column meta-data (including a name) aren't just limited to FITS tables and can 
also be used in plain text tables, see @ref{Gnuastro text table format}.
+@end enumerate
 
-We can finally calculate the colors of the objects from these two datasets.
-If you inspect the contents of the two catalogs, you'll notice that because 
they were both derived from the same segmentation maps, the rows are ordered 
identically (they correspond to the same object/clump in both filters).
-But to be generic (usable even when the rows aren't ordered similarly) and 
display another useful program in Gnuastro, we'll use @ref{Match}.
+@noindent
+Table also has tools to limit the displayed rows.
+For example with the first command below only rows with a magnitude in the 
range of 29 to 30 will be shown.
+With the second command, you can further limit the displayed rows to rows with 
an S/N larger than 10 (a range between 10 to infinity).
+You can further sort the output rows, only show the top (or bottom) N rows and 
etc, for more see @ref{Table}.
 
-As the name suggests, Gnuastro's Match program will match rows based on 
distance (or aperture in 2D) in one (or two) columns.
-In the command below, the options relating to each catalog are placed under it 
for easy understanding.
-You give Match two catalogs (from the two different filters we derived above) 
as argument, and the HDUs containing them (if they are FITS files) with the 
@option{--hdu} and @option{--hdu2} options.
-The @option{--ccol1} and @option{--ccol2} options specify the 
coordinate-columns which should be matched with which in the two catalogs.
-With @option{--aperture} you specify the acceptable error (radius in 2D), in 
the same units as the columns (see below for why we have requested an aperture 
of 0.35 arcseconds, or less than 6 HST pixels).
+@example
+$ asttable cat/xdf-f160w.fits -hCLUMPS --range=MAGNITUDE,28:29
+$ asttable cat/xdf-f160w.fits -hCLUMPS \
+           --range=MAGNITUDE,28:29 --range=SN,10:inf
+@end example
 
-The @option{--outcols} of Match is a very convenient feature in Match: you can 
use it to specify which columns from the two catalogs you want in the output 
(merge two input catalogs into one).
-If the first character is an `@key{a}', the respective matched column (number 
or name, similar to Table above) in the first catalog will be written in the 
output table.
-When the first character is a `@key{b}', the respective column from the second 
catalog will be written in the output.
-Also, if the first character is followed by @code{_all}, then all the columns 
from the respective catalog will be put in the output.
+Now that you are comfortable in viewing table columns and rows, let's look 
into merging columns of multiple tables into one table (which is necessary for 
measuring the color of the clumps).
+Since @file{cat/xdf-f160w.fits} and @file{cat/xdf-f105w-on-f160w-lab.fits} 
have exactly the same number of rows and the rows correspond to the same clump, 
let's merge them to have one table with magnitudes in both filters.
+
+We can merge columns with the @option{--catcolumnfile} option like below.
+You give this option a file name (which is assumed to be a table that has the 
same number of rows as the main input), and all the table's columns will be 
concatenated/appended to the main table.
+So please try it out with the commands below.
+We'll first look at the metadata of the first table (only the @code{CLUMPS} 
extension).
+With the second command, we'll concatenate the two tables and write them in, 
@file{two-in-one.fits} and finally, we'll check the new catalog's metadata.
 
 @example
-$ astmatch cat/xdf-f160w.fits           cat/xdf-f105w.fits         \
-           --hdu=CLUMPS                 --hdu2=CLUMPS              \
-           --ccol1=RA,DEC               --ccol2=RA,DEC             \
-           --aperture=0.35/3600 --log                              \
-           --outcols=a_all,bMAGNITUDE,bSN                          \
-           --output=cat/xdf-f160w-f105w.fits
+$ asttable cat/xdf-f160w.fits -i -hCLUMPS
+$ asttable cat/xdf-f160w.fits -hCLUMPS --output=two-in-one.fits \
+           --catcolumnfile=cat/xdf-f125w-on-f160w-lab.fits \
+           --catcolumnhdu=CLUMPS
+$ asttable two-in-one.fits -i
 @end example
 
-Let's have a look at the columns in the matched catalog:
+By comparing the two metadata, we see that both tables have the same number of 
rows.
+But what might have attracted your attention more, is that 
@file{two-in-one.fits} has double the number of columns (as expected, after 
all, you merged both tables into one file, and didn't ask for any specific 
column).
+In fact you can concatenate any number of other tables in one command, for 
example:
 
 @example
-$ asttable cat/xdf-f160w-f105w.fits -i
+$ asttable cat/xdf-f160w.fits -hCLUMPS --output=three-in-one.fits \
+           --catcolumnfile=cat/xdf-f125w-on-f160w-lab.fits \
+           --catcolumnfile=cat/xdf-f105w-on-f160w-lab.fits \
+           --catcolumnhdu=CLUMPS --catcolumnhdu=CLUMPS
+$ asttable three-in-one.fits -i
 @end example
 
-Indeed, its exactly the columns we wanted: there are two @code{MAGNITUDE} and 
@code{SN} columns.
-The first is from the F160W filter, the second is from the F105W.
-Right now, you know this.
-But in one hour, you'll start doubting your self: going through your command 
history, trying to answer this question: ``which magnitude corresponds to which 
filter?''.
-You should never torture your future-self (or colleagues) like this! So, let's 
rename these confusing columns in the matched catalog.
-The FITS standard for tables stores the column names in the @code{TTYPE} 
header keywords, so let's have a look:
+As you see, to avoid confusion in column names, Table has intentionally 
appended a @code{-1} to the column names of the first concatenated table (so 
for example we have the original @code{RA} column, and another one called 
@code{RA-1}).
+Similarly a @code{-2} has been added for the columns of the second 
concatenated table.
+
+However, this example clearly shows a problem with this full concatenation: 
some columns are identical (for example @code{HOST_OBJ_ID} and 
@code{HOST_OBJ_ID-1}), or not needed (for example @code{RA-1} and @code{DEC-1} 
which are not necessary here).
+In such cases, you can use @option{--catcolumns} to only concatenate certain 
columns, not the whole table.
+For example this command:
 
 @example
-$ astfits cat/xdf-f160w-f105w.fits -h1 | grep TTYPE
+$ asttable cat/xdf-f160w.fits -hCLUMPS --output=two-in-one-2.fits \
+           --catcolumnfile=cat/xdf-f125w-on-f160w-lab.fits \
+           --catcolumnhdu=CLUMPS --catcolumns=MAGNITUDE
+$ asttable two-in-one-2.fits -i
 @end example
 
-Changing/updating the column names is as easy as updating the values to
-these options with the first command below, and with the second, confirm
-this change:
+You see that we have now only appended the @code{MAGNITUDE} column of 
@file{cat/xdf-f125w-on-f160w-lab.fits}.
+This is what we needed to be able to later subtract the magnitudes.
+Let's go ahead and add the F105W magnitudes also with the command below.
+Note how we need to call @option{--catcolumnhdu} once for every table that 
should be appended, but we only call @option{--catcolumn} once (assuming all 
the tables that should be appended have this column).
 
 @example
-$ astfits cat/xdf-f160w-f105w.fits -h1                          \
-          --update=TTYPE5,MAG_F160W   --update=TTYPE6,SN_F160W  \
-          --update=TTYPE7,MAG_F105W   --update=TTYPE8,SN_F105W
-$ asttable cat/xdf-f160w-f105w.fits -i
+$ asttable cat/xdf-f160w.fits -hCLUMPS --output=three-in-one-2.fits \
+           --catcolumnfile=cat/xdf-f125w-on-f160w-lab.fits \
+           --catcolumnfile=cat/xdf-f105w-on-f160w-lab.fits \
+           --catcolumnhdu=CLUMPS --catcolumnhdu=CLUMPS \
+           --catcolumns=MAGNITUDE
+$ asttable three-in-one-2.fits -i
 @end example
 
-If you noticed, when running Match, we also asked for a log file 
(@option{--log}).
-Many Gnuastro programs have this option to provide some detailed information 
on their operation in case you are curious or want to debug something.
-Here, we are using it to justify the value we gave to @option{--aperture}.
-Even though you asked for the output to be written in the @file{cat} 
directory, a listing of the contents of your current directory will show you an 
extra @file{astmatch.fits} file.
-Let's have a look at what columns it contains.
+But we aren't finished yet!
+There is a very big problem: its not immediately clear which one of 
@code{MAGNITUDE}, @code{MAGNITUDE-1} or @code{MAGNITUDE-2} columns belong to 
which filter!
+Right now, you know this because you just ran this command.
+But in one hour, you'll start doubting your self and will be forced to go 
through your command history, trying to figure out if you added F105W first, or 
F125W.
+You should never torture your future-self (or your colleagues) like this!
+So, let's rename these confusing columns in the matched catalog.
+
+Fortunately, with the @option{--colmetadata} option, you can correct the 
column metadata of the final table (just before it is written).
+It takes four values:
+1) the original column name or number,
+2) the new column name,
+3) the column unit and
+4) the column comments.
+Since the comments are usually human-friendly sentences and contain space 
characters, you should put them in double quotations like below.
+For example by adding three calls of this option to the previous command, we 
write the filter name in the magnitude column name and description.
 
 @example
-$ ls
-$ asttable astmatch.fits -i
+$ asttable cat/xdf-f160w.fits -hCLUMPS --output=three-in-one-3.fits \
+        --catcolumnfile=cat/xdf-f125w-on-f160w-lab.fits \
+        --catcolumnfile=cat/xdf-f105w-on-f160w-lab.fits \
+        --catcolumnhdu=CLUMPS --catcolumnhdu=CLUMPS \
+        --catcolumns=MAGNITUDE \
+        --colmetadata=MAGNITUDE,MAG-F160w,log,"Magnitude in F160W." \
+        --colmetadata=MAGNITUDE-1,MAG-F125w,log,"Magnitude in F125W." \
+        --colmetadata=MAGNITUDE-2,MAG-F105w,log,"Magnitude in F105W."
+$ asttable three-in-one-3.fits -i
 @end example
 
-@cindex Flux-weighted
-@cindex SED, Spectral Energy Distribution
-@cindex Spectral Energy Distribution, SED
-The @file{MATCH_DIST} column contains the distance of the matched rows, let's 
have a look at the distribution of values in this column.
-You might be asking yourself ``why should the positions of the two filters 
differ when I gave MakeCatalog the same segmentation map?'' The reason is that 
the central positions are @emph{flux-weighted}.
-Therefore the @option{--valuesfile} dataset you give to MakeCatalog will also 
affect the center measurements@footnote{To only measure the center based on the 
labeled pixels (and ignore the pixel values), you can ask for the columns that 
contain @option{geo} (for geometric) in them.
-For example @option{--geow1} or @option{--geow2} for the RA and Declination 
(first and second world-coordinates).}.
-Recall that the Spectral Energy Distribution (SED) of galaxies is not flat and 
they have substructure, therefore, they can have different shapes/morphologies 
in different filters.
+We now have all three magnitudes in one table and can start doing arithmetic 
on them (to estimate colors, which are just a subtraction of magnitudes).
+To use column arithmetic, simply call the column selection option 
(@option{--column} or @option{-c}), put the value in single quotations and 
start the value with @code{arith} (followed by a space) like the example below.
+Column arithmetic uses the same ``reverse polish notation'' as the Arithmetic 
program (see @ref{Reverse polish notation}), with almost all the same operators 
(see @ref{Arithmetic operators}), and some column-specific operators (that 
aren't available for images).
+In column-arithmetic, you can identify columns by number (prefixed with a 
@code{$}) or name, for more see @ref{Column arithmetic}.
+
+So let's estimate one color from @file{three-in-one-3.fits} using column 
arithmetic.
+All the commands below will produce the same output, try them each and focus 
on the differences.
+Note that column arithmetic can be mixed with other ways to choose output 
columns (the @code{-c} option).
+
+@example
+$ asttable three-in-one-3.fits -ocolor-cat.fits \
+           -c1,2,3,4,'arith $5 $7 -'
+
+$ asttable three-in-one-3.fits -ocolor-cat.fits \
+           -c1,2,RA,DEC,'arith MAG-F125W MAG-F160W -'
+
+$ asttable three-in-one-3.fits -ocolor-cat.fits -c1,2 \
+           -cRA,DEC --column='arith MAG-F105W MAG-F160W -'
+@end example
 
-Gnuastro has a simple program for basic statistical analysis.
-The command below will print some basic information about the distribution 
(minimum, maximum, median, etc), along with a cute little ASCII histogram to 
visually help you understand the distribution on the command-line without the 
need for a graphic user interface.
-This ASCII histogram can be useful when you just want some coarse and general 
information on the input dataset.
-It is also useful when working on a server (where you may not have graphic 
user interface), and finally, its fast.
+This example again highlights the important point on using column names: if 
you don't know the commands before, you have no way of making sense of the 
first command: what is in column 5 and 7? why not subtract columns 3 and 4 from 
each other?
+Do you see how cryptic the first one is?
+Then look at the last one: even if you have no idea how this table was 
created, you immediately understand the desired operation.
+@strong{When you have column names, please use them.}
+If your table doesn't have column names, give them names with the 
@option{--colmetadata} (described above) as you are creating them.
+But how about the metadata for the column you just created with column 
arithmetic?
+Have a look at the column metadata of the table produced above:
 
 @example
-$ aststatistics astmatch.fits -cMATCH_DIST
-$ rm astmatch.fits
+$ asttable color-cat.fits -i
 @end example
 
-The units of this column are the same as the columns you gave to Match: in 
degrees.
-You see that while almost all the objects matched very nicely, the maximum 
distance is roughly 0.31 arcseconds.
-This is why we asked for an aperture of 0.35 arcseconds when doing the match.
+The name of the column produced by arithmetic column is @command{ARITH_1}!
+This is natural: Arithmetic has no idea what the modified column is!
+You could have multiplied two columns, or done much more complex 
transformations with many columns.
+@emph{Metadata can't be set automatically, your (the human) input is 
necessary.}
+To add metadata, you can use @option{--colmetadata} like before:
 
-Gnuastro's Table program can also be used to measure the colors using the 
command below.
-As before, the @option{-c1,2} option will tell Table to print the first two 
columns.
-With the @option{--range=SN_F160W,7,inf} we only keep the rows that have a 
F160W signal-to-noise ratio larger than 7@footnote{The value of 7 is taken from 
the clump S/N threshold in F160W (where the clumps were defined).}.
+@example
+$ asttable three-in-one-3.fits -ocolor-cat.fits -c1,2,RA,DEC \
+         --column='arith MAG-F105W MAG-F160W -' \
+         --colmetadata=ARITH_1,F105W-F160W,log,"Magnitude difference"
+$ asttable color-cat.fits -i
+@end example
 
-Finally, for estimating the colors, we use Table's column arithmetic feature.
-It uses the same notation as the Arithmetic program (see @ref{Reverse polish 
notation}), with almost all the same operators (see @ref{Arithmetic operators}).
-You can use column arithmetic in any output column, just put the value in 
double quotations and start the value with @code{arith} (followed by a space) 
like below.
-In column-arithmetic, you can identify columns by number or name, see 
@ref{Column arithmetic}.
+We are now ready to make our final table.
+We want it to have the magnitudes in all three filters, as well as the three 
possible colors.
+Recall that by convention in astronomy colors are defined by subtracting the 
bluer magnitude from the redder magnitude.
+In this way a larger color value corresponds to a redder object.
+So from the three magnitudes, we can produce three colors (as shown below).
+Also, because this is the final table we are creating here and want to use it 
later, we'll store it in @file{cat/} and we'll also give it a clear name and 
use the @option{--range} option to only print columns with a signal-to-noise 
ratio (@code{SN} column, from the F160W filter) above 5.
 
 @example
-$ asttable cat/xdf-f160w-f105w.fits -ocat/f105w-f160w.fits \
-           -c1,2,RA,DEC,"arith MAG_F105W MAG_F160W -"      \
-           --range=SN_F160W,7,inf
+$ asttable three-in-one-3.fits --range=SN,5,inf -c1,2,RA,DEC,SN \
+         -cMAG-F160W,MAG-F125W,MAG-F105W \
+         -c'arith MAG-F125W MAG-F160W -' \
+         -c'arith MAG-F105W MAG-F125W -' \
+         -c'arith MAG-F105W MAG-F160W -' \
+         --colmetadata=SN,SN-F160W,ratio,"F160W signal to noise ratio" \
+         --colmetadata=ARITH_1,F125W-F160W,log,"Color F125W and F160W" \
+         --colmetadata=ARITH_2,F105W-F125W,log,"Color F105W and F125W" \
+         --colmetadata=ARITH_3,F105W-F160W,log,"Color F105W and F160W" \
+         --output=cat/mags-with-color.fits
+$ asttable cat/mags-with-color.fits -i
 @end example
 
-@noindent
-You can inspect the distribution of colors with the Statistics program.
-But first, let's give the color column a proper name.
+The table now has all the columns we need and it has the proper metadata to 
let us safely use it later (without frustrating over column orders!) or passing 
it to colleagues.
+
+Let's finish this section of the tutorial with a useful tip on modifying 
column metadata.
+Above, updating/changing column metadata was done with the 
@option{--colmetadata} in the same command that produced the newly created 
Table file.
+But in many situations, the table is already made and you just want to update 
the metadata of one column.
+In such cases using @option{--colmetadata} is over-kill (wasting CPU/RAM 
energy or time if the table is large) because it will load the full table data 
and metadata into memory, just change the metadata and write it back into a 
file.
+
+In scenarios when the table's data doesn't need to be changed and you just 
want to set or update the metadata, it is much more efficient to use basic FITS 
keyword editing.
+For example, in the FITS standard, column names are stored in the @code{TTYPE} 
header keywords, so let's have a look:
 
 @example
-$ astfits cat/f105w-f160w.fits --update=TTYPE5,COLOR_F105W_F160W
-$ aststatistics cat/f105w-f160w.fits -cCOLOR_F105W_F160W
+$ asttable two-in-one.fits -i
+$ astfits two-in-one.fits -h1 | grep TTYPE
 @end example
 
+Changing/updating the column names is as easy as updating the values to these 
keywords.
+You don't need to touch the actual data!
+With the command below, we'll just update the @code{MAGNITUDE} and 
@code{MAGNITUDE-1} columns (which are respectively stored in the @code{TTYPE5} 
and @code{TTYPE11} keywords) by modifying the keyword values and checking the 
effect by listing the column metadata again:
+
+@example
+$ astfits two-in-one.fits -h1 \
+          --update=TTYPE5,MAG-F160W \
+          --update=TTYPE11,MAG-F125W
+$ asttable two-in-one.fits -i
+@end example
+
+You can see that the column names have indeed been changed without touching 
any of the data.
+You can do the same for the column units or comments by modifying the keywords 
starting with @code{TUNIT} or @code{TCOMM}.
+
+Generally, Gnuastro's table is a very useful program in data analysis and what 
you have seen so far is just the tip of the iceberg.
+But to avoid making the tutorial even longer, we'll stop reviewing the 
features here, for more, please see @ref{Table}.
+Before continuing, let's just delete all the temporary FITS tables we placed 
in the top project directory:
+
+@example
+rm *.fits
+@end example
+
+
+
+
+
+@node Column statistics color-magnitude diagram, Aperture photometry, Working 
with catalogs estimating colors, General program usage tutorial
+@subsection Column statistics (color-magnitude diagram)
+In @ref{Working with catalogs estimating colors} we created a single catalog 
containing the magnitudes of our desired clumps in all three filters, and their 
colors.
+To start with, let's inspect the distribution of three colors with the 
Statistics program.
+
+@example
+$ aststatistics cat/mags-with-color.fits -cF105W-F125W
+$ aststatistics cat/mags-with-color.fits -cF105W-F160W
+$ aststatistics cat/mags-with-color.fits -cF125W-F160W
+@end example
+
+This tiny and cute ASCII histogram (and the general information printed above 
it) gives you a crude (but very useful and fast) feeling on the distribution.
 You can later use Gnuastro's Statistics program with the @option{--histogram} 
option to build a much more fine-grained histogram as a table to feed into your 
favorite plotting program for a much more accurate/appealing plot (for example 
with PGFPlots in @LaTeX{}).
-If you just want a specific measure, for example the mean, median and standard 
deviation, you can ask for them specifically with this command:
+If you just want a specific measure, for example the mean, median and standard 
deviation, you can ask for them specifically, like below:
 
 @example
-$ aststatistics cat/f105w-f160w.fits -cCOLOR_F105W_F160W \
+$ aststatistics cat/mags-with-color.fits -cF105W-F160W \
                 --mean --median --std
 @end example
 
+@cindex Color-magnitude diagram
+The basic statistics we measured above were just on one column.
+In many scenarios this is fine, but things get much more exciting if you look 
at the correlation of two columns with each other.
+For example, let's create the color-magnitude diagram for our measured targets.
+
+@cindex Scatter plot
+@cindex 2D histogram
+@cindex Plot, scatter
+@cindex Histogram, 2D
+In many papers, the color-magnitude diagram is usually plotted as a scatter 
plot.
+However, scatter plots have a major limitation when there are a lot of points 
and they cluster together in one region of the plot: the possible correlation 
in that dense region is lost (because the points fall over each other).
+In such cases, its much better to use a 2D histogram.
+In a 2D histogram, the full range in both columns is divided into discrete 2D 
bins (or pixels!) and we count how many objects fall in that 2D bin.
+
+Since a 2D histogram is a pixelated space, we can simply save it as a FITS 
image and view it in a FITS viewer.
+Let's do this in the command below.
+As is common with color-magnitude plots, we'll put the redder magnitude on the 
horizontal axis and the color on the vertical axis.
+We'll set both dimensions to have 100 bins (with @option{--numbins} for the 
horizontal and @option{--numbins2} for the vertical).
+Also, to avoid strong outliers in any of the dimensions, we'll manually set 
the range of each dimension with the @option{--greaterequal}, 
@option{--greaterequal2}, @option{--lessthan} and @option{--lessthan2} options.
+
+@example
+$ aststatistics cat/mags-with-color.fits -cMAG-F160W,F105W-F160W \
+                --histogram2d=image --manualbinrange \
+                --numbins=100  --greaterequal=22  --lessthan=30 \
+                --numbins2=100 --greaterequal2=-1 --lessthan2=3 \
+                --manualbinrange --output=cmd.fits
+@end example
+
+@noindent
+You can now open this FITS file as a normal FITS image, for example with the 
command below.
+Try hovering/zooming over the pixels: not only will you see the number of 
objects in the UVUDF catalog that fall in each bin/pixel, but you also see the 
@code{F160W} magnitude and color of that pixel also (in the same place you 
usually see RA and Dec when hovering over an astronomical image).
+
+@example
+$ ds9 cmd.fits -cmap sls -zoom to fit
+@end example
+
+Having a 2D histogram as a FITS image with WCS has many great advantages.
+For example, just like FITS images of the night sky, you can ``match'' many 2D 
histograms that were created independently.
+You can add two histograms with each other, or you can use advanced features 
of FITS viewers to find structure in the correlation of your columns.
+
+@noindent
+With the first command below, you can activate the grid feature of DS9 to 
actually see the coordinate grid, as well as values on each line.
+With the second command, DS9 will even read the labels of the axises and use 
them to generate an almost publication-ready plot.
+
+@example
+$ ds9 cmd.fits -cmap sls -zoom to fit -grid yes
+$ ds9 cmd.fits -cmap sls -zoom to fit -grid yes -grid type publication
+@end example
+
+If you are happy with the grid and coloring and etc, you can also use ds9 to 
save this as a JPEG image to directly use in your documents/slides with these 
extra DS9 options (DS9 will write the image to @file{cmd-2d.jpeg} and quit 
immediately afterwards):
+
+@example
+$ ds9 cmd.fits -cmap sls -zoom 4 -grid yes -grid type publication \
+      -saveimage cmd-2d.jpeg -quit
+@end example
+
+@cindex PGFPlots (@LaTeX{} package)
+This is good for a fast progress update.
+But for your paper or more official report, you want to show something with 
higher quality.
+For that, you can use the PGFPlots package in @LaTeX{} to add axises in the 
same font as your text, sharp grids and many other elegant/powerful features 
(like over-plotting interesting points, lines and etc).
+But to load the 2D histogram into PGFPlots first you need to convert the FITS 
image into a more standard format, for example PDF.
+We'll use Gnuastro's @ref{ConvertType} for this, and use the 
@code{sls-inverse} color map (which will map the pixels with a value of zero to 
white):
+
+@example
+$ astconvertt cmd.fits --colormap=sls-inverse --borderwidth=0 -ocmd.pdf
+@end example
+
+@noindent
+Below you can see a minimally working example of how to add axis numbers, 
labels and a grid to the PDF generated above.
+First, let's create a new @file{report} directory to keep the @LaTeX{} 
outputs, then put the minimal report's source in a file called 
@file{report.tex}.
+Notice the @code{xmin}, @code{xmax}, @code{ymin}, @code{ymax} values and how 
they are the same as the range specified above.
+
+@example
+$ mkdir report
+$ mv cmd.pdf report/
+$ cat report/report.tex
+\documentclass@{article@}
+\usepackage@{pgfplots@}
+\dimendef\prevdepth=0
+\begin@{document@}
+
+You can write all you want here...\par
+
+\begin@{tikzpicture@}
+  \begin@{axis@}[
+      enlargelimits=false,
+      grid,
+      axis on top,
+      width=\linewidth,
+      height=\linewidth,
+      xlabel=@{Magnitude (F160W)@},
+      ylabel=@{Color (F105W-F160W)@}]
+
+    \addplot graphics[xmin=22, xmax=30, ymin=-1, ymax=3] @{cmd.pdf@};
+  \end@{axis@}
+\end@{tikzpicture@}
+\end@{document@}
+@end example
+
+@noindent
+Run this command to build your PDF (assuming you have @LaTeX{} and PGFPlots).
+
+@example
+$ cd report
+$ pdflatex report.tex
+@end example
+
+Open the newly created @file{report.pdf} and enjoy the exquisite quality.
+The improved quality, blending in with the text, vector-graphics resolution 
and other features make this plot pleasing to the eye, and let your readers 
focus on the main point of your scientific argument.
+PGFPlots can also built the PDF of the plot separately from the rest of the 
paper/report, see @ref{2D histogram as a table} for the necessary changes in 
the preamble.
+
+We won't go much deeper into the Statistics program here, but there is so much 
more you can do with it.
+After finishing the tutorial, see @ref{Statistics}.
 
-@node Aperture photometry, Finding reddest clumps and visual inspection, 
Working with catalogs estimating colors, General program usage tutorial
+
+
+
+
+@node Aperture photometry, Matching catalogs, Column statistics 
color-magnitude diagram, General program usage tutorial
 @subsection Aperture photometry
-Some researchers prefer to have colors in a fixed aperture for all the objects.
 The colors we calculated in @ref{Working with catalogs estimating colors} used 
a different segmentation map for each object.
-This might not satisfy some science cases.
-To make a catalog from fixed apertures, we should make a labeled image which 
has a fixed label for each aperture.
-That labeled image can be given to MakeCatalog instead of Segment's labeled 
detection image.
+This might not satisfy some science cases that need the flux within a fixed 
area/aperture.
+Fortunately Gnuastro's modular programs make it very easy do this type of 
measurement (photometry).
+To do this, we can ignore the labeled images of NoiseChisel of Segment, we can 
just built our own labeled image!
+That labeled image can then be given to MakeCatalog
 
 @cindex GNU AWK
 To generate the apertures catalog we'll use Gnuastro's MakeProfiles (see 
@ref{MakeProfiles}).
-We'll first read the clump positions from the F160W catalog, then use AWK to 
set the other parameters of each profile to be a fixed circle of radius 5 
pixels (recall that we want all apertures to be identical in this scenario).
+But first we need a list of positions (aperture photometry needs a-priori 
knowledge of your target positions).
+So we'll first read the clump positions from the F160W catalog, then use AWK 
to set the other parameters of each profile to be a fixed circle of radius 5 
pixels (recall that we want all apertures to have an identical size/area in 
this scenario).
 
 @example
 $ rm *.fits *.txt
-$ asttable cat/xdf-f160w.fits -hCLUMPS -cRA,DEC                    \
+$ asttable cat/xdf-f160w.fits -hCLUMPS -cRA,DEC \
            | awk '!/^#/@{print NR, $1, $2, 5, 5, 0, 0, 1, NR, 1@}' \
            > apertures.txt
+$ cat apertures.txt
 @end example
 
 We can now feed this catalog into MakeProfiles using the command below to 
build the apertures over the image.
@@ -3156,39 +3668,102 @@ Without it, MakeProfiles would build the profiles such 
that the @emph{sum} of th
 See @ref{Invoking astmkprof} for details on the options.
 
 @example
-$ astmkprof apertures.txt --background=flat-ir/xdf-f160w.fits     \
-            --clearcanvas --replace --type=int16 --mforflatpix    \
+$ astmkprof apertures.txt --background=flat-ir/xdf-f160w.fits \
+            --clearcanvas --replace --type=int16 --mforflatpix \
             --mode=wcs
 @end example
 
 The first thing you might notice in the printed information is that the 
profiles are not built in order.
 This is because MakeProfiles works in parallel, and parallel CPU operations 
are asynchronous.
-You can try running MakeProfiles with one thread (using 
@option{--numthreads=1}) to see how order is respected in that case.
+You can try running MakeProfiles with one thread (using 
@option{--numthreads=1}) to see how order is respected in that case, but slower 
(note that the multi-threaded run will be much more faster when more 
mathematically-complicated profiles are built, like S@'eric profiles).
 
-Open the output @file{apertures.fits} file and see the result.
+Open @file{apertures.fits} with a FITS viewer and look around at the circles 
placed over the targets.
+Also open the input image and Segment's clumps image and compare them with the 
positions of these circles.
 Where the apertures overlap, you will notice that one label has replaced the 
other (because of the @option{--replace} option).
 In the future, MakeCatalog will be able to work with overlapping labels, but 
currently it doesn't.
 If you are interested, please join us in completing Gnuastro with added 
improvements like this (see task 14750 
@footnote{@url{https://savannah.gnu.org/task/index.php?14750}}).
 
 We can now feed the @file{apertures.fits} labeled image into MakeCatalog 
instead of Segment's output as shown below.
-In comparison with the previous MakeCatalog call, you will notice that there 
is no more @option{--clumpscat} option, since each aperture is treated as a 
separate ``object'' here.
+In comparison with the previous MakeCatalog call, you will notice that there 
is no more @option{--clumpscat} option, since there is no more separate 
``clump'' image now, each aperture is treated as a separate ``object''.
 
 @example
-$ astmkcatalog apertures.fits -h1 --zeropoint=26.27        \
-               --valuesfile=nc/xdf-f105w.fits              \
-               --ids --ra --dec --magnitude --sn           \
+$ astmkcatalog apertures.fits -h1 --zeropoint=26.27 \
+               --valuesfile=nc/xdf-f105w.fits \
+               --ids --ra --dec --magnitude --sn \
                --output=cat/xdf-f105w-aper.fits
 @end example
 
 This catalog has the same number of rows as the catalog produced from clumps 
in @ref{Working with catalogs estimating colors}.
 Therefore similar to how we found colors, you can compare the aperture and 
clump magnitudes for example.
 
-You can also change the filter name and zeropoint magnitudes and run this
-command again to have the fixed aperture magnitude in the F160W filter and
-measure colors on apertures.
+You can also change the filter name and zero point magnitudes and run this 
command again to have the fixed aperture magnitude in the F160W filter and 
measure colors on apertures.
+
+
+
+@node Matching catalogs, Finding reddest clumps and visual inspection, 
Aperture photometry, General program usage tutorial
+@subsection Matching catalogs
+
+In the example above, we had the luxury to generate the catalogs ourselves, 
and where thus able to generate them in a way that the rows match.
+But this isn't generally the case.
+In many situations, you need to use catalogs from many different telescopes, 
or catalogs with high-level calculations that you can't simply regenerate with 
the same pixels without spending a lot of time or using heavy computation.
+In such cases, when each catalog has the coordinates of its own objects, you 
can use the coordinates to match the rows with Gnuastro's Match program (see 
@ref{Match}).
 
+As the name suggests, Gnuastro's Match program will match rows based on 
distance (or aperture in 2D) in one, two, or three columns.
+For this tutorial, let's try matching the two catalogs that weren't created 
from the same labeled images, recall how each has a different number of rows:
 
-@node Finding reddest clumps and visual inspection, Citing and acknowledging 
Gnuastro, Aperture photometry, General program usage tutorial
+@example
+$ asttable cat/xdf-f105w.fits -hCLUMPS -i
+$ asttable cat/xdf-f160w.fits -hCLUMPS -i
+@end example
+
+You give Match two catalogs (from the two different filters we derived above) 
as argument, and the HDUs containing them (if they are FITS files) with the 
@option{--hdu} and @option{--hdu2} options.
+The @option{--ccol1} and @option{--ccol2} options specify the 
coordinate-columns which should be matched with which in the two catalogs.
+With @option{--aperture} you specify the acceptable error (radius in 2D), in 
the same units as the columns.
+
+@example
+$ astmatch cat/xdf-f160w.fits           cat/xdf-f105w.fits \
+           --hdu=CLUMPS                 --hdu2=CLUMPS \
+           --ccol1=RA,DEC               --ccol2=RA,DEC \
+           --aperture=0.5/3600 \
+           --output=matched.fits
+$ astfits matched.fits
+@end example
+
+From the second command, you see that the output has two extensions and that 
both have the same number of rows.
+The rows in each extension are the matched rows of the respective input table: 
those in the first HDU come from the first input and those in the second HDU 
come from the second.
+However, their order may be different from the input tables because the rows 
match: the first row in the first HDU matches with the first row in the second 
HDU, and etc.
+You can also see which objects didn't match with the @option{--notmatched}, 
like below.
+Note how each extension of  now has a different number of rows.
+
+@example
+$ astmatch cat/xdf-f160w.fits           cat/xdf-f105w.fits \
+           --hdu=CLUMPS                 --hdu2=CLUMPS \
+           --ccol1=RA,DEC               --ccol2=RA,DEC \
+           --aperture=0.5/3600 \
+           --output=not-matched.fits    --notmatched
+$ astfits not-matched.fits
+@end example
+
+The @option{--outcols} of Match is a very convenient feature: you can use it 
to specify which columns from the two catalogs you want in the output (merge 
two input catalogs into one).
+If the first character is an `@key{a}', the respective matched column (number 
or name, similar to Table above) in the first catalog will be written in the 
output table.
+When the first character is a `@key{b}', the respective column from the second 
catalog will be written in the output.
+Also, if the first character is followed by @code{_all}, then all the columns 
from the respective catalog will be put in the output.
+
+@example
+$ astmatch cat/xdf-f160w.fits           cat/xdf-f105w.fits \
+           --hdu=CLUMPS                 --hdu2=CLUMPS \
+           --ccol1=RA,DEC               --ccol2=RA,DEC \
+           --aperture=0.35/3600 \
+           --outcols=a_all,bMAGNITUDE,bSN \
+           --output=matched.fits
+$ astfits matched.fits
+@end example
+
+
+
+
+
+@node Finding reddest clumps and visual inspection, Writing scripts to 
automate the steps, Matching catalogs, General program usage tutorial
 @subsection Finding reddest clumps and visual inspection
 @cindex GNU AWK
 As a final step, let's go back to the original clumps-based color measurement 
we generated in @ref{Working with catalogs estimating colors}.
@@ -3196,24 +3771,31 @@ We'll find the objects with the strongest color and 
make a cutout to inspect the
 With the command below, we'll select the reddest objects (those with a color 
larger than 1.5):
 
 @example
-$ asttable cat/f105w-f160w.fits --range=COLOR_F105W_F160W,1.5,inf
+$ asttable cat/mags-with-color.fits --range=F105W-F160W,1.5,inf
 @end example
 
-We want to crop the F160W image around each of these objects, but we need a 
unique identifier for them first.
+@noindent
+You can see how many they are by piping it to @code{wc -l}:
+
+@example
+$ asttable cat/mags-with-color.fits --range=F105W-F160W,1.5,inf | wc -l
+@end example
+
+Let's crop the F160W image around each of these objects, but we first need a 
unique identifier for them.
 We'll define this identifier using the object and clump labels (with an 
underscore between them) and feed the output of the command above to AWK to 
generate a catalog.
-Note that since we are making a plain text table, we'll define the column 
metadata manually (see @ref{Gnuastro text table format}).
+Note that since we are making a plain text table, we'll define the necessary 
(for the string-type first column) metadata manually (see @ref{Gnuastro text 
table format}).
 
 @example
 $ echo "# Column 1: ID [name, str10] Object ID" > reddest.txt
-$ asttable cat/f105w-f160w.fits --range=COLOR_F105W_F160W,1.5,inf \
+$ asttable cat/mags-with-color.fits --range=F105W-F160W,1.5,inf \
            | awk '@{printf("%d_%-10d %f %f\n", $1, $2, $3, $4)@}' \
            >> reddest.txt
 @end example
 
 We can now feed @file{reddest.txt} into Gnuastro's Crop program to see what 
these objects look like.
 To keep things clean, we'll make a directory called @file{crop-red} and ask 
Crop to save the crops in this directory.
-We'll also add a @file{-f160w.fits} suffix to the crops (to remind us which 
image they came from).
-The width of the crops will be 15 arcseconds.
+We'll also add a @file{-f160w.fits} suffix to the crops (to remind us which 
filter they came from).
+The width of the crops will be 15 arc-seconds (or 15/3600 degrees, which is 
the units of the WCS).
 
 @example
 $ mkdir crop-red
@@ -3235,6 +3817,7 @@ $ for f in *.fits; do                                     
             \
     astconvertt $f --fluxlow=-0.001 --fluxhigh=0.005 --invert -ojpg;   \
   done
 $ cd ..
+$ ls crop-red/
 @end example
 
 You can now use your general graphic user interface image viewer to flip 
through the images more easily, or import them into your papers/reports.
@@ -3253,6 +3836,12 @@ $ parallel astconvertt --fluxlow=-0.001 --fluxhigh=0.005 
--invert   \
 $ cd ..
 @end example
 
+@noindent
+Did you notice how much faster this one was? When possible, its always very 
helpful to do your analysis in parallel.
+But the problem is that many operations are not as simple as this.
+For such cases, you can use 
@url{https://en.wikipedia.org/wiki/Make_(software), Make} which will greatly 
help designing workflows.
+But that is beyond the topic here.
+
 @cindex DS9
 @cindex SAO DS9
 As the final action, let's see how these objects are positioned over the 
dataset.
@@ -3276,48 +3865,380 @@ $ ds9 -mecube seg/xdf-f160w.fits -zscale -zoom to fit  
  \
       -regions load all reddest.reg
 @end example
 
+@node Writing scripts to automate the steps, Citing and acknowledging 
Gnuastro, Finding reddest clumps and visual inspection, General program usage 
tutorial
+@subsection Writing scripts to automate the steps
 
-@node Citing and acknowledging Gnuastro,  , Finding reddest clumps and visual 
inspection, General program usage tutorial
-@subsection Citing and acknowledging Gnuastro
-In conclusion, we hope this extended tutorial has been a good starting point 
to help in your exciting research.
-If this book or any of the programs in Gnuastro have been useful for your 
research, please cite the respective papers, and acknowledge the funding 
agencies that made all of this possible.
-All Gnuastro programs have a @option{--cite} option to facilitate the citation 
and acknowledgment.
-Just note that it may be necessary to cite additional papers for different 
programs, so please try it out on all the programs that you used, for example:
+In the previous sub-sections, we went through a series of steps like 
downloading the necessary datasets (in @ref{Setup and data download}), 
detecting the objects in the image, and finally selecting a particular subset 
of them to inspect visually (in @ref{Finding reddest clumps and visual 
inspection}).
+To benefit most effectively from this subsection, please go through the 
previous sub-sections, and if you haven't actually done them, we recommended to 
do/run them before continuing here.
+
+@cindex @command{history}
+@cindex Shell history
+Each sub-section/step of the sub-sections above involved several commands on 
the command-line.
+Therefore, if you want to reproduce the previous results (for example to only 
change one part, and see its effect), you'll have to go through all the 
sections above and read through them again.
+If you done the commands recently, you may also have them in the history of 
your shell (command-line environment).
+You can see many of your previous commands on the shell (even if you have 
closed the terminal) with the @command{history} command, like this:
 
 @example
-$ astmkcatalog --cite
-$ astnoisechisel --cite
+$ history
 @end example
 
+@cindex GNU Bash
+Try it in your teminal to see for your self.
+By default in GNU Bash, it shows the last 500 commands.
+You can also save this ``history'' of previous commands to a file using shell 
redirection (to have it after your next 500 commands), with this command
 
+@example
+$ history > my-previous-commands.txt
+@end example
 
+This is a good way to temporarily keep track of every single command you ran.
+But in the middle of all the useful commands, you will have many extra 
commands, like tests that you did before/after the good output of a step (that 
you decided to continue working on), or an unrelated job you had to do in the 
middle of this project.
+Because of these impurities, after a few days (that you have forgot the 
context: tests you didn't end-up using, or unrelated jobs) reading this full 
history will be very frustrating.
 
+Keeping the final commands that were used in each step of an analysis is a 
common problem for anyone who is doing something serious with the computer.
+But simply keeping the most important commands in a text file is not enough, 
the small steps in the middle (like making a directory to keep the outputs of 
one step) are also important.
+In other words, the only way you can be sure that you are under control of 
your processing (and actually understand how you produced your final result) is 
to run the commands automatically.
 
+@cindex Shell script
+@cindex Script, shell
+Fortunately, typing commands interactively with your fingers isn't the only 
way to operate the shell.
+The shell can also take its orders/commands from a plain-text file, which is 
called a @emph{script}.
+When given a script, the shell will read it line-by-line as if you have 
actually typed it manually.
+
+@cindex Redirection in shell
+@cindex Shell redirection
+Let's continue with an example: try typing the commands below in your shell.
+With these commands we are making a text file (@code{a.txt}) containing a 
simple @mymath{3\times3} matrix, converting it to a FITS image and computing 
its basic statistics.
+After the first three commands open @file{a.txt} with a text editor to 
actually see the values we wrote in it, and after the fourth, open the FITS 
file to see the matrix as an image.
+@file{a.txt} is created through the shell's redirection feature: `@code{>}' 
overwrites the existing contents of a file, and `@code{>>}' appends the new 
contents after the old contents.
+
+@example
+$ echo "1 1 1" > a.txt
+$ echo "1 2 1" >> a.txt
+$ echo "1 1 1" >> a.txt
+$ astconvertt a.txt --output=a.fits
+$ aststatistics a.fits
+@end example
+
+To automate these series of commands, you should put them in a text file.
+But that text file must have two special features:
+1) It should tell the shell what program should interpret the script.
+2) The operating system should know that the file can be directly executed.
+
+@cindex Shebang
+@cindex Hashbang
+For the first, Unix-like operating systems define the @emph{shebang} concept 
(also known as @emph{sha-bang} or @emph{hashbang}).
+In the shebang convention, the first two characters of a file should be 
`@code{#!}'.
+When confronted with these characters, the script will be interpreted with the 
program that follows them.
+In this case, we want to write a shell script and the most common shell 
program is GNU Bash which is installed in @file{/bin/bash}.
+So the first line of your script should be `@code{#!/bin/bash}'@footnote{
+When the script is to be run by the same shell that is calling it (like this 
script), the shebang is optional.
+But it is still recommended, because it ensures that even if the user isn't 
using GNU Bash, the script will be run in GNU Bash: given the differences 
between various shells, writing truely portable shell scripts, that can be run 
by many shell programs/implementations, isn't easy (sometimes not possible!).}.
+
+It may happen (rarely) that GNU Bash is in another location on your system.
+In other cases, you may prefer to use a non-standard version of Bash installed 
in another location (that has higher priority in your @code{PATH}, see 
@ref{Installation directory}).
+In such cases, you can use the `@code{#!/usr/bin/env bash}' shebang instead.
+Through the @code{env} program, this shebang will look in your @code{PATH} and 
use the first @command{bash} it finds to run your script.
+But for simplicity in the rest of the tutorial, we'll continue with the 
`@code{#!/bin/bash}' shebang.
+
+Using your favorite text editor, make a new empty file, let's call it 
@file{my-first-script.sh}.
+Write the GNU Bash shebang (above) as its first line
+After the shebang, copy the series of commands we ran above.
+Just note that the `@code{$}' sign at the start of every line above is the 
prompt of the interactive shell (you never actually typed it, remember?).
+Therefore, commands in a shell script should not start with a `@code{$}'.
+Once you add the commands, close the text editor and run the @command{cat} 
command to confirm its contents.
+It should look like the example below.
+Recall that you should only type the line that starts with a `@code{$}', the 
lines without a `@code{$}', are printed automatically on the command-line (they 
are the contents of your script).
+
+@example
+$ cat my-first-script.sh
+#!/bin/bash
+echo "1 1 1" > a.txt
+echo "1 2 1" >> a.txt
+echo "1 1 1" >> a.txt
+astconvertt a.txt --output=a.fits
+aststatistics a.fits
+@end example
+
+@cindex File flags
+@cindex Flags, file
+The script contents are now ready, but to run it, you should activate the 
script file's @emph{executable flag}.
+In Unix-like operating systems, every file has three types of flags: 
@emph{read} (or @code{r}), @emph{write} (or @code{w}) and @emph{execute} (or 
@code{x}).
+To toggle a file's flags, you should use the @command{chmod} (for ``change 
mode'') command.
+To activate a flag, you put a `@code{+}' before the flag character (for 
example @code{+x}).
+To deactivate it, you put a `@code{-}' (for example @code{-x}).
+In this case, you want to activate the script's executable flag, so you should 
run
+
+@example
+$ chmod +x my-first-script.sh
+@end example
+
+Your script is now ready to run/execute the series of commands.
+To run it, you should call it while specifying its location in the file system.
+Since you are currently in the same directory as the script, its easiest to 
use relative addressing like below (where `@code{./}' means the current 
directory).
+But before running your script, first delete the two @file{a.txt} and 
@file{a.fits} files that were created when you interactively ran the commands.
+
+@example
+$ rm a.txt a.fits
+$ ls
+$ ./my-first-script.sh
+$ ls
+@end example
 
+@noindent
+The script immediately prints the statistics while doing all the previous 
steps in the background.
+With the last @command{ls}, you see that it automatically re-built the 
@file{a.txt} and @file{a.fits} files, open them and have a look at their 
contents.
 
+An extremely useful feature of shell scripts is that the shell will ignore 
anything after a `@code{#}' character.
+You can thus add descriptions/comments to the commands and make them much more 
useful for the future.
+For example, after adding comments, your script might look like this:
 
-@node Detecting large extended targets,  , General program usage tutorial, 
Tutorials
-@section Detecting large extended targets
+@example
+$ cat my-first-script.sh
+#!/bin/bash
 
-The outer wings of large and extended objects can sink into the noise very 
gradually and can have a large variety of shapes (for example due to tidal 
interactions).
-Therefore separating the outer boundaries of the galaxies from the noise can 
be particularly tricky.
-Besides causing an under-estimation in the total estimated brightness of the 
target, failure to detect such faint wings will also cause a bias in the noise 
measurements, thereby hampering the accuracy of any measurement on the dataset.
-Therefore even if they don't constitute a significant fraction of the target's 
light, or aren't your primary target, these regions must not be ignored.
-In this tutorial, we'll walk you through the strategy of detecting such 
targets using @ref{NoiseChisel}.
+# This script is my first attempt at learning to write shell scripts.
+# As a simple series of commands, I am just building a small FITS
+# image, and calculating its basic statistics.
+
+# Write the matrix into a file.
+echo "1 1 1" > a.txt
+echo "1 2 1" >> a.txt
+echo "1 1 1" >> a.txt
+
+# Convert the matrix to a FITS image.
+astconvertt a.txt --output=a.fits
+
+# Calculate the statistics of the FITS image.
+aststatistics a.fits
+@end example
 
-@cartouche
 @noindent
-@strong{Don't start with this tutorial:} If you haven't already completed 
@ref{General program usage tutorial}, we strongly recommend going through that 
tutorial before starting this one.
-Basic features like access to this book on the command-line, the configuration 
files of Gnuastro's programs, benefiting from the modular nature of the 
programs, viewing multi-extension FITS files, or using NoiseChisel's outputs 
are discussed in more detail there.
-@end cartouche
+Isn't this much more easier to read now?
+Comments help to provide human-friendly context to the raw commands.
+At the time you make a script, comments may seem like an extra effort and slow 
you down.
+But in one year, you will forget almost everything about your script and you 
will appreciate the effort so much!
+Think of the comments as an email to your future-self and always put a 
well-written description of the context/purpose (most importantly, things that 
aren't directly clear by reading the commands) in your scripts.
 
-@cindex M51
-@cindex NGC5195
+The example above was very basic and mostly redundant series of commands, to 
show the basic concepts behind scripts.
+You can put any (arbitrarily long and complex) series of commands in a script 
by following the two rules: 1) add a shebang, and 2) enable the executable flag.
+Infact, as you continue your own research projects, you will find that any 
time you are dealing with more than two or three commands, keeping them in a 
script (and modifying that script, and running it) is much more easier, and 
future-proof, then typing the commands directly on the command-line and relying 
on things like @command{history}. Here are some tips that will come in handy 
when you are writing your scripts:
+
+As a more realistic example, let's have a look at a script that will do the 
steps of @ref{Setup and data download} and @ref{Dataset inspection and 
cropping}.
+In particular note how often we are using variables to avoid repeating fixed 
strings of characters (usually file/directory names).
+This greatly helps in scaling up your project, and avoiding hard-to-find bugs 
that are caused by typos in those fixed strings.
+
+@example
+$ cat gnuastro-tutorial-1.sh
+#!/bin/bash
+
+
+# Download the input datasets
+# ---------------------------
+#
+# The default file names have this format (where `FILTER' differs for
+# each filter):
+#   hlsp_xdf_hst_wfc3ir-60mas_hudf_FILTER_v1_sci.fits
+# To make the script easier to read, a prefix and suffix variable are
+# used to sandwich the filter name into one short line.
+downloaddir=download
+xdfsuffix=_v1_sci.fits
+xdfprefix=hlsp_xdf_hst_wfc3ir-60mas_hudf_
+xdfurl=http://archive.stsci.edu/pub/hlsp/xdf
+
+# The file name and full URLs of the input data.
+f105w_in=$xdfprefix"f105w"$xdfsuffix
+f160w_in=$xdfprefix"f160w"$xdfsuffix
+f105w_full=$xdfurl/$f105w_in
+f160w_full=$xdfurl/$f160w_in
+
+# Go into the download directory and download the images there,
+# then come back up to the top running directory.
+mkdir $downloaddir
+cd $downloaddir
+wget $f105w_full
+wget $f160w_full
+cd ..
+
+
+# Only work on the deep region
+# ----------------------------
+#
+# To help in readability, each vertice of the deep/flat field is stored
+# as a separate variable. They are then merged into one variable to
+# define the polygon.
+flatdir=flat-ir
+vertice1="53.187414,-27.779152"
+vertice2="53.159507,-27.759633"
+vertice3="53.134517,-27.787144"
+vertice4="53.161906,-27.807208"
+f105w_flat=$flatdir/xdf-f105w.fits
+f160w_flat=$flatdir/xdf-f160w.fits
+deep_polygon="$vertice1:$vertice2:$vertice3:$vertice4"
+
+mkdir $flatdir
+astcrop --mode=wcs -h0 --output=$f105w_flat \
+        --polygon=$deep_polygon $downloaddir/$f105w_in
+astcrop --mode=wcs -h0 --output=$f160w_flat \
+        --polygon=$deep_polygon $downloaddir/$f160w_in
+@end example
+
+The first thing you may notice is that even if you already have the downloaded 
input images, this script will always try to re-download them.
+Also, if you re-run the script, you will notice that @command{mkdir} prints an 
error message that the download directory already exists.
+Therefore, the script above isn't too useful and some modifications are 
necessary to make it more generally useful.
+Here are some general tips that are often very useful when writing scripts:
+
+@table @strong
+@item Stop script if a command crashes
+By default, if a command in a script crashes (aborts and fails to do what it 
was meant to do), the script will continue onto the next command.
+In GNU Bash, you can tell the shell to stop a script in the case of a crash by 
adding this line at the start of your script:
+
+@example
+set -e
+@end example
+
+@item Check if a file/directory exists to avoid re-creating it
+Conditionals are a very useful feature in scripts.
+One common conditional is to check if a file exists or not.
+Assuming the file's name is @file{FILENAME}, you can check its existance (to 
avoid re-doing the commands that build it) like this:
+@example
+if [ -f FILENAME ]; then
+  echo "FILENAME exists"
+else
+  # Some commands to generate the file
+  echo "done" > FILENAME
+fi
+@end example
+To check the existance of a directory instead of a file, use @code{-d} instead 
of @code{-f}.
+To negate a conditional, use `@code{!}' and note that conditionals can be 
written in one line also (useful for when its short).
+
+One common scenario that you'll need to check the existance of directories is 
when you are making them: the default @command{mkdir} command will crash if the 
desired directory already exists.
+On some systems (including GNU/Linux distributions), @code{mkdir} has options 
to deal with such cases. But if you want your script to be portable, its best 
to check yourself like below:
+
+@example
+if ! [ -d DIRNAME ]; then mkdir DIRNAME; fi
+@end example
+@end table
+
+@noindent
+Taking these tips into consideration, we can write a better version of the 
script above that includes checks on every step to avoid repeating 
steps/commands.
+Please compare this script with the previous one carefully to spot the 
differences.
+These are very important points that you will definitely encouter during your 
own research, and knowing them can greatly help your productiveity, so pay 
close attention (even in the comments).
+
+@example
+$ cat gnuastro-tutorial-2.sh
+#!/bin/bash
+set -e
+
+
+# Download the input datasets
+# ---------------------------
+#
+# The default file names have this format (where `FILTER' differs for
+# each filter):
+#   hlsp_xdf_hst_wfc3ir-60mas_hudf_FILTER_v1_sci.fits
+# To make the script easier to read, a prefix and suffix variable are
+# used to sandwich the filter name into one short line.
+downloaddir=download
+xdfsuffix=_v1_sci.fits
+xdfprefix=hlsp_xdf_hst_wfc3ir-60mas_hudf_
+xdfurl=http://archive.stsci.edu/pub/hlsp/xdf
+
+# The file name and full URLs of the input data.
+f105w_in=$xdfprefix"f105w"$xdfsuffix
+f160w_in=$xdfprefix"f160w"$xdfsuffix
+f105w_full=$xdfurl/$f105w_in
+f160w_full=$xdfurl/$f160w_in
+
+# Go into the download directory and download the images there,
+# then come back up to the top running directory.
+if ! [ -d $downloaddir ]; then mkdir $downloaddir; fi
+cd $downloaddir
+if ! [ -f $f105w_in ]; then wget $f105w_full; fi
+if ! [ -f $f160w_in ]; then wget $f160w_full; fi
+cd ..
+
+
+# Only work on the deep region
+# ----------------------------
+#
+# To help in readability, each vertice of the deep/flat field is stored
+# as a separate variable. They are then merged into one variable to
+# define the polygon.
+flatdir=flat-ir
+vertice1="53.187414,-27.779152"
+vertice2="53.159507,-27.759633"
+vertice3="53.134517,-27.787144"
+vertice4="53.161906,-27.807208"
+f105w_flat=$flatdir/xdf-f105w.fits
+f160w_flat=$flatdir/xdf-f160w.fits
+deep_polygon="$vertice1:$vertice2:$vertice3:$vertice4"
+
+if ! [ -d $flatdir ]; then mkdir $flatdir; fi
+if ! [ -f $f105w_flat ]; then
+    astcrop --mode=wcs -h0 --output=$f105w_flat \
+            --polygon=$deep_polygon $downloaddir/$f105w_in
+fi
+if ! [ -f $f160w_flat ]; then
+    astcrop --mode=wcs -h0 --output=$f160w_flat \
+            --polygon=$deep_polygon $downloaddir/$f160w_in
+fi
+@end example
+
+@node Citing and acknowledging Gnuastro,  , Writing scripts to automate the 
steps, General program usage tutorial
+@subsection Citing and acknowledging Gnuastro
+In conclusion, we hope this extended tutorial has been a good starting point 
to help in your exciting research.
+If this book or any of the programs in Gnuastro have been useful for your 
research, please cite the respective papers, and acknowledge the funding 
agencies that made all of this possible.
+Without citations, we won't be able to secure future funding to continue 
working on Gnuastro or improving it, so please take software citation seriously 
(for all the scientific software you use, not just Gnuastro).
+
+To help you in this aspect is well, all Gnuastro programs have a 
@option{--cite} option to facilitate the citation and acknowledgment.
+Just note that it may be necessary to cite additional papers for different 
programs, so please try it out on all the programs that you used, for example:
+
+@example
+$ astmkcatalog --cite
+$ astnoisechisel --cite
+@end example
+
+
+
+
+
+
+
+
+@node Detecting large extended targets,  , General program usage tutorial, 
Tutorials
+@section Detecting large extended targets
+
+The outer wings of large and extended objects can sink into the noise very 
gradually and can have a large variety of shapes (for example due to tidal 
interactions).
+Therefore separating the outer boundaries of the galaxies from the noise can 
be particularly tricky.
+Besides causing an under-estimation in the total estimated brightness of the 
target, failure to detect such faint wings will also cause a bias in the noise 
measurements, thereby hampering the accuracy of any measurement on the dataset.
+Therefore even if they don't constitute a significant fraction of the target's 
light, or aren't your primary target, these regions must not be ignored.
+In this tutorial, we'll walk you through the strategy of detecting such 
targets using @ref{NoiseChisel}.
+
+@cartouche
+@noindent
+@strong{Don't start with this tutorial:} If you haven't already completed 
@ref{General program usage tutorial}, we strongly recommend going through that 
tutorial before starting this one.
+Basic features like access to this book on the command-line, the configuration 
files of Gnuastro's programs, benefiting from the modular nature of the 
programs, viewing multi-extension FITS files, or using NoiseChisel's outputs 
are discussed in more detail there.
+@end cartouche
+
+@cindex M51
+@cindex NGC5195
 @cindex SDSS, Sloan Digital Sky Survey
 @cindex Sloan Digital Sky Survey, SDSS
 We'll try to detect the faint tidal wings of the beautiful M51 
group@footnote{@url{https://en.wikipedia.org/wiki/M51_Group}} in this tutorial.
 We'll use a dataset/image from the public @url{http://www.sdss.org/, Sloan 
Digital Sky Survey}, or SDSS.
 Due to its more peculiar low surface brightness structure/features, we'll 
focus on the dwarf companion galaxy of the group (or NGC 5195).
+
+
+@menu
+* Downloading and validating input data::  How to get and check the input data.
+* NoiseChisel optimization::    Detect the extended and diffuse wings.
+* Achieved surface brightness level::  Calculate the outer surface brightness.
+@end menu
+
+@node Downloading and validating input data, NoiseChisel optimization, 
Detecting large extended targets, Detecting large extended targets
+@subsection Downloading and validating input data
+
 To get the image, you can use SDSS's @url{https://dr12.sdss.org/fields, Simple 
field search} tool.
 As long as it is covered by the SDSS, you can find an image containing your 
desired target either by providing a standard name (if it has one), or its 
coordinates.
 To access the dataset we will use here, write @code{NGC5195} in the ``Object 
Name'' field and press ``Submit'' button.
@@ -3345,10 +4266,50 @@ $ 
topurl=https://dr12.sdss.org/sas/dr12/boss/photoObj/frames
 $ wget $topurl/301/3716/6/frame-r-003716-6-0117.fits.bz2 -Or.fits.bz2
 @end example
 
+When you want to reproduce a previous result (a known analysis, on a known 
dataset, to get a known result: like the case here!) it is important to verify 
that the file is correct: that the input file hasn't changed (on the remote 
server, or in your own archive), or there was no downloading problem.
+Otherwise, if the data have changed in your server/archive, and you use the 
same script, you will get a different result, causing a lot of confusion!
+
+@cindex Checksum
+@cindex SHA-1 checksum
+@cindex Verification, checksum
+One good way to verify the contents of a file is to store its @emph{Checksum} 
in your analysis script and check it before any other operation.
+The @emph{Checksum} algorithms look into the contents of a file and calculate 
a fixed-length string from them.
+If any change (even in a bit or byte) is made within the file, the resulting 
string will change, for more see @url{https://en.wikipedia.org/wiki/Checksum, 
Wikipedia}.
+There are many common algorithms, but a simple one is the 
@url{https://en.wikipedia.org/wiki/SHA-1, SHA-1 algorithm} (Secure Hash 
Algorithm 1) that you can calculate easily with the command below (the second 
line is the output, and the checksum is the first/long string: it is 
independent of the file name)
+
+@example
+$ sha1sum r.fits.bz2
+5fb06a572c6107c72cbc5eb8a9329f536c7e7f65  r.fits.bz2
+@end example
+
+If the checksum on your computer is different from this, either the file has 
been incorrectly downloaded (most probable), or it has changed on SDSS servers 
(very unlikely@footnote{If your checksum is different, try uncompressing the 
file with the @command{bunzip2} command after this, and open the resulting FITS 
file.
+If it opens and you see the image of M51 and NGC5195, then there was no 
download problem, and the file has indeed changed on the SDSS servers!
+In this case, please contact us at @code{bug-gnuastro@@gnu.org}.}).
+To get a better feeling of checksums open your favorite text editor and make a 
test file by writing something in it.
+Save it and calculate the text file's SHA-1 checksum with @command{sha1sum}.
+Try renaming that file, and you'll see the checksum hasn't changed (checksums 
only look into the contents, not the name/location of the file).
+Then open the file with your text editor again, make a change and re-calculate 
its checksum, you'll see the checksum string has changed.
+
+Its always good to keep this short checksum string with your project's scripts 
and validate your input data before using them.
+You can do this with a shell conditional like this:
+
+@example
+filename=r.fits.bz2
+expected=5fb06a572c6107c72cbc5eb8a9329f536c7e7f65
+sum=$(sha1sum $filename | awk '@{print $1@}')
+if [ $sum = $expected ]; then
+  echo "$filename: validated"
+else
+  echo "$filename: wrong checksum!"
+  exit 1
+fi
+@end example
+
 @cindex Bzip2
 @noindent
-This server keeps the files in a Bzip2 compressed file format.
-So we'll first decompress it with the following command.
+Now that we know you have the same data that we wrote this tutorial with, 
let's continue.
+The SDSS server keeps the files in a Bzip2 compressed file format (that have a 
@code{.bz2} suffix).
+So we'll first decompress it with the following command to use it as a normal 
FITS file.
 By convention, compression programs delete the original file (compressed when 
uncompressing, or uncompressed when compressing).
 To keep the original file, you can use the @option{--keep} or @option{-k} 
option which is available in most compression programs for this job.
 Here, we don't need the compressed file any more, so we'll just let 
@command{bunzip} delete it for us and keep the directory clean.
@@ -3363,7 +4324,7 @@ $ bunzip2 r.fits.bz2
 * Achieved surface brightness level::  Measure how much you detected.
 @end menu
 
-@node NoiseChisel optimization, Achieved surface brightness level, Detecting 
large extended targets, Detecting large extended targets
+@node NoiseChisel optimization, Achieved surface brightness level, Downloading 
and validating input data, Detecting large extended targets
 @subsection NoiseChisel optimization
 In @ref{Detecting large extended targets} we downloaded the single exposure 
SDSS image.
 Let's see how NoiseChisel operates on it with its default parameters:
@@ -3372,8 +4333,8 @@ Let's see how NoiseChisel operates on it with its default 
parameters:
 $ astnoisechisel r.fits -h0
 @end example
 
-As described in @ref{Multiextension FITS files NoiseChisel's output}, 
NoiseChisel's default output is a multi-extension FITS file.
-Open the output @file{r_detected.fits} file and have a look at the extensions, 
the first extension is only meta-data and contains NoiseChisel's configuration 
parameters.
+As described in @ref{NoiseChisel and Multiextension FITS files}, NoiseChisel's 
default output is a multi-extension FITS file.
+Open the output @file{r_detected.fits} file and have a look at the extensions, 
the 0-th extension is only meta-data and contains NoiseChisel's configuration 
parameters.
 The rest are the Sky-subtracted input, the detection map, Sky values and Sky 
standard deviation.
 
 @example
@@ -3381,30 +4342,31 @@ $ ds9 -mecube r_detected.fits -zscale -zoom to fit
 @end example
 
 Flipping through the extensions in a FITS viewer, you will see that the first 
image (Sky-subtracted image) looks reasonable: there are no major artifacts due 
to bad Sky subtraction compared to the input.
-The second extension also seems reasonable with a large detection map that 
covers the whole of NGC5195, but also extends beyond towards the bottom of the 
image.
+The second extension also seems reasonable with a large detection map that 
covers the whole of NGC5195, but also extends towards the bottom of the image 
where we actually see faint and diffuse signal in the input image.
 
 Now try flipping between the @code{DETECTIONS} and @code{SKY} extensions.
 In the @code{SKY} extension, you'll notice that there is still significant 
signal beyond the detected pixels.
-You can tell that this signal belongs to the galaxy because the far-right side 
of the image is dark and the brighter tiles are surrounding the detected pixels.
+You can tell that this signal belongs to the galaxy because the far-right side 
of the image (away from M51) is dark (has lower values) and the brighter parts 
in the Sky image (with larger values) are just under the detections and follow 
a similar pattern.
 
-The fact that signal from the galaxy remains in the Sky dataset shows that you 
haven't done a good detection.
+The fact that signal from the galaxy remains in the @code{SKY} HDU shows that 
NoiseChisel can be optimized for a much better result.
 The @code{SKY} extension must not contain any light around the galaxy.
-Generally, any time your target is much larger than the tile size and the 
signal is almost flat (like this case), this @emph{will} happen.
+Generally, any time your target is much larger than the tile size and the 
signal is very diffuse and extended at low signal-to-noise values (like this 
case), this @emph{will} happen.
 Therefore, when there are large objects in the dataset, @strong{the best 
place} to check the accuracy of your detection is the estimated Sky image.
 
 When dominated by the background, noise has a symmetric distribution.
 However, signal is not symmetric (we don't have negative signal).
-Therefore when non-constant signal is present in a noisy dataset, the 
distribution will be positively skewed.
-This skewness is a good measure of how much signal we have in the distribution.
-The skewness can be accurately measured by the difference in the mean and 
median: assuming no strong outliers, the more distant they are, the more skewed 
the dataset is.
+Therefore when non-constant@footnote{by constant, we mean that it has a single 
value in the region we are measuring.} signal is present in a noisy dataset, 
the distribution will be positively skewed.
+For a demonstration, see Figure 1 of @url{https://arxiv.org/abs/1505.01664, 
Akhlaghi and Ichikawa [2015]}.
+This skewness is a good measure of how much faint signal we have in the 
distribution.
+The skewness can be accurately measured by the difference in the mean and 
median (assuming no strong outliers): the more distant they are, the more 
skewed the dataset is.
 For more see @ref{Quantifying signal in a tile}.
 
 However, skewness is only a proxy for signal when the signal has structure 
(varies per pixel).
-Therefore, when it is approximately constant over a whole tile, or sub-set of 
the image, the signal's effect is just to shift the symmetric center of the 
noise distribution to the positive and there won't be any skewness (major 
difference between the mean and median).
+Therefore, when it is approximately constant over a whole tile, or sub-set of 
the image, the constant signal's effect is just to shift the symmetric center 
of the noise distribution to the positive and there won't be any skewness 
(major difference between the mean and median).
 This positive@footnote{In processed images, where the Sky value can be 
over-estimated, this constant shift can be negative.} shift that preserves the 
symmetric distribution is the Sky value.
 When there is a gradient over the dataset, different tiles will have different 
constant shifts/Sky-values, for example see Figure 11 of 
@url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]}.
 
-To get less scatter in measuring the mean and median (and thus better estimate 
the skewness), you will need a larger tile.
+To make this very large diffuse/flat signal detectable, you will therefore 
need a larger tile to contain a larger change in the values within it (and 
improve number statistics, for less scatter when measuring the mean and median).
 So let's play with the tessellation a little to see how it affects the result.
 In Gnuastro, you can see the option values (@option{--tilesize} in this case) 
by adding the @option{-P} option to your last command.
 Try running NoiseChisel with @option{-P} to see its default tile size.
@@ -3417,20 +4379,25 @@ To see which tiles were used for estimating the 
quantile threshold (no skewness
 $ astnoisechisel r.fits -h0 --checkqthresh
 @end example
 
-Notice how this option doesn't allow NoiseChisel to finish.
-NoiseChisel aborted after finding and applying the quantile thresholds.
+Did you see how NoiseChisel aborted after finding and applying the quantile 
thresholds?
 When you call any of NoiseChisel's @option{--check*} options, by default, it 
will abort as soon as all the check steps have been written in the check file 
(a multi-extension FITS file).
 This allows you to focus on the problem you wanted to check as soon as 
possible (you can disable this feature with the @option{--continueaftercheck} 
option).
 
-To optimize the threshold-related settings for this image, let's playing with 
this quantile threshold check image a little.
+To optimize the threshold-related settings for this image, let's play with 
this quantile threshold check image a little.
 Don't forget that ``@emph{Good statistical analysis is not a purely routine 
matter, and generally calls for more than one pass through the computer}'' 
(Anscombe 1973, see @ref{Science and its tools}).
 A good scientist must have a good understanding of her tools to make a 
meaningful analysis.
-So don't hesitate in playing with the default configuration and reviewing the 
manual when you have a new dataset in front of you.
+So don't hesitate in playing with the default configuration and reviewing the 
manual when you have a new dataset (from a new instrument) in front of you.
 Robust data analysis is an art, therefore a good scientist must first be a 
good artist.
+So let's open the check image as a multi-extension cube:
 
-The first extension of @file{r_qthresh.fits} (@code{CONVOLVED}) is the 
convolved input image where the threshold(s) is(are) defined and applied.
+@example
+$ ds9 -mecube r_qthresh.fits -zscale -cmap sls -zoom to fit
+@end example
+
+The first extension (called @code{CONVOLVED}) of @file{r_qthresh.fits} is the 
convolved input image where the threshold(s) is(are) defined (and later applied 
to).
 For more on the effect of convolution and thresholding, see Sections 3.1.1 and 
3.1.2 of @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]}.
-The second extension (@code{QTHRESH_ERODE}) has a blank value for all the 
pixels of any tile that was identified as having significant signal.
+The second extension (@code{QTHRESH_ERODE}) has a blank/white value for all 
the pixels of any tile that was identified as having significant signal.
+The other tiles have the measured threshold over them.
 The next two extensions (@code{QTHRESH_NOERODE} and @code{QTHRESH_EXPAND}) are 
the other two quantile thresholds that are necessary in NoiseChisel's later 
steps.
 Every step in this file is repeated on the three thresholds.
 
@@ -3439,12 +4406,21 @@ As one line of attack against discarding too much 
signal below the threshold, No
 Go forward by three extensions to @code{VALUE1_NO_OUTLIER} and you will see 
that many of the tiles over the galaxy have been removed in this step.
 For more on the outlier rejection algorithm, see the latter half of 
@ref{Quantifying signal in a tile}.
 
-However, the default outlier rejection parameters weren't enough, and when you 
play with the color-bar, you still see a strong gradient around the outer tidal 
feature of the galaxy.
-You have two strategies for fixing this problem: 1) Increase the tile size to 
get more accurate measurements of skewness.
+Even though much of the galaxy's footprint has been rejected as outliers, 
there are still tiles with signal remaining:
+play with the DS9 color-bar and you still see a gradient near the outer tidal 
feature of the galaxy.
+Before trying to correct this, let's look at the other extensions of this 
check image.
+We will use a @code{*} as a wild-card that can be 1, 2 or 3.
+In the @code{THRESH*_INTERP} extensions, you see that all the blank tiles have 
been interpolated using their nearest neighbors (the relevant option here is 
@option{--interpnumngb}).
+In the following @code{THRESH*_SMOOTH} extensions, you can see the tile values 
after smoothing (configured with @option{--smoothwidth} option).
+Finally, in @code{QTHRESH-APPLIED}, you see the thresholded image: pixels with 
a value of 1 will be eroded later, but pixels with a value of 2 will pass the 
erosion step un-touched.
+
+Let's get back to the problem of optimizing the result.
+You have two strategies for detecting the outskirts of the merging galaxies:
+1) Increase the tile size to get more accurate measurements of skewness.
 2) Strengthen the outlier rejection parameters to discard more of the tiles 
with signal.
 Fortunately in this image we have a sufficiently large region on the right of 
the image that the galaxy doesn't extend to.
 So we can use the more robust first solution.
-In situations where this doesn't happen (for example if the field of view in 
this image was shifted to have more of M51 and less sky) you are limited to a 
combination of the two solutions or just to the second solution.
+In situations where this doesn't happen (for example if the field of view in 
this image was shifted to the left to have more of M51 and less sky) you are 
limited to a combination of the two solutions or just to the second solution.
 
 @cartouche
 @noindent
@@ -3453,132 +4429,135 @@ Therefore when your dataset is large (unlike the one 
in this test), and you are
 For more on @option{--convolved}, see @ref{NoiseChisel input}.
 @end cartouche
 
-To identify the skewness caused by the flat NGC 5195 and M51 tidal features on 
the tiles under it, we thus have to choose a tile size that is larger than the 
gradient of the signal.
-Let's try a tile size of 75 by 75 pixels:
+To better identify the skewness caused by the flat NGC 5195 and M51 tidal 
features on the tiles under it, we have to choose a larger tile size.
+Let's try a tile size of 100 by 100 pixels and inspect the check image.
 
 @example
-$ astnoisechisel r.fits -h0 --tilesize=75,75 --checkqthresh
+$ astnoisechisel r.fits -h0 --tilesize=100,100 --checkqthresh
+$ ds9 -mecube r_qthresh.fits -zscale -cmap sls -zoom to fit
 @end example
 
-You can clearly see the effect of this increased tile size: the tiles are much 
larger and when you look into @code{VALUE1_NO_OUTLIER}, you see that almost all 
the previous tiles under the galaxy have been discarded and we only have a few 
tiles on the edge with a gradient.
-So let's define a more strict condition to keep tiles:
+You can clearly see the effect of this increased tile size: the tiles are much 
larger and when you look into @code{VALUE1_NO_OUTLIER}, you see that all the 
tiles are nicely grouped on the right side of the image (the farthest from M51, 
where we don't see a gradient in @code{QTHRESH_ERODE}).
+Things look good now, so let's remove @option{--checkqthresh} and let 
NoiseChisel proceed with its detection.
 
 @example
-$ astnoisechisel r.fits -h0 --tilesize=75,75 --meanmedqdiff=0.001 \
-                 --checkqthresh
+$ astnoisechisel r.fits -h0 --tilesize=100,100
+$ ds9 -mecube r_detected.fits -zscale -cmap sls -zoom to fit
 @end example
 
-After constraining @code{--meanmedqdiff}, NoiseChisel stopped with a different 
error.
-Please read it: at the start, it says that only 6 tiles passed the constraint 
while you have asked for 9.
-The @file{r_qthresh.fits} image also only has 8 extensions (not the original 
15).
-Take a look at the initially selected tiles and those after outlier rejection.
-You can see the place of the tiles that passed.
-They seem to be in the good place (very far away from the M51 group and its 
tidal feature.
-Using the 6 nearest neighbors is also not too bad.
-So let's decrease the number of neighboring tiles for interpolation so 
NoiseChisel can continue:
+The detected pixels of the @code{DETECTIONS} extension have expanded a little, 
but not as much.
+Also, the gradient in the @code{SKY} image is almost fully removed (and 
doesn't fall over M51 anymore).
+However, on the bottom-right of the m51 detection, we see many holes gradually 
increasing in size.
+This hints that there is still signal out there.
+Let's check the next series of detection steps by adding the 
@code{--checkdetection} option this time:
 
 @example
-$ astnoisechisel r.fits -h0 --tilesize=75,75 --meanmedqdiff=0.001 \
-                 --interpnumngb=6 --checkqthresh
+$ astnoisechisel r.fits -h0 --tilesize=100,100 --checkdetection
+$ ds9 -mecube r_detcheck.fits -zscale -cmap sls -zoom to fit
 @end example
 
-The next group of extensions (those ending with @code{_INTERP}), give a value 
to all blank tiles based on the nearest tiles with a measurement.
-The following group of extensions (ending with @code{_SMOOTH}) have smoothed 
the interpolated image to avoid sharp cuts on tile edges.
-Inspecting @code{THRESH1_SMOOTH}, you can see that there is no longer any 
significant gradient and no major signature of NGC 5195 exists.
+@cindex Erosion (image processing)
+The output now has 16 extensions, showing every step that is taken by 
NoiseChisel.
+The first and second (@code{INPUT} and @code{CONVOLVED}) are clear from their 
names.
+The third (@code{THRESHOLDED}) is the thresholded image after finding the 
quantile threshold (last extension of the output of @code{--checkqthresh}).
+The fourth HDU (@code{ERODED}) is new: its the name-stake of NoiseChisel, or 
eroding pixels that are above the threshold.
+By erosion, we mean that all pixels with a value of @code{1} (above the 
threshold) that are touching a pixel with a value of @code{0} (below the 
threshold) will be flipped to zero (or ``carved'' out)@footnote{Pixels with a 
value of @code{2} are very high signal-to-noise pixels, they are not eroded, to 
preserve sharp and bright sources.}.
+You can see its effect directly by going back and forth between the 
@code{THRESHOLDED} and @code{ERODED} extensions.
 
-We can now remove @option{--checkqthresh} and let NoiseChisel proceed with its 
detection.
-Also, similar to the argument in @ref{NoiseChisel optimization for detection}, 
in the command above, we set the pseudo-detection signal-to-noise ratio 
quantile (@option{--snquant}) to 0.95.
+@cindex Dilation (image processing)
+In the fifth extension (@code{OPENED-AND-LABELED}) the image is ``opened'', 
which is a name for eroding once, then dilating (dilation is the inverse of 
erosion).
+This is good to remove thin connections that are only due to noise.
+Each separate connected group of pixels is also given its unique label here.
+Do you see how just beyond the large M51 detection, there are many smaller 
detections that get smaller as you go more distant?
+This hints at the solution: the default number of erosions is too much.
+Let's see how many erosions take place by default (by adding @command{-P | 
grep erode} to the previous command)
 
 @example
-$ rm r_qthresh.fits
-$ astnoisechisel r.fits -h0 --tilesize=75,75 --meanmedqdiff=0.001 \
-                 --interpnumngb=6 --snquant=0.95
+$ astnoisechisel r.fits -h0 --tilesize=100,100 -P | grep erode
 @end example
 
-Looking at the @code{DETECTIONS} extension of NoiseChisel's output, we see the 
right-ward edges in particular have many holes that are fully surrounded by 
signal and the signal stretches out in the noise very thinly (the size of the 
holes increases as we go out).
-This suggests that there is still signal that can be detected.
-You can confirm this guess by looking at the @code{SKY} extension to see that 
indeed, there is a clear footprint of the M51 group in the Sky image (which is 
not good!).
-Therefore, we should dig deeper into the noise.
-
-With the @option{--detgrowquant} option, NoiseChisel will use the detections 
as seeds and grow them in to the noise.
-Its value is the ultimate limit of the growth in units of quantile (between 0 
and 1).
-Therefore @option{--detgrowquant=1} means no growth and 
@option{--detgrowquant=0.5} means an ultimate limit of the Sky level (which is 
usually too much!).
-Try running the previous command with various values (from 0.6 to higher 
values) to see this option's effect.
-For this particularly huge galaxy (with signal that extends very gradually 
into the noise), we'll set it to @option{0.65}:
+@noindent
+We see that the value of @code{erode} is @code{2}.
+The default NoiseChisel parameters are primarily targeted to processed images 
(where there is correlated noise due to all the processing that has gone into 
the warping and stacking of raw images, see @ref{NoiseChisel optimization for 
detection}).
+In those scenarios 2 erosions are commonly necessary.
+But here, we have a single-exposure image where there is no correlated noise 
(the pixels aren't mixed).
+So let's see how things change with only one erosion:
 
 @example
-$ astnoisechisel r.fits -h0 --tilesize=75,75 --meanmedqdiff=0.001 \
-                 --interpnumngb=6 --snquant=0.95 --detgrowquant=0.65
+$ astnoisechisel r.fits -h0 --tilesize=100,100 --erode=1 \
+                 --checkdetection
+$ ds9 -mecube r_detcheck.fits -zscale -cmap sls -zoom to fit
 @end example
 
-Beyond this level (smaller @option{--detgrowquant} values), you see the 
smaller background galaxies starting to create thin spider-leg-like features, 
showing that we are following correlated noise for too much.
+Looking at the @code{OPENED-AND-LABELED} extension again, we see that the 
main/large detection is now much larger than before.
+While the immediately-outer connected regions are still present, they have 
decreased dramatically, so we can pass this step.
 
-Now, when you look at the @code{DETECTIONS} extension, you see the wings of 
the galaxy being detected much farther out, But you also see many holes which 
are clearly just caused by noise.
-After growing the objects, NoiseChisel also allows you to fill such holes when 
they are smaller than a certain size through the @option{--detgrowmaxholesize} 
option.
-In this case, a maximum area/size of 10,000 pixels seems to be good:
+After the @code{OPENED-AND-LABELED} extension, NoiseChisel goes onto finding 
false detections using the undetected pixels.
+The process is fully described in Section 3.1.5. (Defining and Removing False 
Detections) of arXiv:@url{https://arxiv.org/pdf/1505.01664.pdf,1505.01664}.
+Please compare the extensions to what you read there and things will be very 
clear.
+In the last HDU (@code{DETECTION-FINAL}), we have the final detected pixels 
that will be used to estimate the Sky and its Standard deviation.
+We see that the main detection has indeed been detected very far out, so let's 
see how the full NoiseChisel will estimate the Sky and its standard deviation 
(by removing @code{--checkdetection}):
 
 @example
-$ astnoisechisel r.fits -h0 --tilesize=75,75 --meanmedqdiff=0.001    \
-                 --interpnumngb=6 --snquant=0.95 --detgrowquant=0.65 \
-                 --detgrowmaxholesize=10000
+$ astnoisechisel r.fits -h0 --tilesize=100,100 --erode=1
+$ ds9 -mecube r_detected.fits -zscale -cmap sls -zoom to fit
 @end example
 
-The detection looks good now, but when you look in to the @code{SKY} 
extension, you still clearly still see a footprint of the galaxy.
-We'll leave it as an exercise for you to play with NoiseChisel further and 
improve the detected pixels.
-
-So, we'll just stop with one last tool NoiseChisel gives you to get a slightly 
better estimation of the Sky: @option{--minskyfrac}.
-On each tile, NoiseChisel will only measure the Sky-level if the fraction of 
undetected pixels is larger than the value given to this option.
-To avoid the edges of the galaxy, we'll set it to @option{0.9}.
-Therefore, tiles that are covered by detected pixels for more than 
@mymath{10\%} of their area are ignored.
+The @code{DETECTIONS} extension of @code{r_detected.fits} closely follows what 
the @code{DETECTION-FINAL} of the check image (looks good!).
+If you go ahead to the @code{SKY} extension, things still look good.
+But it can still be improved.
 
-@example
-$ astnoisechisel r.fits -h0 --tilesize=75,75 --meanmedqdiff=0.001    \
-                 --interpnumngb=6 --snquant=0.95 --detgrowquant=0.65 \
-                 --detgrowmaxholesize=10000 --minskyfrac=0.9
-@end example
+Look at the @code{DETECTIONS} again, you will see the right-ward edges of 
M51's detected pixels have many ``holes'' that are fully surrounded by signal 
(value of @code{1}) and the signal stretches out in the noise very thinly (the 
size of the holes increases as we go out).
+This suggests that there is still undetected signal and that we can still dig 
deeper into the noise.
 
-The footprint of the galaxy still exists in the @code{SKY} extension, but it 
has decreased in significance now.
-Let's calculate the significance of the undetected gradient, in units of noise.
-Since the gradient is roughly along the horizontal axis, we'll collapse the 
image along the second (vertical) FITS dimension to have a 1D array (a table 
column, see its values with the second command).
+With the @option{--detgrowquant} option, NoiseChisel will ``grow'' the 
detections in to the noise.
+Its value is the ultimate limit of the growth in units of quantile (between 0 
and 1).
+Therefore @option{--detgrowquant=1} means no growth and 
@option{--detgrowquant=0.5} means an ultimate limit of the Sky level (which is 
usually too much and will cover the whole image!).
+See Figure 2 of arXiv:@url{https://arxiv.org/pdf/1909.11230.pdf,1909.11230} 
for more on this option.
+Try running the previous command with various values (from 0.6 to higher 
values) to see this option's effect on this dataset.
+For this particularly huge galaxy (with signal that extends very gradually 
into the noise), we'll set it to @option{0.75}:
 
 @example
-$ astarithmetic r_detected.fits 2 collapse-mean -hSKY -ocollapsed.fits
-$ asttable collapsed.fits
+$ astnoisechisel r.fits -h0 --tilesize=100,100 --erode=1 \
+                 --detgrowquant=0.75
+$ ds9 -mecube r_detected.fits -zscale -cmap sls -zoom to fit
 @end example
 
-We can now calculate the minimum and maximum values of this array and define 
their difference (in units of noise) as the gradient:
+Beyond this level (smaller @option{--detgrowquant} values), you see many of 
the smaller background galaxies (towards the right side of the image) starting 
to create thin spider-leg-like features, showing that we are following 
correlated noise for too much.
+Please try it for your self by changing it to @code{0.6} for example.
+
+When you look at the @code{DETECTIONS} extension of the command shown above, 
you see the wings of the galaxy being detected much farther out, But you also 
see many holes which are clearly just caused by noise.
+After growing the objects, NoiseChisel also allows you to fill such holes when 
they are smaller than a certain size through the @option{--detgrowmaxholesize} 
option.
+In this case, a maximum area/size of 10,000 pixels seems to be good:
 
 @example
-$ grad=$(astarithmetic r_detected.fits 2 collapse-mean set-i   \
-                       i maxvalue i minvalue - -hSKY -q)
-$ echo $grad
-$ std=$(aststatistics r_detected.fits -hSKY_STD --mean)
-$ echo $std
-$ astarithmetic -q $grad $std /
+$ astnoisechisel r.fits -h0 --tilesize=100,100 --erode=1 \
+                 --detgrowquant=0.75 --detgrowmaxholesize=10000
+$ ds9 -mecube r_detected.fits -zscale -cmap sls -zoom to fit
 @end example
 
-The undetected gradient (@code{grad} above) is thus roughly a quarter of the 
noise.
-But don't forget that this is per-pixel: individually its small, but it 
extends over millions of pixels, so the total flux may still be relevant.
-
-When looking at the raw input shallow image, you don't see anything so far out 
of the galaxy.
-You might just think that ``this is all noise, I have just dug too deep and 
I'm following systematics''! If you feel like this, have a look at the deep 
images of this system in @url{https://arxiv.org/abs/1501.04599, Watkins et al. 
[2015]}, or a 12 hour deep image of this system (with a 12-inch telescope): 
@url{https://i.redd.it/jfqgpqg0hfk11.jpg}@footnote{The image is taken from this 
Reddit discussion: 
@url{https://www.reddit.com/r/Astronomy/comments/9d6x0q/12_hours_of_exposure_on_the_wh
 [...]
-In these deeper images you see that the outer edges of the M51 group clearly 
follow this exact structure, below in @ref{Achieved surface brightness level}, 
we'll measure the exact level.
+When looking at the raw input image (which is very ``shallow'': less than a 
minute exposure!), you don't see anything so far out of the galaxy.
+You might just think to yourself that ``this is all noise, I have just dug too 
deep and I'm following systematics''! If you feel like this, have a look at the 
deep images of this system in @url{https://arxiv.org/abs/1501.04599, Watkins et 
al. [2015]}, or a 12 hour deep image of this system (with a 12-inch telescope): 
@url{https://i.redd.it/jfqgpqg0hfk11.jpg}@footnote{The image is taken from this 
Reddit discussion: 
@url{https://www.reddit.com/r/Astronomy/comments/9d6x0q/12_hours_of_exposu [...]
+In these deeper images you clearly see how the outer edges of the M51 group 
follow this exact structure, below in @ref{Achieved surface brightness level}, 
we'll measure the exact level.
 
 As the gradient in the @code{SKY} extension shows, and the deep images cited 
above confirm, the galaxy's signal extends even beyond this.
 But this is already far deeper than what most (if not all) other tools can 
detect.
-Therefore, we'll stop configuring NoiseChisel at this point in the tutorial 
and let you play with it a little more while reading more about it in 
@ref{NoiseChisel}.
-
-After finishing this tutorial please go through the NoiseChisel paper and its 
options and play with them to further decrease the gradient.
-This will greatly help you get a good feeling of the options.
-When you do find a better configuration, please send it to us and we'll 
mention your name here with your suggested configuration.
+Therefore, we'll stop configuring NoiseChisel at this point in the tutorial 
and let you play with the other options a little more, while reading more about 
it in the papers (@url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa 
[2015]} and @url{https://arxiv.org/abs/1909.11230, Akhlaghi [2019]}) and 
@ref{NoiseChisel}.
+When you do find a better configuration feel free to contact us for feedback.
 Don't forget that good data analysis is an art, so like a sculptor, master 
your chisel for a good result.
 
+To avoid typing all these options every time you run NoiseChisel on this 
image, you can use Gnuastro's configuration files, see @ref{Configuration 
files}.
+For an applied example of setting/using them, see @ref{Option management and 
configuration files}.
+
 @cartouche
 @noindent
-@strong{This NoiseChisel configuration is NOT GENERIC:} Don't use this 
configuration blindly on another image.
+@strong{This NoiseChisel configuration is NOT GENERIC:} Don't use the 
configuration derived above, on another instrument's image @emph{blindly}.
+If you are unsure, just use the default values.
 As you saw above, the reason we chose this particular configuration for 
NoiseChisel to detect the wings of the M51 group was strongly influenced by the 
noise properties of this particular image.
-So as long as your image noise has similar properties (from the same 
data-reduction step of the same database), you can use this configuration on 
any image.
-For images from other instruments, or higher-level/reduced SDSS products, 
please follow a similar logic to what was presented here and find the best 
configuration yourself.
+Remember @ref{NoiseChisel optimization for detection}, where we looked into 
the very deep XDF image which had strong correlated noise?
+
+As long as your other images have similar noise properties (from the same 
data-reduction step of the same instrument), you can use your configuration on 
any of them.
+But for images from other instruments, please follow a similar logic to what 
was presented in these tutorials to find the optimal configuration.
 @end cartouche
 
 @cartouche
@@ -3605,13 +4584,27 @@ $ astarithmetic $det 2 connected-components 
-olabeled.fits
 @end example
 
 You can find the label of the main galaxy visually (by opening the image and 
hovering your mouse over the M51 group's label).
-But to have a little more fun, lets do this automatically.
-The M51 group detection is by far the largest detection in this image, this 
allows us to find the ID/label that corresponds to it.
-We'll first run MakeCatalog to find the area of all the detections, then we'll 
use AWK to find the ID of the largest object and keep it as a shell variable 
(@code{id}):
+But to have a little more fun, let's do this automatically (which is necessary 
in a general scenario).
+The M51 group detection is by far the largest detection in this image, this 
allows us to find its ID/label easily.
+We'll first run MakeCatalog to find the area of all the labels, then we'll use 
Table to find the ID of the largest object and keep it as a shell variable 
(@code{id}):
 
 @example
-$ astmkcatalog labeled.fits --ids --geoarea -h1 -ocat.txt
-$ id=$(awk '!/^#/@{if($2>max) @{id=$1; max=$2@}@} END@{print id@}' cat.txt)
+# Run MakeCatalog to find the area of each label.
+$ astmkcatalog labeled.fits --ids --geoarea -h1 -ocat.fits
+
+## Sort the table by the area column.
+$ asttable cat.fits --sort=AREA_FULL
+
+## The largest object, is the last one, so we'll use '--tail'.
+$ asttable cat.fits --sort=AREA_FULL --tail=1
+
+## We only want the ID, so let's only ask for that column:
+$ asttable cat.fits --sort=AREA_FULL --tail=1 --column=OBJ_ID
+
+## Now, let's put this result in a variable (instead of printing)
+$ id=$(asttable cat.fits --sort=AREA_FULL --tail=1 --column=OBJ_ID)
+
+## Just to confirm everything is fine.
 $ echo $id
 @end example
 
@@ -3641,10 +4634,10 @@ You'll see that the detected edge of the M51 group is 
now clearly visible.
 You can use @file{edge.fits} to mark (set to blank) this boundary on the input 
image and get a visual feeling of how far it extends:
 
 @example
-$ astarithmetic r.fits edge.fits nan where -ob-masked.fits -h0
+$ astarithmetic r.fits edge.fits nan where -oedge-masked.fits -h0
 @end example
 
-To quantify how deep we have detected the low-surface brightness regions, 
we'll use the command below.
+To quantify how deep we have detected the low-surface brightness regions (in 
units of signal to-noise ratio), we'll use the command below.
 In short it just divides all the non-zero pixels of @file{edge.fits} in the 
Sky subtracted input (first extension of NoiseChisel's output) by the pixel 
standard deviation of the same pixel.
 This will give us a signal-to-noise ratio image.
 The mean value of this image shows the level of surface brightness that we 
have achieved.
@@ -3661,64 +4654,39 @@ Finally, with the @code{meanvalue} operator, we are 
taking the mean value of all
 $ edge="edge.fits -h1"
 $ skystd="r_detected.fits -hSKY_STD"
 $ skysub="r_detected.fits -hINPUT-NO-SKY"
-$ astarithmetic $skysub $skystd / $edge not nan where       \
+$ astarithmetic $skysub $skystd / $edge not nan where \
                 meanvalue --quiet
 @end example
 
 @cindex Surface brightness
-We have thus detected the wings of the M51 group down to roughly 1/4th of the 
noise level in this image! But the signal-to-noise ratio is a relative 
measurement.
-Let's also measure the depth of our detection in absolute surface brightness 
units; or magnitudes per square arcseconds.
-To find out, we'll first need to calculate how many pixels of this image are 
in one arcsecond-squared.
-Fortunately the world coordinate system (or WCS) meta data of Gnuastro's 
output FITS files (in particular the @code{CDELT} keywords) give us this 
information.
-
-@example
-$ pixscale=$(astfits r_detected.fits -h1                           \
-                    | awk '/CDELT1/ @{p=1/($3*3600); print p*p@}')
-$ echo $pixscale
-@end example
-
-@noindent
-Note that we multiplied the value by 3600 so we work in units of arc-seconds 
not degrees.
-Now, let's calculate the average sky-subtracted flux in the border region per 
pixel.
-
-@example
-$ f=$(astarithmetic r_detected.fits edge.fits not nan where set-i \
-                    i sumvalue i numbervalue / -q -hINPUT-NO-SKY)
-$ echo $f
-@end example
-
-@noindent
-We can just multiply the two to get the average flux on this border in one 
arcsecond squared.
-We also have the r-band SDSS zeropoint magnitude@footnote{From 
@url{http://classic.sdss.org/dr7/algorithms/fluxcal.html}} to be 24.80.
-Therefore we can get the surface brightness of the outer edge (in magnitudes 
per arcsecond squared) using the following command.
-Just note that @code{log} in AWK is in base-2 (not 10), and that AWK doesn't 
have a @code{log10} operator.
-So we'll do an extra division by @code{log(10)} to correct for this.
+We have thus detected the wings of the M51 group down to roughly 1/3rd of the 
noise level in this image! But the signal-to-noise ratio is a relative 
measurement.
+Let's also measure the depth of our detection in absolute surface brightness 
units; or magnitudes per square arc-seconds (see @ref{Brightness flux 
magnitude}).
+Fortunately Gnuastro's MakeCatalog does this operation easily.
+SDSS image pixel values are calibrated in units of ``nanomaggy'', so the zero 
point magnitude is 22.5@footnote{From 
@url{https://www.sdss.org/dr12/algorithms/magnitudes}}.
 
 @example
-$ z=24.80
-$ echo "$pixscale $f $z" | awk '@{print -2.5*log($1*$2)/log(10)+$3@}'
---> 28.2989
+astmkcatalog edge.fits -h1 --valuesfile=r_detected.fits \
+             --zeropoint=22.5 --ids --surfacebrightness
+asttable edge_cat.fits
 @end example
 
-On a single-exposure SDSS image, we have reached a surface brightness limit 
fainter than 28 magnitudes per arcseconds squared!
-
+We have thus reached an outer surface brightness of @mymath{25.69} 
magnitudes/arcsec@mymath{^2} (second column in @file{edge_cat.fits}) on this 
single exposure SDSS image!
 In interpreting this value, you should just have in mind that NoiseChisel 
works based on the contiguity of signal in the pixels.
-Therefore the larger the object, the deeper NoiseChisel can carve it out of 
the noise.
-In other words, this reported depth, is only for this particular object and 
dataset, processed with this particular NoiseChisel configuration: if the M51 
group in this image was larger/smaller than this, or if the image was 
larger/smaller, or if we had used a different configuration, we would go 
deeper/shallower.
-
-To avoid typing all these options every time you run NoiseChisel on this 
image, you can use Gnuastro's configuration files, see @ref{Configuration 
files}.
-For an applied example of setting/using them, see @ref{Option management and 
configuration files}.
+Therefore the larger the object (with a similarly diffuse emission), the 
deeper NoiseChisel can carve it out of the noise.
+In other words, this reported depth, is the depth we have reached for this 
object in this dataset, processed with this particular NoiseChisel 
configuration.
+If the M51 group in this image was larger/smaller than this (the field of view 
was smaller/larger), or if the image was from a different instrument, or if we 
had used a different configuration, we would go deeper/shallower.
 
 To continue your analysis of such datasets with extended emission, you can use 
@ref{Segment} to identify all the ``clumps'' over the diffuse regions: 
background galaxies and foreground stars.
 
 @example
-$ astsegment r_detected.fits
+$ astsegment r_detected.fits --output=r_segmented.fits
+$ ds9 -mecube r_segmented.fits -zscale -cmap sls -zoom to fit
 @end example
 
 @cindex DS9
 @cindex SAO DS9
-Open the output @file{r_detected_segmented.fits} as a multi-extension data 
cube like before and flip through the first and second extensions to see the 
detected clumps (all pixels with a value larger than 1).
-To optimize the parameters and make sure you have detected what you wanted, 
its highly recommended to visually inspect the detected clumps on the input 
image.
+Open the output @file{r_segmented.fits} as a multi-extension data cube like 
before and flip through the first and second extensions to see the detected 
clumps (all pixels with a value larger than 1).
+To optimize the parameters and make sure you have detected what you wanted, we 
recommend to visually inspect the detected clumps on the input image.
 
 For visual inspection, you can make a simple shell script like below.
 It will first call MakeCatalog to estimate the positions of the clumps, then 
make an SAO ds9 region file and open ds9 with the image and region file.
@@ -3779,7 +4747,7 @@ But more importantly, depending on the dataset's world 
coordinate system, you ha
 Otherwise the circle regions can be too small/large.} output of Segment (when 
@option{--rawoutput} is @emph{not} used) with a command like this:
 
 @example
-$ ./check-clumps.sh r_detected_segmented -0.1 2
+$ ./check-clumps.sh r_segmented -0.1 2
 @end example
 
 Go ahead and run this command.
@@ -3808,8 +4776,8 @@ You can do this using Arithmetic in a command like below.
 For easy reading of the command, we'll define the shell variable @code{i} for 
the image name and save the output in @file{masked.fits}.
 
 @example
-$ in="r_detected_segmented.fits -hINPUT"
-$ clumps="r_detected_segmented.fits -hCLUMPS"
+$ in="r_segmented.fits -hINPUT"
+$ clumps="r_segmented.fits -hCLUMPS"
 $ astarithmetic $in $clumps 0 gt nan where -oclumps-masked.fits
 @end example
 
@@ -4022,14 +4990,19 @@ $ sudo make install
 @node Optional dependencies, Bootstrapping dependencies, Mandatory 
dependencies, Dependencies
 @subsection Optional dependencies
 
-The libraries listed here are only used for very specific applications, 
therefore if you don't want these operations, Gnuastro will be built and 
installed without them and you don't have to have the dependencies.
+The libraries listed here are only used for very specific applications, 
therefore they are optional and Gnuastro can be built without them (with only 
those specific features disabled).
+Since these are pretty low-level tools, they are not too hard to install from 
source, but you can also use your operating system's package manager to easily 
install all of them.
+For more, see @ref{Dependencies from package managers}.
 
 @cindex GPL Ghostscript
-If the @command{./configure} script can't find these requirements, it will 
warn you in the end that they are not present and notify you of the 
operation(s) you can't do due to not having them.
-If the output you request from a program requires a missing library, that 
program is going to warn you and abort.
-In the case of program dependencies (like GPL GhostScript), if you install 
them at a later time, the program will run.
-This is because if required libraries are not present at build time, the 
executables cannot be built, but an executable is called by the built program 
at run time so if it becomes available, it will be used.
-If you do install an optional library later, you will have to rebuild Gnuastro 
and reinstall it for it to take effect.
+If the @command{./configure} script can't find any of these optional 
dependencies, it will notify you of the operation(s) you can't do due to not 
having them.
+If you continue the build and request an operation that uses a missing 
library, Gnuastro's programs will warn that the optional library was missing at 
build-time and abort.
+Since Gnuastro was built without that library, installing the library 
afterwards won't help.
+The only way is to re-build Gnuastro from scratch (after the library has been 
installed).
+However, for program dependencies (like cURL or GhostScript) things are 
easier: you can install them after building Gnuastro also.
+This is because libraries are used to build the internal structure of 
Gnuastro's executables.
+However, a program dependency is called by Gnuastro's programs at run-time and 
has no effect on their internal structure.
+So if a dependency program becomes available later, it will be used next time 
it is requested.
 
 @table @asis
 
@@ -4043,7 +5016,7 @@ See @ref{Installation directory} for more on @code{PATH}.
 
 GNU Libtool (the binary/executable file) is a low-level program that is 
probably already present on your system, and if not, is available in your 
operating system package manager@footnote{Note that we want the 
binary/executable Libtool program which can be run on the command-line.
 In Debian-based operating systems which separate various parts of a package, 
you want want @code{libtool-bin}, the @code{libtool} package won't contain the 
executable program.}.
-If you want to install GNU Libtool's latest version from source, please visit 
its @url{https://www.gnu.org/software/libtool/, webpage}.
+If you want to install GNU Libtool's latest version from source, please visit 
its @url{https://www.gnu.org/software/libtool/, web page}.
 
 Gnuastro's tarball is shipped with an internal implementation of GNU Libtool.
 Even if you have GNU Libtool, Gnuastro's internal implementation is used for 
the building and installation of Gnuastro.
@@ -4076,14 +5049,16 @@ libtiff is used by ConvertType and the libraries to 
read TIFF images, see @ref{R
 @url{http://www.simplesystems.org/libtiff/, libtiff} is a very basic library 
that provides tools to read and write TIFF images, most Unix-like operating 
system graphic programs and libraries use it.
 Therefore even if you don't have it installed, it must be easily available in 
your package manager.
 
+@item cURL
+@cindex cURL (downloading tool)
+cURL's executable (@command{curl}) is called by @ref{Query} for submitting 
queries to remote datasets and retrieving the results.
+It isn't necessary for the build of Gnuastro from source (only a warning will 
be printed if it can't be found at configure time), so if you don't have it at 
build-time there is no problem.
+Just be sure to have it when you run @command{astquery}, otherwise you'll get 
an error about not finding @command{curl}.
 
 @item GPL Ghostscript
 @cindex GPL Ghostscript
 GPL Ghostscript's executable (@command{gs}) is called by ConvertType to 
compile a PDF file from a source PostScript file, see @ref{ConvertType}.
 Therefore its headers (and libraries) are not needed.
-With a very high probability you already have it in your GNU/Linux 
distribution.
-Unfortunately it does not follow the standard GNU build style so installing it 
is very hard.
-It is best to rely on your distribution's package managers for this.
 
 @end table
 
@@ -4166,7 +5141,7 @@ So the @file{./boostrap} script will run @LaTeX{} to 
build the figures.
 The best way to install @LaTeX{} and all the necessary packages is through 
@url{https://www.tug.org/texlive/, @TeX{} live} which is a package manager for 
@TeX{} related tools that is independent of any operating system.
 It is thus preferred to the @TeX{} Live versions distributed by your operating 
system.
 
-To install @TeX{} Live, go to the webpage and download the appropriate 
installer by following the ``download'' link.
+To install @TeX{} Live, go to the web page and download the appropriate 
installer by following the ``download'' link.
 Note that by default the full package repository will be downloaded and 
installed (around 4 Giga Bytes) which can take @emph{very} long to download and 
to update later.
 However, most packages are not needed by everyone, it is easier, faster and 
better to install only the ``Basic scheme'' (consisting of only the most basic 
@TeX{} and @LaTeX{} packages, which is less than 200 Mega bytes)@footnote{You 
can also download the DVD iso file at a later time to keep as a backup for when 
you don't have internet connection if you need a package.}.
 
@@ -4179,7 +5154,7 @@ To install all the necessary @TeX{} packages for a 
successful Gnuastro bootstrap
 $ su
 # tlmgr install epsf jknapltx caption biblatex biber iftex \
                 etoolbox logreq xstring xkeyval pgf ms     \
-                xcolor pgfplots times rsfs pstools epspdf
+                xcolor pgfplots times rsfs ps2eps epspdf
 @end example
 
 @item ImageMagick (@command{imagemagick})
@@ -4209,8 +5184,11 @@ Here are some basic reasons behind this recommendation.
 @enumerate
 
 @item
-Your distribution's pre-built package might not be the most recent
-release.
+Your operating system's pre-built software might not be the most recent 
release.
+For example, Gnuastro itself is also packaged in some package managers.
+For the list see: @url{https://repology.org/project/gnuastro/versions}.
+You will notice that Gnuastro's version in some operating systems is more than 
10 versions old!
+It is the same for all the dependencies of Gnuastro.
 
 @item
 For each package, Gnuastro might preform better (or require) certain 
configuration options that your distribution's package managers didn't add for 
you.
@@ -4227,7 +5205,6 @@ By reading their manuals, installing them and staying up 
to date with changes/bu
 
 Based on your package manager, you can use any of the following commands to 
install the mandatory and optional dependencies.
 If your package manager isn't included in the list below, please send us the 
respective command, so we add it.
-Gnuastro itself if also already packaged in some package managers (for example 
Debian or Homebrew).
 
 As discussed above, we recommend installing the @emph{mandatory} dependencies 
manually from source (see @ref{Mandatory dependencies}).
 Therefore, in each command below, first the optional dependencies are given.
@@ -4254,7 +5231,7 @@ This arguably makes Debian-based OSs the largest, and 
most used, class of GNU/Li
 All of them use Debian's Advanced Packaging Tool (APT, for example 
@command{apt-get}) for managing packages.
 @example
 $ sudo apt-get install ghostscript libtool-bin libjpeg-dev  \
-                       libtiff-dev libgit2-dev lzip         \
+                       libtiff-dev libgit2-dev curl lzip    \
                                                             \
                        libgsl0-dev libcfitsio-dev wcslib-dev
 @end example
@@ -4278,9 +5255,9 @@ But since it is free software, many other teams use its 
code to spin-off their o
 Red Hat-based GNU/Linux distributions initially used the ``Yellowdog Updated, 
Modifier'' (YUM) package manager, which has been replaced by ``Dandified yum'' 
(DNF).
 If the latter isn't available on your system, you can use @command{yum} 
instead of @command{dnf} in the command below.
 @example
-$ sudo dnf install ghostscript libtool libjpeg-devel        \
-                   libtiff-devel libgit2-devel lzip         \
-                                                            \
+$ sudo dnf install ghostscript libtool libjpeg-devel     \
+                   libtiff-devel libgit2-devel lzip curl \
+                                                         \
                    gsl-devel cfitsio-devel wcslib-devel
 @end example
 
@@ -4298,9 +5275,9 @@ If not already installed, first obtain Homebrew by 
following the instructions at
 Homebrew manages packages in different `taps'.
 To install WCSLIB (discussed in @ref{Mandatory dependencies}) via Homebrew you 
will need to @command{tap} into @command{brewsci/science} first (the tap may 
change in the future, but can be found by calling @command{brew search wcslib}).
 @example
-$ brew install ghostscript libtool libjpeg libtiff          \
-               libgit2 lzip                                 \
-                                                            \
+$ brew install ghostscript libtool libjpeg libtiff \
+               libgit2 curl lzip                   \
+                                                   \
                gsl cfitsio
 $ brew tap brewsci/science
 $ brew install wcslib
@@ -4313,26 +5290,27 @@ $ brew install wcslib
 It ``focuses on elegance, code correctness, minimalism and simplicity, and 
expects the user to be willing to make some effort to understand the system's 
operation''.
 Arch Linux uses ``Package manager'' (Pacman) to manage its packages/components.
 @example
-$ sudo pacman -S ghostscript libtool libjpeg libtiff        \
-                 libgit2 lzip                               \
-                                                            \
+$ sudo pacman -S ghostscript libtool libjpeg libtiff \
+                 libgit2 curl lzip                   \
+                                                     \
                  gsl cfitsio wcslib
 @end example
 
 @item @command{zypper} (openSUSE and SUSE Linux Enterprise Server)
 @cindex openSUSE
 @cindex SUSE Linux Enterprise Server
-@cindex @command{zypper} @url{https://www.opensuse.org,openSUSE} is a 
community project supported by @url{https://www.suse.com,SUSE} with both stable 
and rolling releases.
+@cindex @command{zypper}, OpenSUSE package manager
 SUSE Linux Enterprise 
Server@footnote{@url{https://www.suse.com/products/server}} (SLES) is the 
commercial offering which shares code and tools.
 Many additional packages are offered in the Build 
Service@footnote{@url{https://build.opensuse.org}}.
 openSUSE and SLES use @command{zypper} (cli) and YaST (GUI) for managing 
repositories and
 packages.
 
 @example
-$ sudo zypper install ghostscript_any libtool pkgconfig    \
-              cfitsio-devel gsl-devel libcurl-devel        \
-              libgit2-devel libjpeg62-devel libtiff-devel  \
-              wcslib-devel
+$ sudo zypper install ghostscript_any libtool pkgconfig   \
+              libcurl-devel libgit2-devel libjpeg62-devel \
+              libtiff-devel curl                          \
+                                                          \
+              gsl-devel cfitsio-devel wcslib-devel
 @end example
 @noindent
 When building Gnuastro, run the configure script with the following 
@code{CPPFLAGS} environment variable:
@@ -4435,7 +5413,7 @@ Gnuastro's official/stable tarball is released with two 
formats: Gzip (with suff
 The pre-release tarballs (after version 0.3) are released only as an Lzip 
tarball.
 Gzip is a very well-known and widely used compression program created by GNU 
and available in most systems.
 However, Lzip provides a better compression ratio and more robust archival 
capacity.
-For example Gnuastro 0.3's tarball was 2.9MB and 4.3MB with Lzip and Gzip 
respectively, see the @url{http://www.nongnu.org/lzip/lzip.html, Lzip webpage} 
for more.
+For example Gnuastro 0.3's tarball was 2.9MB and 4.3MB with Lzip and Gzip 
respectively, see the @url{http://www.nongnu.org/lzip/lzip.html, Lzip web page} 
for more.
 Lzip might not be pre-installed in your operating system, if so, installing it 
from your operating system's package manager or from source is very easy and 
fast (it is a very small program).
 
 The GNU FTP server is mirrored (has backups) in various locations on the globe 
(@url{http://www.gnu.org/order/ftp.html}).
@@ -4638,7 +5616,7 @@ If you want to make changes in the code, have a look at 
@ref{Developing} to get
 Be sure to commit your changes in a separate branch (keep your @code{master} 
branch to follow the official repository) and re-run @command{autoreconf -f} 
after the commit.
 If you intend to send your work to us, you can safely use your commit since it 
will be ultimately recorded in Gnuastro's official history.
 If not, please upload your separate branch to a public hosting service, for 
example @url{https://gitlab.com, GitLab}, and link to it in your report/paper.
-Alternatively, run @command{make distcheck} and upload the output 
@file{gnuastro-X.X.X.XXXX.tar.gz} to a publicly accessible webpage so your 
results can be considered scientific (reproducible) later.
+Alternatively, run @command{make distcheck} and upload the output 
@file{gnuastro-X.X.X.XXXX.tar.gz} to a publicly accessible web page so your 
results can be considered scientific (reproducible) later.
 
 
 
@@ -5541,6 +6519,7 @@ When the output is a FITS file, all the programs also 
store some very useful inf
 * Installed scripts::           Installed Bash scripts, not compiled programs.
 * Multi-threaded operations::   How threads are managed in Gnuastro.
 * Numeric data types::          Different types and how to specify them.
+* Memory management::           How memory is allocated (in RAM or HDD/SSD).
 * Tables::                      Recognized table formats.
 * Tessellation::                Tile the dataset into non-overlapping bins.
 * Automatic output::            About automatic output names.
@@ -5706,8 +6685,13 @@ There are generally two types, depending on the context.
 If they are for fractions, they will have to be less than or equal to unity.
 
 @item STR
-The value is read as a string of characters (for example a file name)
-or other particular settings like a HDU name, see below.
+The value is read as a string of characters.
+For example column names in a table, or HDU names in a multi-extension FITS 
file.
+Other examples include human-readable settings by some programs like the 
@option{--domain} option of the Convolve program that can be either 
@code{spatial} or @code{frequency} (to specify the type of convolution, see 
@ref{Convolve}).
+
+@item FITS @r{or} FITS/TXT
+The value should be a file (most commonly FITS).
+In many cases, other formats may also be accepted (for example input tables 
can be FITS or plain-text, see @ref{Recognized table formats}).
 
 @end vtable
 
@@ -5899,30 +6883,16 @@ Also, if they are irrelevant for a program, these 
options will not display in th
 @table @option
 
 @item --minmapsize=INT
-The minimum size (in bytes) to store the contents of each main processing 
array of a program as a file (on the non-volatile HDD/SSD), not in RAM.
-This can be very useful when you have limited RAM, but need to process large 
datasets which can be very memory intensive.
-In such scenarios, without this option, the program will crash.
-
-A random filename is assigned to the array.
-This file will keep the contents of the array as long as it is necessary and 
the program will delete it as soon as its not necessary any more.
-
-If the @file{.gnuastro} directory exists and is writable, then the random file 
will be placed in there.
-Otherwise, the @file{.gnuastro_mmap} directory will be checked.
-If @file{.gnuastro_mmap} does not exist, or @file{.gnuastro} is not writable, 
the random file will be directly written in the current directory with the 
@file{.gnuastro_mmap_} prefix.
-
-By default, the name of the created file, and its size (in bytes) is printed 
by the program when it is created and later, when its deleted/freed.
-These messages are useful to the user who has enough RAM, but has forgot to 
increase the value to @code{--minmapsize} (this is often the case).
-To suppress/disable such messages, use the @code{--quietmmap} option.
-
-When this option has a value of @code{0} (zero, strongly discouraged, see box 
below), all arrays that use this feature in a program will actually be placed 
in a file (not in RAM).
-When this option is larger than all the input datasets, all arrays will be 
definitely allocated in RAM and the program will run MUCH faster.
+The minimum size (in bytes) to memory-map a processing/internal array as a 
file (on the non-volatile HDD/SSD), and not use the system's RAM.
+Before using this option, please read @ref{Memory management}.
+By default processing arrays will only be memory-mapped to a file when the RAM 
is full.
+With this option, you can force the memory-mapping, even when there is enough 
RAM.
+To ensure this default behavior, the pre-defined value to this option is an 
extremely large value (larger than any existing RAM).
 
 Please note that using a non-volatile file (in the HDD/SDD) instead of RAM can 
significantly increase the program's running time, especially on HDDs (where 
read/write is slower).
-So it is best to give this option large values by default.
+Also, note that the number of memory-mapped files that your kernel can support 
is limited.
+So when this option is necessary, it is best to give it values larger than 1 
megabyte (@option{--minmapsize=1000000}).
 You can then decrease it for a specific program's invocation on a large input 
after you see memory issues arise (for example an error, or the program not 
aborting and fully consuming your memory).
-
-The random file will be deleted once it is no longer needed by the program.
-The @file{.gnuastro} directory will also be deleted if it has no other 
contents (you may also have configuration files in this directory, see 
@ref{Configuration files}).
 If you see randomly named files remaining in this directory when the program 
finishes normally, please send us a bug report so we address the problem, see 
@ref{Report a bug}.
 
 @cartouche
@@ -6451,7 +7421,7 @@ The prefix of @file{/usr/local/} is conventionally used 
for programs you install
 Probably the first time you read this book, it is either in the PDF or HTML 
formats.
 These two formats are very convenient for when you are not actually working, 
but when you are only reading.
 Later on, when you start to use the programs and you are deep in the middle of 
your work, some of the details will inevitably be forgotten.
-Going to find the PDF file (printed or digital) or the HTML webpage is a major 
distraction.
+Going to find the PDF file (printed or digital) or the HTML web page is a 
major distraction.
 
 @cindex Online help
 @cindex Command-line help
@@ -6464,7 +7434,7 @@ With this type of help, you can resume your exciting 
research without taking you
 Another major advantage of such command-line based help routines is that they 
are installed with the software in your computer, therefore they are always in 
sync with the executable you are actually running.
 Three of them are actually part of the executable.
 You don't have to worry about the version of the book or program.
-If you rely on external help (a PDF in your personal print or digital archive 
or HTML from the official webpage) you have to check to see if their versions 
fit with your installed program.
+If you rely on external help (a PDF in your personal print or digital archive 
or HTML from the official web page) you have to check to see if their versions 
fit with your installed program.
 
 If you only need to remember the short or long names of the options, 
@option{--usage} is advised.
 If it is what the options do, then @option{--help} is a great tool.
@@ -6903,7 +7873,7 @@ This allows you to be much more productive in easily 
checking various ideas/assu
 
 
 
-@node Numeric data types, Tables, Multi-threaded operations, Common program 
behavior
+@node Numeric data types, Memory management, Multi-threaded operations, Common 
program behavior
 @section Numeric data types
 
 @cindex Bit
@@ -7017,7 +7987,139 @@ If you are writing your own program, you can use the 
@code{gal_data_copy_to_new_
 
 
 
-@node Tables, Tessellation, Numeric data types, Common program behavior
+@node Memory management, Tables, Numeric data types, Common program behavior
+@section Memory management
+
+@cindex Memory management
+@cindex Non-volatile memory
+@cindex Memory, non-volatile
+In this section we'll review how Gnuastro manages your input data in your 
system's memory.
+Knowing this can help you optimize your usage (in speed and memory 
consumption) when the data volume is large and approaches, or exceeds, your 
available RAM (usually in various calls to multiple programs simultaneously).
+But before diving into the details, let's have a short basic introduction to 
memory in general and in particular the types of memory most relevant to this 
discussion.
+
+Input datasets (that are later fed into programs for analysis) are commonly 
first stored in @emph{non-volatile memory}.
+This is a type of memory that doesn't need a constant power supply to keep the 
data and is therefore primarily aimed for long-term storage, like HDDs or SSDs.
+So data in this type of storage is preserved when you turn off your computer.
+But by its nature, non-volatile memory is much slower, in reading or writing, 
than the speeds that CPUs can process the data.
+Thus relying on this type of memory alone would create a bad bottleneck in the 
input/output (I/O) phase of any processing.
+
+@cindex RAM
+@cindex Volatile memory
+@cindex Memory, volatile
+The first step to decrease this bottleneck is to have a faster storage space, 
but with a much limited storage volume.
+For this type of storage, computers have a Random Access Memory (or RAM).
+RAM is classified as a @emph{volatile memory} because it needs a constant flow 
of electricity to keep the information.
+In other words, the moment power is cut-off, all the stored information in 
your RAM is gone (hence the ``volatile'' name).
+But thanks to that constant supply of power, it can access any random address 
with equal (and very high!) speed.
+
+Hence, the general/simplistic way that programs deal with memory is the 
following (this is general to almost all programs, not just Gnuastro's):
+1) Load/copy the input data from the non-volatile memory into RAM.
+2) Use the copy of the data in RAM as input for all the internal processing as 
well as the intermediate data that is necessary during the processing.
+3) Finally, when the analysis is complete, write the final output data back 
into non-volatile memory, and free/delete all the used space in the RAM (the 
initial copy and all the intermediate data).
+Usually the RAM is most important for the data of the intermediate steps (that 
you never see as a user of a program!).
+
+When the input dataset(s) to a program are small (compared to the available 
space in your system's RAM at the moment it is run) Gnuastro's programs and 
libraries follow the standard series of steps above.
+The only exception is that deleting the intermediate data is not only done at 
the end of the program.
+As soon as an intermediate dataset is no longer necessary for the next 
internal steps, the space it occupied is deleted/freed.
+This allows Gnuastro programs to minimize their usage of your system's RAM 
over the full running time.
+
+The situation gets complicated when the datasets are large (compared to your 
available RAM when the program is run).
+For example if a dataset is half the size of your system's available RAM, and 
the program's internal analysis needs three or more intermediately processed 
copies of it at one moment in its analysis.
+There won't be enough RAM to keep those higher-level intermediate data.
+In such cases, programs that don't do any memory management will crash.
+But fortunately Gnuastro's programs do have a memory management plans for such 
situations.
+
+@cindex Memory-mapped file
+When the necessary amount of space for an intermediate dataset cannot be 
allocated in the RAM, Gnuastro's programs will not use the RAM at all.
+They will use the ``memory-mapped file'' concept in modern operating systems 
to create a randomly-named file in your non-volatile memory and use that 
instead of the RAM.
+That file will have the exact size (in bytes) of that intermediate dataset.
+Any time the program needs that intermediate dataset, the operating system 
will directly go to that file, and bypass your RAM.
+As soon as that file is no longer necessary for the analysis, it will be 
deleted.
+But as mentioned above, non-volatile memory has much slower I/O speed than the 
RAM.
+Hence in such situations, the programs will become noticeably slower 
(sometimes by factors of 10 times slower, depending on your non-volatile memory 
speed).
+
+Because of the drop in I/O speed (and thus the speed of your running program), 
the moment that any to-be-allocated dataset is memory-mapped, Gnuastro's 
programs and libraries will notify you with a descriptive statement like below 
(can happen in any phase of their analysis).
+It shows the location of the memory-mapped file, its size, complemented with a 
small description of the cause, a pointer to this section of the book for more 
information on how to deal with it (if necessary), and what to do to suppress 
it.
+
+@example
+astarithmetic: ./gnuastro_mmap/Fu7Dhs: temporary memory-mapped file
+(XXXXXXXXXXX bytes) created for intermediate data that is not stored
+in RAM (see the "Memory management" section of Gnuastro's manual for
+optimizing your project's memory management, and thus speed). To
+disable this warning, please use the option '--quiet-mmap'
+@end example
+
+@noindent
+Finally, when the intermediate dataset is no longer necessary, the program 
will automatically delete it and notify you with a statement like this:
+
+@example
+astarithmetic: ./gnuastro_mmap/Fu7Dhs: deleted
+@end example
+
+@noindent
+To disable these messages, you can run the program with @code{--quietmmap}, or 
set the @code{quietmmap} variable in the allocating library function to be 
non-zero.
+
+An important component of these messages is the name of the memory-mapped file.
+Knowing that the file has been deleted is important for the user if the 
program crashes for any reason: internally (for example a parameter is given 
wrongly) or externally (for example you mistakenly kill the running job).
+In the event of a crash, the memory-mapped files will not be deleted and you 
have to manually delete them because they are usually large and they may soon 
fill your full storage if not deleted in a long time due to successive crashes.
+
+This brings us to managing the memory-mapped files in your non-volatile memory.
+In other words: knowing where they are saved, or intentionally placing them in 
different places of your file system, or deleting them when necessary.
+As the examples above show, memory-mapped files are stored in a sub-directory 
of the the running directory called @file{gnuastro_mmap}.
+If this directory doesn't exist, Gnuastro will automatically create it when 
memory mapping becomes necessary.
+Alternatively, it may happen that the @file{gnuastro_mmap} sub-directory 
exists and isn't writable, or it can't be created.
+In such cases, the memory-mapped file for each dataset will be created in the 
running directory with a @file{gnuastro_mmap_} prefix.
+
+Therefore one easy way to delete all memory-mapped files in case of a crash, 
is to delete everything within the sub-directory (first command below), or all 
files stating with this prefix:
+
+@example
+rm -f gnuastro_mmap/*
+rm -f gnuastro_mmap_*
+@end example
+
+A much more common issue when dealing with memory-mapped files is their 
location.
+For example, you may be running a program in a partition that is hosted by an 
HDD.
+But you also have another partition on an SSD (which has much faster I/O).
+So you want your memory-mapped files to be created in the SSD to speed up your 
processing.
+In this scenario, you want your project source directory to only contain your 
plain-text scripts and you want your project's built products (even the 
temporary memory-mapped files) to be built in a different location because they 
are large; thus I/O speed becomes important.
+
+To host the memory-mapped files in another location (with fast I/O), you can 
set (@file{gnuastro_mmap}) to be a symbolic link to it.
+For example, let's assume you want your memory-mapped files to be stored in 
@file{/path/to/dir/for/mmap}.
+All you have to do is to run the following command before your Gnuastro 
analysis command(s).
+
+@example
+ln -s /path/to/dir/for/mmap gnuastro_mmap
+@end example
+
+The programs will delete a memory-mapped file when it is no longer needed, but 
they won't delete the @file{gnuastro_mmap} directory that hosts them.
+So if your project involves many Gnuastro programs (possibly called in 
parallel) and you want your memory-mapped files to be in a different location, 
you just have to make the symbolic link above once at the start, and all the 
programs will use it if necessary.
+
+Another memory-management scenario that may happen is this: you don't want a 
Gnuastro program to allocate internal datasets in the RAM at all.
+For example the speed of your Gnuastro-related project doesn't matter at that 
moment, and you have higher-priority jobs that are being run at the same time 
which need to have RAM available.
+In such cases, you can use the @option{--minmapsize} option that is available 
in all Gnuastro programs (see @ref{Processing options}).
+Any intermediate dataset that has a size larger than the value of this option 
will be memory-mapped, even if there is space available in your RAM.
+For example if you want any dataset larger than 100 megabytes to be 
memory-mapped, use @option{--minmapsize=100000000} (8 zeros!).
+
+@cindex Linux kernel
+@cindex Kernel, Linux
+You shouldn't set the value of @option{--minmapsize} to be too small, 
otherwise even small intermediate values (that are usually very numerous) in 
the program will be memory-mapped.
+However the kernel can only host a limited number of memory-mapped files at 
every moment (by all running programs combined).
+For example in the default@footnote{If you need to host more memory-mapped 
files at one moment, you need to build your own customized Linux kernel.} Linux 
kernel on GNU/Linux operating systems this limit is roughly 64000.
+If the total number of memory-mapped files exceeds this number, all the 
programs using them will crash.
+Gnuastro's programs will warn you if your given value is too small and may 
cause a problem later.
+
+Actually, the default behavior for Gnuastro's programs (to only use 
memory-mapped files when there isn't enough RAM) is a side-effect of 
@option{--minmapsize}.
+The pre-defined value to this option is an extremely large value in the 
lowest-level Gnuastro configuration file (the installed @file{gnuastro.conf} 
described in @ref{Configuration file precedence}).
+This value is larger than the largest possible available RAM.
+You can check by running any Gnuastro program with a @option{-P} option.
+Because no dataset will be larger than this, by default the programs will 
first attempt to use the RAM for temporary storage.
+But if writing in the RAM fails (for any reason, mainly due to lack of 
available space), then a memory-mapped file will be created.
+
+
+
+
+
+@node Tables, Tessellation, Memory management, Common program behavior
 @section Tables
 
 ``A table is a collection of related data held in a structured format within a 
database.
@@ -7165,8 +8267,12 @@ A column information comment is assumed to have the 
following format:
 Any sequence of characters between `@key{:}' and `@key{[}' will be interpreted 
as the column name (so it can contain anything except the `@key{[}' character).
 Anything between the `@key{]}' and the end of the line is defined as a comment.
 Within the brackets, anything before the first `@key{,}' is the units 
(physical units, for example km/s, or erg/s), anything before the second 
`@key{,}' is the short type identifier (see below, and @ref{Numeric data 
types}).
+
 Finally (still within the brackets), any non-white characters after the second 
`@key{,}' are interpreted as the blank value for that column (see @ref{Blank 
pixels}).
-Note that blank values will be stored in the same type as the column, not as a 
string@footnote{For floating point types, the @code{nan}, or @code{inf} strings 
(both not case-sensitive) refer to IEEE NaN (not a number) and infinity values 
respectively and will be stored as a floating point, so they are acceptable.}.
+The blank value can either be in the same type as the column (for example 
@code{-99} for a signed integer column), or any string (for example @code{NaN} 
in that same column).
+In both cases, the values will be stored in memory as Gnuastro's fixed blank 
values for each type.
+For floating point types, Gnuastro's internal blank value is IEEE NaN 
(Not-a-Number).
+For signed integers, it is the smallest possible value and for unsigned 
integers its the largest possible value.
 
 When a formatting problem occurs (for example you have specified the wrong 
type code, see below), or the column was already given meta-data in a previous 
comment, or the column number is larger than the actual number of columns in 
the table (the non-commented or empty lines), then the comment information line 
will be ignored.
 
@@ -7239,7 +8345,7 @@ Conversely if integer types are important for you, you 
have to manually set them
 At the lowest level, the only defining aspect of a column in a table is its 
number, or position.
 But selecting columns purely by number is not very convenient and, especially 
when the tables are large it can be very frustrating and prone to errors.
 Hence, table file formats (for example see @ref{Recognized table formats}) 
have ways to store additional information about the columns (meta-data).
-Some of the most common pieces of information about each column are its 
@emph{name}, the @emph{units} of data in the it, and a @emph{comment} for 
longer/informal description of the column's data.
+Some of the most common pieces of information about each column are its 
@emph{name}, the @emph{units} of data in it, and a @emph{comment} for 
longer/informal description of the column's data.
 
 To facilitate research with Gnuastro, you can select columns by matching, or 
searching in these three fields, besides the low-level column number.
 To view the full list of information on the columns in the table, you can use 
the Table program (see @ref{Table}) with the command below (replace 
@file{table-file} with the filename of your table, if its FITS, you might also 
need to specify the HDU/extension which contains the table):
@@ -7264,7 +8370,7 @@ The matching will be done following this convention:
 @itemize
 @item
 If the value is enclosed in two slashes (for example @command{-x/RA_/}, or 
@option{--coordcol=/RA_/}, see @ref{Crop options}), then it is assumed to be a 
regular expression with the same convention as GNU AWK.
-GNU AWK has a very well written 
@url{https://www.gnu.org/software/gawk/manual/html_node/Regexp.html, chapter} 
describing regular expressions, so we we will not continue discussing them here.
+GNU AWK has a very well written 
@url{https://www.gnu.org/software/gawk/manual/html_node/Regexp.html, chapter} 
describing regular expressions, so we will not continue discussing them here.
 Regular expressions are a very powerful tool in matching text and useful in 
many contexts.
 We thus strongly encourage reviewing this chapter for greatly improving the 
quality of your work in many cases, not just for searching column meta-data in 
Gnuastro.
 
@@ -7530,16 +8636,16 @@ It can be used to select certain table columns in a 
FITS table and see them as a
 
 @menu
 * Fits::                        View and manipulate extensions and keywords.
-* Sort FITS files by night::    Installed script to sort FITS files by obs 
night.
 * ConvertType::                 Convert data to various formats.
 * Table::                       Read and Write FITS tables to plain text.
+* Query::                       Import data from external databases.
 @end menu
 
 
 
 
 
-@node Fits, Sort FITS files by night, Data containers, Data containers
+@node Fits, ConvertType, Data containers, Data containers
 @section Fits
 
 @cindex Vatican library
@@ -7550,7 +8656,7 @@ In the last few decades it has proved so useful and 
robust that the Vatican Libr
 @cindex IAU, international astronomical union
 Although the full name of the standard invokes the idea that it is only for 
images, it also contains complete and robust features for tables.
 It started off in the 1970s and was formally published as a standard in 1981, 
it was adopted by the International Astronomical Union (IAU) in 1982 and an IAU 
working group to maintain its future was defined in 1988.
-The FITS 2.0 and 3.0 standards were approved in 2000 and 2008 respectively, 
and the 4.0 draft has also been released recently, please see the 
@url{https://fits.gsfc.nasa.gov/fits_standard.html, FITS standard document 
webpage} for the full text of all versions.
+The FITS 2.0 and 3.0 standards were approved in 2000 and 2008 respectively, 
and the 4.0 draft has also been released recently, please see the 
@url{https://fits.gsfc.nasa.gov/fits_standard.html, FITS standard document web 
page} for the full text of all versions.
 Also see the @url{https://doi.org/10.1051/0004-6361/201015362, FITS 3.0 
standard paper} for a nice introduction and history along with the full 
standard.
 
 @cindex Meta-data
@@ -7635,8 +8741,10 @@ $ astfits --write=MYKEY1,20.00,"An example keyword" 
--write=MYKEY2,fd
 @end example
 
 @cindex HDU
+@cindex HEALPix
 When no action is requested (and only a file name is given), Fits will print a 
list of information about the extension(s) in the file.
 This information includes the HDU number, HDU name (@code{EXTNAME} keyword), 
type of data (see @ref{Numeric data types}, and the number of data elements it 
contains (size along each dimension for images and table rows and columns).
+Optionally, a comment column is printed for special situations (like a 2D 
HEALPix grid that is usually stored as a 1D dataset/table).
 You can use this to get a general idea of the contents of the FITS file and 
what HDU to use for further processing, either with the Fits program or any 
other Gnuastro program.
 
 Here is one example of information about a FITS file with four extensions: the 
first extension has no data, it is a purely meta-data HDU (commonly used to 
keep meta-data about the whole file, or grouping of extensions, see @ref{Fits}).
@@ -7668,25 +8776,102 @@ Therefore as described in the paragraphs above, when 
no explicit call to the @op
 
 The operating mode and input/output options to Fits are similar to the other 
programs and fully described in @ref{Common options}.
 The options particular to Fits can be divided into two groups:
-1) those related to modifying HDUs or extensions (see @ref{HDU manipulation}), 
and
-2) those related to viewing/modifying meta-data keywords (see @ref{Keyword 
manipulation}).
+1) those related to modifying HDUs or extensions (see @ref{HDU information and 
manipulation}), and
+2) those related to viewing/modifying meta-data keywords (see @ref{Keyword 
inspection and manipulation}).
 These two classes of options cannot be called together in one run: you can 
either work on the extensions or meta-data keywords in any instance of Fits.
 
 @menu
-* HDU manipulation::            Manipulate HDUs within a FITS file.
-* Keyword manipulation::        Manipulate metadata keywords in a HDU
+* HDU information and manipulation::  Learn about the HDUs and move them.
+* Keyword inspection and manipulation::  Manipulate metadata keywords in a HDU
 @end menu
 
 
 
 
 
-@node HDU manipulation, Keyword manipulation, Invoking astfits, Invoking 
astfits
-@subsubsection HDU manipulation
-Each header data unit, or HDU (also known as an extension), in a FITS file is 
an independent dataset (data + meta-data).
+@node HDU information and manipulation, Keyword inspection and manipulation, 
Invoking astfits, Invoking astfits
+@subsubsection HDU information and manipulation
+Each FITS file header data unit, or HDU (also known as an extension) is an 
independent dataset (data + meta-data).
 Multiple HDUs can be stored in one FITS file, see @ref{Fits}.
-The HDU modifying options to the Fits program are listed below.
+The general HDU-related options to the Fits program are listed below as two 
general classes:
+the first group below focus on HDU information while the latter focus on 
manipulating (moving or deleting) the HDUs.
+
+The options below print information about the given HDU on the command-line.
+Thus they cannot be called together in one command (each has its own 
independent output).
+
+@table @option
+@item -n
+@itemx --numhdus
+Print the number of extensions/HDUs in the given file.
+Note that this option must be called alone and will only print a single number.
+It is thus useful in scripts, for example when you need to do check the number 
of extensions in a FITS file.
+
+For a complete list of basic meta-data on the extensions in a FITS file, don't 
use any of the options in this section or in @ref{Keyword inspection and 
manipulation}.
+For more, see @ref{Invoking astfits}.
 
+@item --pixelscale
+Print the HDU's pixel-scale (change in world coordinate for one pixel along 
each dimension) and pixel area or voxel volume.
+Without the @option{--quiet} option, the output of @option{--pixelscale} has 
multiple lines and explanations, thus being more human-friendly.
+It prints the file/HDU name, number of dimensions, and the units along with 
the actual pixel scales.
+Also, when any of the units are in degrees, the pixel scales and area/volume 
are also printed in units of arc-seconds.
+For 3D datasets, the pixel area (on each 2D slice of the 3D cube) is printed 
as well as the voxel volume.
+
+However, in scripts (that are to be run automatically), this human-friendly 
format is annoying, so when called with the @option{--quiet} option, only the 
pixel-scale value(s) along each dimension is(are) printed in one line.
+These numbers are followed by the pixel area (in the raw WCS units).
+For 3D datasets, this will be area on each 2D slice.
+Finally, for 3D datasets, a final number (the voxel volume) is printed.
+As a summary, in @option{--quiet} mode, for 2D datasets three numbers are 
printed and for 3D datasets, 5 numbers are printed.
+
+@item --skycoverage
+@cindex Image's sky coverage
+@cindex Coverage of image over sky
+Print the rectangular area (or 3D cube) covered by the given image/datacube 
HDU over the Sky in the WCS units.
+The covered area is reported in two ways:
+1) the center and full width in each dimension,
+2) the minimum and maximum sky coordinates in each dimension.
+This is option is thus useful when you want to get a general feeling of a new 
image/dataset, or prepare the inputs to query external databases in the region 
of the image (for example with @ref{Query}).
+
+If run without the @option{--quiet} option, the values are given with a 
human-friendly description.
+For example here is the output of this option on an image taken near the star 
Castor:
+
+@example
+$ astfits castor.fits --skycoverage
+Input file: castor.fits (hdu: 1)
+
+Sky coverage by center and (full) width:
+  Center: 113.9149075    31.93759664
+  Width:  2.41762045     2.67945253
+
+Sky coverage by range along dimensions:
+  RA       112.7235592    115.1411797
+  DEC      30.59262123    33.27207376
+@end example
+
+With the @option{--quiet} option, the values are more machine-friendly (easy 
to parse).
+It has two lines, where the first line contains the center/width values and 
the second line shows the coordinate ranges in each dimension.
+
+@example
+$ astfits castor.fits --skycoverage --quiet
+113.9149075     31.93759664     2.41762045      2.67945253
+112.7235592     115.1411797     30.59262123     33.27207376
+@end example
+
+Note that this is a simple rectangle (cube in 3D) definition, so if the image 
is rotated in relation to the celestial coordinates a general polygon is 
necessary to exactly describe the coverage.
+Hence when there is rotation, the reported area will be larger than the actual 
area containing data, you can visually see the area with the @option{--align} 
option of @ref{Warp}.
+
+@item --datasum
+@cindex @code{DATASUM}: FITS keyword
+Calculate and print the given HDU's "datasum" to stdout.
+The given HDU is specified with the @option{--hdu} (or @option{-h}) option.
+This number is calculated by parsing all the bytes of the given HDU's data 
records (excluding keywords).
+This option ignores any possibly existing @code{DATASUM} keyword in the HDU.
+For more on @code{DATASUM} in the FITS standard, see @ref{Keyword inspection 
and manipulation} (under the @code{checksum} component of @option{--write}).
+
+You can use this option to confirm that the data in two different HDUs 
(possibly with different keywords) is identical.
+Its advantage over @option{--write=datasum} (which writes the @code{DATASUM} 
keyword into the given HDU) is that it doesn't require write permissions.
+@end table
+
+The following options manipulate (move/delete) the HDUs in one FITS file or to 
another FITS file.
 These options may be called multiple times in one run.
 If so, the extensions will be copied from the input FITS file to the output 
FITS file in the given order (on the command-line and also in configuration 
files, see @ref{Configuration file precedence}).
 If the separate classes are called together in one run of Fits, then first 
@option{--copy} is run (on all specified HDUs), followed by @option{--cut} 
(again on all specified HDUs), and then @option{--remove} (on all specified 
HDUs).
@@ -7698,15 +8883,6 @@ If no output file name is given, then automatic output 
will be used to store the
 
 @table @option
 
-@item -n
-@itemx --numhdus
-Print the number of extensions/HDUs in the given file.
-Note that this option must be called alone and will only print a single number.
-It is thus useful in scripts, for example when you need to do check the number 
of extensions in a FITS file.
-
-For a complete list of basic meta-data on the extensions in a FITS file, don't 
use any of the options in this section or in @ref{Keyword manipulation}.
-For more, see @ref{Invoking astfits}.
-
 @item -C STR
 @itemx --copy=STR
 Copy the specified extension into the output file, see explanations above.
@@ -7741,15 +8917,93 @@ If we hadn't used @option{--primaryimghdu}, then the 
zero-th extension of @file{
 @end table
 
 
-@node Keyword manipulation,  , HDU manipulation, Invoking astfits
-@subsubsection Keyword manipulation
+@node Keyword inspection and manipulation,  , HDU information and 
manipulation, Invoking astfits
+@subsubsection Keyword inspection and manipulation
 The meta-data in each header data unit, or HDU (also known as extension, see 
@ref{Fits}) is stored as ``keyword''s.
 Each keyword consists of a name, value, unit, and comments.
 The Fits program (see @ref{Fits}) options related to viewing and manipulating 
keywords in a FITS HDU are described below.
 
+First, let's review the @option{--keyvalue} option which should be called 
separately from the rest of the options described in this section.
+Also, unlike the rest of the options in this section, with 
@option{--keyvalue}, you can give more than one input file.
+
+@table @option
+@item -l STR[,STR[,...]
+@itemx --keyvalue=STR[,STR[,...]
+Only print the value of the requested keyword(s): the @code{STR}s.
+@option{--keyvalue} can be called multiple times, and each call can contain 
multiple comma-separated values.
+If more than one file is given, this option uses the same HDU/extension for 
all of them (value to @option{--hdu}).
+For example, you can get the number of dimensions of the three FITS files in 
the running directory, as well as the length along each dimension, with this 
command:
+
+@example
+$ astfits *.fits --keyvalue=NAXIS,NAXIS1 --keyvalue=NAXIS2
+image-a.fits 2      774    672
+image-b.fits 2      774    672
+image-c.fits 2      387    336
+@end example
+
+If a single dataset is given, its name is not printed on the first column, 
only the values of the requested keywords.
+
+@example
+$ astfits image-a.fits --keyvalue=NAXIS,NAXIS1 \
+          --keyvalue=NAXIS2
+2      774    672
+@end example
+
+The output is internally stored (and finally printed) as a table (with one 
column per keyword).
+Therefore just like the Table program, you can use @option{--colinfoinstdout} 
to print the metadata like the example below (also see @ref{Invoking asttable}).
+The keyword metadata (comments and units) are extracted from the comments and 
units of the keyword in the input files (first file that has a comment or unit).
+Hence if the keyword doesn't have units or comments in any of the input files, 
they will be empty.
+For more on Gnuastro's plain-text metadata format, see @ref{Gnuastro text 
table format}.
+
+@example
+$ astfits *.fits --keyvalue=NAXIS,NAXIS1,NAXIS2 \
+          --colinfoinstdout
+# Column 1: FILENAME [name,str10,] Name of input file.
+# Column 2: NAXIS    [    ,u8   ,] number of data axes
+# Column 3: NAXIS1   [    ,u16  ,] length of data axis 1
+# Column 4: NAXIS2   [    ,u16  ,] length of data axis 2
+image-a.fits 2      774    672
+image-b.fits 2      774    672
+image-c.fits 2      387    336
+@end example
+
+Another advantage of a table output is that you can directly write the the 
table to a file.
+For example if you add @option{--output=fileinfo.fits}, the information above 
will be printed into a FITS table.
+You can also pipe it into @ref{Table} to select files based on certain 
properties, to sort them based on another property, or any other operation that 
can be done with Table (including @ref{Column arithmetic}).
+For example with the command below, you can select all the files that have a 
size larger than 500 pixels in both dimensions.
+
+@example
+$ astfits *.fits --keyvalue=NAXIS,NAXIS1,NAXIS2 \
+          --colinfoinstdout \
+          | asttable --range=NAXIS1,500,inf \
+                     --range=NAXIS2,500,inf -cFILENAME
+image-a.fits
+image-b.fits
+@end example
+
+Note that @option{--colinfoinstdout} is necessary to use column names in the 
subsequent @command{asttable} command.
+Also, with the @option{-cFILENAME} option, we are asking Table to only print 
the final file names (we don't need the sizes any more).
+
+The commands with multiple files above used @file{*.fits}, which is only 
useful when all your FITS files are in the same directory.
+However, in many cases, your FITS files will be scattered in multiple 
sub-directories of a certain top-level directory, or you may only want those 
with more particular file name patterns.
+A more powerful way to list the input files to @option{--keyvalue} is to use 
the @command{find} program in Unix-like operating systems.
+For example, with the command below you can search all the FITS files in all 
the sub-directories of @file{/TOP/DIR}.
+
+@example
+astfits $(find /TOP/DIR/ -name "*.fits") --keyvalue=NAXIS2
+@end example
+
+@item -O
+@itemx --colinfoinstdout
+Print column information (or metadata) above the column values when writing 
keyword values to standard output with @option{--keyvalue}.
+You can read this option as column-information-in-standard-output.
+@end table
+
+Below we will discuss the options that can be used to manipulate keywords.
 To see the full list of keywords in a FITS HDU, you can use the 
@option{--printallkeys} option.
-If any of the keywords are to be modified, the headers of the input file will 
be changed.
-If you want to keep the original FITS file or HDU, it is easiest to create a 
copy first and then run Fits on that.
+If any of the keyword modification options below are requested (for example 
@option{--update}), the headers of the input file will be changed first, then 
printed.
+Keyword modification is done within the input file.
+Therefore, if you want to keep the original FITS file or HDU intact, it is 
easiest to create a copy of the file/HDU first and then run Fits on that (for 
copying a HDU to another file, see @ref{HDU information and manipulation}.
 In the FITS standard, keywords are always uppercase.
 So case does not matter in the input or output keyword names you specify.
 
@@ -7812,24 +9066,6 @@ Also be careful for the world coordinate system 
keywords, if you modify or chang
 The keyword related options to the Fits program are fully described below.
 @table @option
 
-@item -a STR
-@itemx --asis=STR
-Write @option{STR} exactly into the FITS file header with no modifications.
-If it does not conform to the FITS standards, then it might cause trouble, so 
please be very careful with this option.
-If you want to define the keyword from scratch, it is best to use the 
@option{--write} option (see below) and let CFITSIO worry about the standards.
-The best way to use this option is when you want to add a keyword from one 
FITS file to another unchanged and untouched.
-Below is an example of such a case that can be very useful sometimes (on the 
command-line or in scripts):
-
-@example
-$ key=$(astfits firstimage.fits | grep KEYWORD)
-$ astfits --asis="$key" secondimage.fits
-@end example
-
-@cindex GNU Bash
-In particular note the double quotation signs (@key{"}) around the reference 
to the @command{key} shell variable (@command{$key}).
-FITS keywords usually have lots of space characters, if this variable is not 
quoted, the shell will only give the first word in the full keyword to this 
option, which will definitely be a non-standard FITS keyword and will make it 
hard to work on the file afterwords.
-See the ``Quoting'' section of the GNU Bash manual for more information if 
your keyword has the special characters @key{$}, @key{`}, or @key{\}.
-
 @item -d STR
 @itemx --delete=STR
 Delete one instance of the @option{STR} keyword from the FITS header.
@@ -7923,16 +9159,68 @@ $ astfits test.fits -h1 --write=/,"My keywords"   \
           --write=a2,0.12,"Second keyword"
 @end example
 
+@item -a STR
+@itemx --asis=STR
+Write the given @code{STR} @emph{exactly} as it is, into the given FITS file 
header with no modifications.
+If the contents of @code{STR} does not conform to the FITS standard for 
keywords, then it may (most probably: it will) corrupt your file and you may 
not be able to open it any more.
+So please be @strong{very careful} with this option.
+If you want to define the keyword from scratch, it is best to use the 
@option{--write} option (see below) and let CFITSIO worry about complying with 
the FITS standard.
+
+The best way to use this option is when you want to add a keyword from one 
FITS file to another, unchanged and untouched.
+Below is an example of such a case that can be very useful sometimes (on the 
command-line or in scripts):
+
+@example
+$ key=$(astfits firstimage.fits | grep KEYWORD)
+$ astfits --asis="$key" secondimage.fits
+@end example
+
+@cindex GNU Bash
+In particular note the double quotation signs (@key{"}) around the shell 
variable (@command{$key}).
+This is because FITS keyword strings usually have lots of space characters, if 
this variable is not quoted, the shell will only give the first word in the 
full keyword to this option, which will definitely be a non-standard FITS 
keyword and will make it hard to work on the file afterwords.
+See the ``Quoting'' section of the GNU Bash manual for more information if 
your keyword has the special characters @key{$}, @key{`}, or @key{\}.
+
+You can also use @option{--asis} to copy multiple keywords from one file to 
another.
+But the process will be a little more complicated. So we'll show the process 
as the simple shell script below.
+You can customize it in the first block of variable definitions:
+1) set the names of the keywords you want to copy: it can be as many keys as 
you want, just put a `@code{\|}' between them.
+2) The input FITS file (where keywords should be read from) and its HDU.
+3) The output FITS file (where keywords should be written to) and its HDU.
+4) Set a ``title'' for the newly added keywords in the output (so they are 
visually separate from the existing keywords in the output).
+
+@example
+#!/bin/bash
+
+# Customizations (input, output and key names).
+# NOTE: put a '\|' between each keyword name.
+keys="KEYa\|KEYb\|KEYc\|KEYd"
+ifits=original.fits;     ihdu=1
+ofits=to_write.fits;     ohdu=1
+title="Keys from $ifits (hdu $ihdu)"
+
+# Read keywords from input and write in output.
+oIFS=$IFS
+IFS=''
+c="astfits $ofits -h$ohdu --write=/,\"$title\""
+astfits $ifits -h$ihdu \
+        | grep $keys \
+        | (while read line; \
+             do c="$c --asis=\"$line\""; \
+           done; eval $c); \
+IFS=$oIFS
+@end example
+
+Since its not too long, you can also simply put the variable values of the 
first block into the second, and write it directly on the command line (if its 
just for one time).
+
 @item checksum
 @cindex CFITSIO
 @cindex @code{DATASUM}: FITS keyword
 @cindex @code{CHECKSUM}: FITS keyword
-When nothing is given afterwards, the header integrity 
keywords@footnote{Section 4.4.2.7 (page 15) of 
@url{https://fits.gsfc.nasa.gov/standard40/fits_standard40aa-le.pdf}} 
@code{DATASUM} and @code{CHECKSUM} will be written/updated.
-They are calculated and written by CFITSIO.
-They thus comply with the FITS standard 4.0 that defines these keywords.
+When nothing is given afterwards, the header integrity keywords @code{DATASUM} 
and @code{CHECKSUM} will be calculated and written/updated.
+This is calculation and writing is done fully by CFITSIO.
+They thus comply with the FITS standard 
4.0@footnote{@url{https://fits.gsfc.nasa.gov/standard40/fits_standard40aa-le.pdf}}
 that defines these keywords (its Appendix J).
 
-If a value is given (for example @option{--write=checksum,my-own-checksum,"my 
checksum"}), then CFITSIO won't be called to calculate these two keywords and 
the value (as well as possible comment and unit) will be written just like any 
other keyword.
-This is generally not recommended, but necessary in special circumstances 
(where the checksum needs to be manually updated for example).
+If a value is given (e.g., @option{--write=checksum,MyOwnCheckSum}), then 
CFITSIO won't be called to calculate these two keywords and the value (as well 
as possible comment and unit) will be written just like any other keyword.
+This is generally not recommended, but necessary in special circumstances (for 
example when the checksum needs to be manually updated).
 
 @code{DATASUM} only depends on the data section of the HDU/extension, so it is 
not changed when you update the keywords.
 But @code{CHECKSUM} also depends on the header and will not be valid if you 
make any further changes to the header.
@@ -8019,208 +9307,30 @@ In this case (following the GNU C Library), this 
option will make the following
 
 This is a very useful option for operations on the FITS date values, for 
example sorting FITS files by their dates, or finding the time difference 
between two FITS files.
 The advantage of working with the Unix epoch time is that you don't have to 
worry about calendar details (for example the number of days in different 
months, or leap years, etc).
-@end table
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@node Sort FITS files by night, ConvertType, Fits, Data containers
-@section Sort FITS files by night
-
-@cindex Calendar
-FITS images usually contain (several) keywords for preserving important dates.
-In particular, for lower-level data, this is usually the observation date and 
time (for example, stored in the @code{DATE-OBS} keyword value).
-When analyzing observed datasets, many calibration steps (like the dark, bias 
or flat-field), are commonly calculated on a per-observing-night basis.
-
-However, the FITS standard's date format (@code{YYYY-MM-DDThh:mm:ss.ddd}) is 
based on the western (Gregorian) calendar.
-Dates that are stored in this format are complicated for automatic processing: 
a night starts in the final hours of one calendar day, and extends to the early 
hours of the next calendar day.
-As a result, to identify datasets from one night, we commonly need to search 
for two dates.
-However calendar peculiarities can make this identification very difficult.
-For example when an observation is done on the night separating two months 
(like the night starting on March 31st and going into April 1st), or two years 
(like the night starting on December 31st 2018 and going into January 1st, 
2019).
-To account for such situations, it is necessary to keep track of how many days 
are in a month, and leap years, etc.
-
-@cindex Unix epoch time
-@cindex Time, Unix epoch
-@cindex Epoch, Unix time
-Gnuastro's @file{astscript-sort-by-night} script is created to help in such 
important scenarios.
-It uses @ref{Fits} to convert the FITS date format into the Unix epoch time 
(number of seconds since 00:00:00 of January 1st, 1970), using the 
@option{--datetosec} option.
-The Unix epoch time is a single number (integer, if not given in sub-second 
precision), enabling easy comparison and sorting of dates after January 1st, 
1970.
-
-You can use this script as a basis for making a much more highly customized 
sorting script.
-Here are some examples
-
-@itemize
-@item
-If you need to copy the files, but only need a single extension (not the whole 
file), you can add a step just before the making of the symbolic links, or 
copies, and change it to only copy a certain extension of the FITS file using 
the Fits program's @option{--copy} option, see @ref{HDU manipulation}.
-
-@item
-If you need to classify the files with finer detail (for example the purpose 
of the dataset), you can add a step just before the making of the symbolic 
links, or copies, to specify a file-name prefix based on other certain keyword 
values in the files.
-For example when the FITS files have a keyword to specify if the dataset is a 
science, bias, or flat-field image.
-You can read it and to add a @code{sci-}, @code{bias-}, or @code{flat-} to the 
created file (after the @option{--prefix}) automatically.
-
-For example, let's assume the observing mode is stored in the hypothetical 
@code{MODE} keyword, which can have three values of @code{BIAS-IMAGE}, 
@code{SCIENCE-IMAGE} and @code{FLAT-EXP}.
-With the step below, you can generate a mode-prefix, and add it to the 
generated link/copy names (just correct the filename and extension of the first 
line to the script's variables):
-
-@example
-modepref=$(astfits infile.fits -h1 \
-                   | sed -e"s/'/ /g" \
-                   | awk '$1=="MODE"@{ \
-                       if($3=="BIAS-IMAGE") print "bias-"; \
-                       else if($3=="SCIENCE-IMAGE") print "sci-"; \
-                       else if($3==FLAT-EXP) print "flat-"; \
-                       else print $3, "NOT recognized"; exit 1@}')
-@end example
-
-@cindex GNU AWK
-@cindex GNU Sed
-Here is a description of it.
-We first use @command{astfits} to print all the keywords in extension @code{1} 
of @file{infile.fits}.
-In the FITS standard, string values (that we are assuming here) are placed in 
single quotes (@key{'}) which are annoying in this context/use-case.
-Therefore, we pipe the output of @command{astfits} into @command{sed} to 
remove all such quotes (substituting them with a blank space).
-The result is then piped to AWK for giving us the final mode-prefix: with 
@code{$1=="MODE"}, we ask AWK to only consider the line where the first column 
is @code{MODE}.
-There is an equal sign between the key name and value, so the value is the 
third column (@code{$3} in AWK).
-We thus use a simple @code{if-else} structure to look into this value and 
print our custom prefix based on it.
-The output of AWK is then stored in the @code{modepref} shell variable which 
you can add to the link/copy name.
-
-With the solution above, the increment of the file counter for each night will 
be independent of the mode.
-If you want the counter to be mode-dependent, you can add a different counter 
for each mode and use that counter instead of the generic counter for each 
night (based on the value of @code{modepref}).
-But we'll leave the implementation of this step to you as an exercise.
-
-@end itemize
-
-@menu
-* Invoking astscript-sort-by-night::  Inputs and outputs to this script.
-@end menu
-
-@node Invoking astscript-sort-by-night,  , Sort FITS files by night, Sort FITS 
files by night
-@subsection Invoking astscript-sort-by-night
-
-This installed script will read a FITS date formatted value from the given 
keyword, and classify the input FITS files into individual nights.
-For more on installed scripts please see (see @ref{Installed scripts}).
-This script can be used with the following general template:
-
-@example
-$ astscript-sort-by-night [OPTION...] FITS-files
-@end example
-
-@noindent
-One line examples:
-
-@example
-## Use the DATE-OBS keyword
-$ astscript-sort-by-night --key=DATE-OBS /path/to/data/*.fits
-
-## Make links to the input files with the `img-' prefix
-$ astscript-sort-by-night --link --prefix=img- /path/to/data/*.fits
-@end example
 
-This script will look into a HDU/extension (@option{--hdu}) for a keyword 
(@option{--key}) in the given FITS files and interpret the value as a date.
-The inputs will be separated by "night"s (9:00a.m to next day's 8:59:59a.m, 
spanning two calendar days, exact hour can be set with @option{--hour}).
-
-The default output is a list of all the input files along with the following 
two columns: night number and file number in that night (sorted by time).
-With @option{--link} a symbolic link will be made (one for each input) that 
contains the night number, and number of file in that night (sorted by time), 
see the description of @option{--link} for more.
-When @option{--copy} is used instead of a link, a copy of the inputs will be 
made instead of symbolic link.
-
-Below you can see one example where all the @file{target-*.fits} files in the 
@file{data} directory should be separated by observing night according to the 
@code{DATE-OBS} keyword value in their second extension (number @code{1}, 
recall that HDU counting starts from 0).
-You can see the output after the @code{ls} command.
-
-@example
-$ astscript-sort-by-night -pimg- -h1 -kDATE-OBS data/target-*.fits
-$ ls
-img-n1-1.fits img-n1-2.fits img-n2-1.fits ...
-@end example
-
-The outputs can be placed in a different (already existing) directory by 
including that directory's name in the @option{--prefix} value, for example 
@option{--prefix=sorted/img-} will put them all under the @file{sorted} 
directory.
-
-This script can be configured like all Gnuastro's programs (through 
command-line options, see @ref{Common options}), with some minor differences 
that are described in @ref{Installed scripts}.
-The particular options to this script are listed below:
-
-@table @option
-@item -h STR
-@itemx --hdu=STR
-The HDU/extension to use in all the given FITS files.
-All of the given FITS files must have this extension.
-
-@item -k STR
-@itemx --key=STR
-The keyword name that contains the FITS date format to classify/sort by.
-
-@item -H FLT
-@itemx --hour=FLT
-The hour that defines the next ``night''.
-By default, all times before 9:00a.m are considered to belong to the previous 
calendar night.
-If a sub-hour value is necessary, it should be given in units of hours, for 
example @option{--hour=9.5} corresponds to 9:30a.m.
+@item --wcsdistortion STR
+@cindex WCS distortion
+@cindex Distortion, WCS
+@cindex SIP WCS distortion
+@cindex TPV WCS distortion
+If the argument has a WCS distortion, the output (file given with the 
@option{--output} option) will have the distortion given to this option (for 
example @code{SIP}, @code{TPV}).
+With this option, the FITS program will read the minimal set of keywords from 
the input HDU and the HDU data, it will then write them into the file given to 
the @option{--output} option but with a newly created set of WCS-related 
keywords corresponding to the desired distortion standard.
 
-@cartouche
-@noindent
-@cindex Time zone
-@cindex UTC (Universal time coordinate)
-@cindex Universal time coordinate (UTC)
-@strong{Dealing with time zones:}
-The time that is recorded in @option{--key} may be in UTC (Universal Time 
Coordinate).
-However, the organization of the images taken during the night depends on the 
local time.
-It is possible to take this into account by setting the @option{--hour} option 
to the local time in UTC.
+If no @option{--output} file is specified, an automatically generated output 
name will be used which is composed of the input's name but with the 
@file{-DDD.fits} suffix, see @ref{Automatic output}.
+Where @file{DDD} is the value given to this option (desired output distortion).
 
-For example, consider a set of images taken in Auckland (New Zealand, UTC+12) 
during different nights.
-If you want to classify these images by night, you have to know at which time 
(in UTC time) the Sun rises (or any other separator/definition of a different 
night).
-In this particular example, you can use @option{--hour=21}.
-Because in Auckland, a night finishes (roughly) at the local time of 9:00, 
which corresponds to 21:00 UTC.
-@end cartouche
+Note that all possible conversions between all standards are not yet supported.
+If the requested conversion is not supported, an informative error message 
will be printed.
+If this happens, please let us know and we'll try our best to add the 
respective conversions.
 
-@item -l
-@itemx --link
-Create a symbolic link for each input FITS file.
-This option cannot be used with @option{--copy}.
-The link will have a standard name in the following format (variable parts are 
written in @code{CAPITAL} letters and described after it):
+For example with the command below, you can be sure that if @file{in.fits} has 
a distortion in its WCS, the distortion of @file{out.fits} will be in the SIP 
standard.
 
 @example
-PnN-I.fits
+$ astfits in.fits --wcsdistortion=SIP --output=out.fits
 @end example
-
-@table @code
-@item P
-This is the value given to @option{--prefix}.
-By default, its value is @code{./} (to store the links in the directory this 
script was run in).
-See the description of @code{--prefix} for more.
-@item N
-This is the night-counter: starting from 1.
-@code{N} is just incremented by 1 for the next night, no matter how many 
nights (without any dataset) there are between two subsequent observing nights 
(its just an identifier for each night which you can easily map to different 
calendar nights).
-@item I
-File counter in that night, sorted by time.
 @end table
 
-@item -c
-@itemx --copy
-Make a copy of each input FITS file with the standard naming convention 
described in @option{--link}.
-With this option, instead of making a link, a copy is made.
-This option cannot be used with @option{--link}.
-
-@item -p STR
-@itemx --prefix=STR
-Prefix to append before the night-identifier of each newly created link or 
copy.
-This option is thus only relevant with the @option{--copy} or @option{--link} 
options.
-See the description of @option{--link} for how its used.
-For example, with @option{--prefix=img-}, all the created file names in the 
current directory will start with @code{img-}, making outputs like 
@file{img-n1-1.fits} or @file{img-n3-42.fits}.
 
-@option{--prefix} can also be used to store the links/copies in another 
directory relative to the directory this script is being run (it must already 
exist).
-For example @code{--prefix=/path/to/processing/img-} will put all the 
links/copies in the @file{/path/to/processing} directory, and the files (in 
that directory) will all start with @file{img-}.
-@end table
 
 
 
@@ -8239,7 +9349,7 @@ For example @code{--prefix=/path/to/processing/img-} will 
put all the links/copi
 
 
 
-@node ConvertType, Table, Sort FITS files by night, Data containers
+@node ConvertType, Table, Fits, Data containers
 @section ConvertType
 
 @cindex Data format conversion
@@ -8626,8 +9736,13 @@ Viridis is the default colormap of the popular 
Matplotlib module of Python and a
 @cindex SLS Color
 @cindex Colormap: SLS
 The SLS color range, taken from the commonly used @url{http://ds9.si.edu,SAO 
DS9}.
-The advantage of this color range is that it ranges from black to dark blue, 
and finishes with red and white.
-So unlike the HSV color range, it includes black and white and brighter colors 
(like yellow, red and white) show the larger values.
+The advantage of this color range is that it starts with black, going into 
dark blue and finishes with the brighter colors of red and white.
+So unlike the HSV color range, it includes black and white and brighter colors 
(like yellow, red) show the larger values.
+
+@item sls-inverse
+@cindex Colormap: SLS-inverse
+The inverse of the SLS color map (see above), where the lowest value 
corresponds to white and the highest value is black.
+While SLS is good for visualizing on the monitor, SLS-inverse is good for 
printing.
 @end table
 
 @item --rgbtohsv
@@ -8716,7 +9831,7 @@ Note that this behavior is ideal for gray-scale images, 
if you want a color imag
 
 @end table
 
-@node Table,  , ConvertType, Data containers
+@node Table, Query, ConvertType, Data containers
 @section Table
 
 Tables are the products of processing astronomical images and spectra.
@@ -8763,31 +9878,60 @@ Just note that white-space characters are used between 
the tokens of the arithme
 Therefore the whole expression (including the activation word) has to be 
quoted on the command-line or in a shell script (see the examples below).
 
 To identify a column you can directly use its name, or specify its number 
(counting from one, see @ref{Selecting table columns}).
-When you are giving a column number, it is necessary to prefix it with a 
@code{c} (otherwise it is not distinguishable from a constant number to use in 
the arithmetic operation).
+When you are giving a column number, it is necessary to prefix the number with 
a @code{$}, similar to AWK.
+Otherwise the number is not distinguishable from a constant number to use in 
the arithmetic operation.
 
 For example with the command below, the first two columns of @file{table.fits} 
will be printed along with a third column that is the result of multiplying the 
first column with @mymath{10^{10}} (for example to convert wavelength from 
Meters to Angstroms).
-Note how without the `@key{c}', it is not possible to distinguish between 
@key{1} as a column-counter, or as a constant number to use in the arithmetic 
operation.
+Note that without the `@key{$}', it is not possible to distinguish between 
``1'' as a column-counter, or as a constant number to use in the arithmetic 
operation.
+Also note that because of the significance of @key{$} for the command-line 
environment, the single-quotes are used here (as in an AWK expression), not 
double-quotes.
+
+@example
+$ asttable table.fits -c1,2 -c'arith $1 1e10 x'
+@end example
+
+@cartouche
+@noindent
+@strong{Single quotes when string contains @key{$}}: On the command-line, or 
in shell-scripts, @key{$} is used to expand variables, for example @code{echo 
$PATH} prints the value (a string of characters) in the variable @code{PATH}, 
it will not simply print @code{$PATH}.
+This operation is also permitted within double quotes, so @code{echo "$PATH"} 
will produce the same output.
+This is good when printing values, for example in the command below, 
@code{$PATH} will expand to the value within it.
+
+@example
+$ echo "My path is: $PATH"
+@end example
+
+If you actually want to return the literal string @code{$PATH}, not the value 
in the @code{PATH} variable (like the scenario here in column arithmetic), you 
should put it in single quotes like below.
+The printed value here will include the @code{$}, please try it to see for 
your self and compare to above.
 
 @example
-$ asttable table.fits -c1,2 -c"arith c1 1e10 x"
+$ echo 'My path is: $PATH'
 @end example
 
+Therefore, when your column arithmetic involves the @key{$} sign (to specify 
columns by number), quote your @code{arith } string with a single quotation 
mark.
+Otherwise you can use both single or double quotes.
+@end cartouche
+
 Alternatively, if the columns have meta-data and the first two are 
respectively called @code{AWAV} and @code{SPECTRUM}, the command above is 
equivalent to the command below.
-Note that the @key{c} is no longer necessary in this scenario.
+Note that the character `@key{$}' is no longer necessary in this scenario 
(because names will not be confused with numbers):
 
 @example
-$ asttable table.fits -cAWAV,SPECTRUM -c"arith AWAV 1e10 x"
+$ asttable table.fits -cAWAV,SPECTRUM -c'arith AWAV 1e10 x'
 @end example
 
 Comparison of the two commands above clearly shows why it is recommended to 
use column names instead of numbers.
-When the columns have clear names, the command/script actually becomes much 
more readable, describing the intent of the operation.
+When the columns have descriptive names, the command/script actually becomes 
much more readable, describing the intent of the operation.
 It is also independent of the low-level table structure: for the second 
command, the position of the @code{AWAV} and @code{SPECTRUM} columns in 
@file{table.fits} is irrelevant.
 
+By nature, column arithmetic changes the values of the data within the column.
+So the old column meta data can't be used any more.
+By default the new column created for the arithmetic operation will be given 
generic metadata (for example its name will be @code{ARITH_1}, which is hardly 
useful!).
+But meta data are critically important and it is good practice to always have 
short, but descriptive, names for each columns, units and also some comments 
for more explanation.
+To add metadata to a column, you can use the @option{--colmetadata} option 
that is described in @ref{Invoking asttable}.
+
 Finally, since the arithmetic expressions are a value to @option{--column}, it 
doesn't necessarily have to be a separate option, so the commands above are 
also identical to the command below (note that this only has one @option{-c} 
option).
 Just be very careful with the quoting!
 
 @example
-$ asttable table.fits -cAWAV,SPECTRUM,"arith AWAV 1e10 x"
+$ asttable table.fits -cAWAV,SPECTRUM,'arith AWAV 1e10 x'
 @end example
 
 Almost all the arithmetic operators of @ref{Arithmetic operators} are also 
supported for column arithmetic in Table.
@@ -8795,6 +9939,8 @@ In particular, the few that are not present in the 
Gnuastro library aren't yet s
 For a list of the Gnuastro library arithmetic operators, please see the macros 
starting with @code{GAL_ARITHMETIC_OP} and ending with the operator name in 
@ref{Arithmetic on datasets}.
 Besides the operators in @ref{Arithmetic operators}, several operators are 
only available in Table to use on table columns.
 
+
+
 @cindex WCS: World Coordinate System
 @cindex World Coordinate System (WCS)
 @table @code
@@ -8808,43 +9954,114 @@ The first three columns are the input table's ID, RA 
and Dec columns.
 The fourth and fifth columns will be the pixel positions in @file{image.fits} 
that correspond to each RA and Dec.
 
 @example
-$ asttable table.fits -cID,RA,DEC,"arith RA DEC wcstoimg" \
+$ asttable table.fits -cID,RA,DEC,'arith RA DEC wcstoimg' \
            --wcsfile=image.fits
 $ asttable table.fits -cID,RA -cDEC \
-           -c"arith RA DEC wcstoimg" --wcsfile=image.fits
+           -c'arith RA DEC wcstoimg' --wcsfile=image.fits
 @end example
 
 @item imgtowcs
 Similar to @code{wcstoimg}, except that image/dataset coordinates are 
converted to WCS coordinates.
 
-@item angular-distance
+@item distance-flat
+Return the distance between two points assuming they are on a flat surface.
+Note that each point needs two coordinates, so this operator needs four 
operands (currently it only works for 2D spaces).
+The first and second popped operands are considered to belong to one point and 
the third and fourth popped operands to the second point.
+
+Each of the input points can be a single coordinate or a full table column 
(containing many points).
+In other words, the following commands are all valid:
+
+@example
+$ asttable table.fits \
+           -c'arith X1 Y1 X2 Y2 distance-flat'
+$ asttable table.fits \
+           -c'arith X Y 12.345 6.789 distance-flat'
+$ asttable table.fits \
+           -c'arith 12.345 6.789 X Y distance-flat'
+@end example
+
+In the first case we are assuming that @file{table.fits} has the following 
four columns @code{X1}, @code{Y1}, @code{X2}, @code{Y2}.
+The returned column by this operator will be the difference between two points 
in each row with coordinates like the following (@code{X1}, @code{Y1}) and 
(@code{X2}, @code{Y2}).
+In other words, for each row, the distance between different points is 
calculated.
+In the second and third cases (which are identical), it is assumed that 
@file{table.fits} has the two columns @code{X} and @code{Y}.
+The returned column by this operator will be the difference of each row with 
the fixed point at (12.345, 6.789).
+
+@item distance-on-sphere
 Return the spherical angular distance (along a great circle, in degrees) 
between the given two points.
 Note that each point needs two coordinates (in degrees), so this operator 
needs four operands.
 The first and second popped operands are considered to belong to one point and 
the third and fourth popped operands to the second point.
 
-Each of the input points can be a single coordinate or a full table column 
(containing many points.
+Each of the input points can be a single coordinate or a full table column 
(containing many points).
 In other words, the following commands are all valid:
 
 @example
 $ asttable table.fits \
-           -c"arith RA1 DEC1 RA2 DEC2 angular-distance"
+           -c'arith RA1 DEC1 RA2 DEC2 distance-on-sphere'
 $ asttable table.fits \
-           -c"arith RA DEC 12.345 6.789 angular-distance"
+           -c'arith RA DEC 9.876 5.432 distance-on-sphere'
 $ asttable table.fits \
-           -c"arith 12.345 6.789 RA DEC angular-distance"
+           -c'arith 9.876 5.432 RA DEC distance-on-sphere'
 @end example
 
 In the first case we are assuming that @file{table.fits} has the following 
four columns @code{RA1}, @code{DEC1}, @code{RA2}, @code{DEC2}.
-The returned column by this operator would be the difference between two 
points in each row with coordinates like the following (@code{RA1}, 
@code{DEC1}) and (@code{RA2}, @code{DEC2}).
+The returned column by this operator will be the difference between two points 
in each row with coordinates like the following (@code{RA1}, @code{DEC1}) and 
(@code{RA2}, @code{DEC2}).
 In other words, for each row, the angular distance between different points is 
calculated.
-
 In the second and third cases (which are identical), it is assumed that 
@file{table.fits} has the two columns @code{RA} and @code{DEC}.
-The returned column by this operator will be the difference of each row with 
the fixed point of (12.345, 6.789).
+The returned column by this operator will be the difference of each row with 
the fixed point at (9.876, 5.432).
 
 The distance (along a great circle) on a sphere between two points is 
calculated with the equation below, where @mymath{r_1}, @mymath{r_2}, 
@mymath{d_1} and @mymath{d_2} are the right ascensions and declinations of 
points 1 and 2.
 
 @dispmath {\cos(d)=\sin(d_1)\sin(d_2)+\cos(d_1)\cos(d_2)\cos(r_1-r_2)}
 
+@item ra-to-degree
+Convert the hour-wise Right Ascension (RA) string, in the sexagesimal format 
of @code{_h_m_s} or @code{_:_:_}, to degrees.
+Note that the input column has to have a string format.
+In FITS tables, string columns are well-defined.
+For plain-text tables, please follow the standards defined in @ref{Gnuastro 
text table format}, otherwise the string column won't be read.
+@example
+$ asttable catalog.fits -c'arith RA ra-to-degree'
+$ asttable catalog.fits -c'arith $5 ra-to-degree'
+@end example
+
+@item dec-to-degree
+Convert the sexagesimal Declination (Dec) string, in the format of 
@code{_d_m_s} or @code{_:_:_}, to degrees (a single floating point number).
+For more details please see the @option{ra-to-degree} operator.
+
+@item degree-to-ra
+@cindex Sexagesimal
+@cindex Right Ascension
+Convert degrees (a column with a single floating point number) to the Right 
Ascension, RA, string (in the sexagesimal format hours, minutes and seconds, 
written as @code{_h_m_s}).
+The output will be a string column so no further mathematical operations can 
be done on it.
+The output file can be in any format (for example FITS or plain-text).
+If it is plain-text, the string column will be written following the standards 
described in @ref{Gnuastro text table format}.
+
+@item degree-to-dec
+@cindex Declination
+Convert degrees (a column with a single floating point number) to the 
Declination, Dec, string (in the format of @code{_d_m_s}).
+See the @option{degree-to-ra} for more on the format of the output.
+
+@item date-to-sec
+@cindex Unix epoch time
+@cindex Time, Unix epoch
+@cindex Epoch, Unix time
+The popped operand should be a string column in the FITS date format (most 
generally: @code{YYYY-MM-DDThh:mm:ss.ddd...}).
+This operator will return the corresponding Unix epoch time (number of seconds 
that have passed since 00:00:00 Thursday, January 1st, 1970).
+The returned operand will be named @code{UNIXSEC} (short for Unix-seconds).
+If any of the times have sub-second precision, the numeric datatype of the 
column will be 64-bit floating point type.
+Otherwise it will be a 64-bit, signed integer, see @ref{Numeric data types}.
+
+For example, in the example below we are using this operator, in combination 
with the @option{--keyvalue} option of the Fits program, to sort your desired 
FITS files by observation date (value in the @code{DATE-OBS} keyword in example 
below):
+
+@example
+$ astfits *.fits --keyvalue=DATE-OBS --colinfoinstdout \
+          | asttable -cFILENAME,'arith DATE-OBS date-to-sec' \
+                     --colinfoinstdout \
+          | asttable --sort=UNIXSEC
+@end example
+
+If you don't need to see the Unix-seconds any more, you can add a 
@option{-cFILENAME} (short for @option{--column=FILENAME}) at the end.
+For more on @option{--keyvalue}, see @ref{Keyword inspection and manipulation}.
+
 @end table
 
 
@@ -8885,14 +10102,23 @@ $ asttable bintab.fits --range=10,10e5,inf
 
 ## Only print the 2nd column, and the third column multiplied by 5,
 ## Save the resulting two columns in `table.txt'
-$ asttable bintab.fits -c2,"arith c2 5 x" -otable.fits
+$ asttable bintab.fits -c2,'arith $2 5 x' -otable.fits
 
 ## Sort the output columns by the third column, save output:
 $ asttable bintab.fits --sort=3 -ooutput.txt
 
-## Subtract the first column from the second in `cat.txt' (can also
-## be a FITS table) and keep the third and fourth columns.
-$ asttable cat.txt -c"arith c2 c1 -",3,4 -ocat.fits
+## Subtract the first column from the second in `cat.fits' (can also
+## be a text table) and keep the third and fourth columns.
+$ asttable cat.txt -c'arith $2 $1 -',3,4 -ocat.fits
+
+## Convert sexagesimal coordinates to degrees (same can be done in a
+## large table given as argument).
+$ echo "7h34m35.5498 31d53m14.352s" | asttable
+
+## Convert RA and Dec in degrees to sexagesimal (same can be done in a
+## large table given as argument).
+echo "113.64812416667 31.88732" \
+     | asttable -c'arith $1 degree-to-ra $2 degree-to-dec'
 @end example
 
 Table's input dataset can be given either as a file or from Standard input 
(see @ref{Standard input}).
@@ -8909,7 +10135,26 @@ Options can also be stored in directory, user or 
system-wide configuration files
 Table does not follow Automatic output that is common in most Gnuastro 
programs, see @ref{Automatic output}.
 Thus, in the absence of an output file, the selected columns will be printed 
on the command-line with no column information, ready for redirecting to other 
tools like AWK or sort, similar to the examples above.
 
-@table @option
+@cartouche
+@noindent
+@strong{Sexagesimal coordinates as floats in plain-text tables:}
+When a column is determined to be a floating point type (32-bit or 64-bit) in 
a plain-text table, it can contain sexagesimal values in the format of 
`@code{_h_m_s}' (for RA) and `@code{_d_m_s}' (for Dec).
+In this case, the string will be immediately converted to a single floating 
point number (in units of degrees) and stored in memory with the rest of the 
column or table.
+Besides being useful in large tables, with this feature, conversion to 
sexagesimal coordinates to degrees becomes very easy, for example:
+@example
+echo "7h34m35.5498 31d53m14.352s" | asttable
+@end example
+
+@noindent
+The inverse can also be done with the more general column arithmetic
+operators:
+@example
+echo "113.64812416667 31.88732" \
+     | asttable -c'arith $1 degree-to-ra $2 degree-to-dec'
+@end example
+@end cartouche
+
+@table @option
 
 @item -i
 @itemx --information
@@ -8939,16 +10184,63 @@ The order of the output columns will be the same order 
given to the option or in
 This option is not mandatory, if no specific columns are requested, all the 
input table columns are output.
 When this option is called multiple times, it is possible to output one column 
more than once.
 
-@item -w STR
-@itemx --wcsfile=STR
+@item -w FITS
+@itemx --wcsfile=FITS
 FITS file that contains the WCS to be used in the @code{wcstoimg} and 
@code{imgtowcs} operators of @option{--column} (see above).
 The extension name/number within the FITS file can be specified with 
@option{--wcshdu}.
 
+If the value to this option is @option{none}, no WCS will be written in the 
output.
+
 @item -W STR
 @itemx --wcshdu=STR
 FITS extension/HDU that contains the WCS to be used in the @code{wcstoimg} and 
@code{imgtowcs} operators of @option{--column} (see above).
 The FITS file name can be specified with @option{--wcsfile}.
 
+@item -L FITS/TXT
+@itemx --catcolumnfile=FITS/TXT
+Concatenate (or add, or append) the columns of this option's value (a 
filename) to the output columns.
+This option may be called multiple times (to add columns from more than one 
file into the final output), the columns from each file will be added in the 
same order that this option is called.
+
+By default all the columns of the given file will be appended, if you only 
want certain columns to be appended, use the @option{--catcolumns} option to 
specify their name or number (see @ref{Selecting table columns}).
+Note that the columns given to @option{--catcolumns} must be present in all 
the given files (if this option is called more than once).
+
+The concatenation is done after any column selection (for example with 
@option{--column}) or row selection (for example with @option{--range}) is 
applied to the main input table given to Table.
+The number of rows in the file(s) given to this option has to be the same as 
the final output table if this option wasn't given.
+
+If the file given to this option is a FITS file, its necessary to also define 
the corresponding HDU/extension with @option{--catcolumnhdu}.
+Also note that no operation (for example row selection, arithmetic or etc) is 
applied to the table given to this option.
+
+If the appended columns have a name, the column names of each file will be 
appended with a @code{-N}, where @code{N} is a counter starting from 1 for each 
appended file.
+This is done because when concatenating columns from multiple tables (more 
than two) into one, they may have the same name, and its not good practice to 
have multiple columns with the same name.
+You can disable this feature with @option{--catcolumnrawname}.
+To have full control over the concatenated column names, you can use the 
@option{--colmetadata} option described below.
+
+For example, let's assume you have two catalogs of the same objects (same 
number of rows) in different filters.
+Such that @file{f160w-cat.fits} has a @code{MAGNITUDE} column that has the 
magnitude of each object in the @code{F160W} filter and similarly 
@file{f105w-cat.fits}, also has a @code{MAGNITUDE} column, but for the 
@code{F105W} filter.
+You can use column concatenation like below to import the @code{MAGNITUDE} 
column from the @code{F105W} catalog into the @code{F160W} catalog, while 
giving each magnitude column a different name:
+
+@example
+asttable f160w-cat.fits --output=both.fits \
+  --catcolumnfile=f105w-cat.fits --catcolumns=MAGNITUDE \
+  --colmetadata=MAGNITUDE,MAG-F160W,log,"Magnitude in F160W" \
+  --colmetadata=MAGNITUDE-1,MAG-F105W,log,"Magnitude in F105W"
+@end example
+
+@item -u STR/INT
+@itemx --catcolumnhdu=STR/INT
+The HDU/extension of the FITS file(s) that should be concatenated, or 
appended, with @option{--catcolumnfile}.
+If @option{--catcolumn} is called more than once with more than one FITS file, 
its necessary to call this option more than once.
+The HDUs will be loaded in the same order as the FITS files given to 
@option{--catcolumnfile}.
+
+@item -C STR/INT
+@itemx --catcolumns=STR/INT
+The column(s) in the file(s) given to @option{--catcolumnfile} to append.
+When this option is not given, all the columns will be concatenated.
+See @option{--catcolumnfile} for more.
+
+@item --catcolumnrawname
+Don't modify the names of the concatenated (appended) columns, see description 
in @option{--catcolumnfile}.
+
 @item -O
 @itemx --colinfoinstdout
 @cindex Standard output
@@ -8971,6 +10263,36 @@ This is good when you just want to select using one 
column's values, but don't n
 For one example of using this option, see the example under
 @option{--sigclip-median} in @ref{Invoking aststatistics}.
 
+@item --inpolygon=STR1,STR2
+Only return rows where the given coordinates are inside the polygon specified 
by the @option{--polygon} option.
+The coordinate columns are the given @code{STR1} and @code{STR2} columns, they 
can be a column name or counter (see @ref{Selecting table columns}).
+
+Note that the chosen columns doesn't have to be in the output columns (which 
are specified by the @code{--column} option).
+For example if we want to select rows in the polygon specified in @ref{Dataset 
inspection and cropping}, this option can be used like this (you can remove the 
double quotations and write them all in one line if you remove the white-spaces 
around the colon separating the column vertices):
+
+@example
+asttable table.fits --inpolygon=RA,DEC      \
+         --polygon="53.187414,-27.779152    \
+                    : 53.159507,-27.759633  \
+                    : 53.134517,-27.787144  \
+                    : 53.161906,-27.807208" \
+@end example
+
+@cartouche
+@noindent
+@strong{Flat/Euclidean space: } The @option{--inpolygon} option assumes a 
flat/Euclidean space so it is only correct for RA and Dec when the polygon size 
is very small like the example above.
+If your polygon is a degree or larger, it may not return correct results.
+We are working on other options for this.
+@end cartouche
+
+@item --outpolygon=STR1,STR2
+Only return rows where the given coordinates are outside the polygon specified 
by the @option{--polygon} option.
+This option is very similar to the @option{--inpolygon} option, so see the 
description there for more.
+
+@item --polygon=FLT:FLT,...
+The polygon to use for the @code{--inpolygon} and @option{--outpolygon} 
options.
+The values to this option is parsed in the same way that the Crop program, see 
its description there for more: @ref{Crop options}.
+
 @item -e STR,INT/FLT,...
 @itemx --equal=STR,INT/FLT,...
 Only output rows that are equal to the given number(s) in the given column.
@@ -8978,6 +10300,12 @@ The first argument is the column identifier (name or 
number, see @ref{Selecting
 For example @option{--equal=ID,5,6,8} will only print the rows that have a 
value of 5, 6, or 8 in the @code{ID} column.
 This option can also be called multiple times, so @option{--equal=ID,4,5 
--equal=ID,6,7} has the same effect as @option{--equal=4,5,6,7}.
 
+The @option{--equal} and @option{--notequal} options also work when the given 
column has a string type.
+In this case the given value to the option will also be parsed as a string, 
not as a number.
+When dealing with string columns, be careful with trailing white space 
characters (the actual value maybe adjusted to the right, left, or center of 
the column's width).
+If you need to account for such white spaces, you can use shell quoting.
+For example @code{--equal=NAME,"  myname "}.
+
 @cartouche
 @noindent
 @strong{Equality and floating point numbers:} Floating point numbers are only 
approximate values (see @ref{Numeric data types}).
@@ -8993,6 +10321,7 @@ For example @option{--notequal=ID,5,6,8} will only print 
the rows where the @cod
 This option can also be called multiple times, so @option{--notequal=ID,4,5 
--notequal=ID,6,7} has the same effect as @option{--notequal=4,5,6,7}.
 
 Be very careful if you want to use the non-equality with floating point 
numbers, see the special note under @option{--equal} for more.
+This option also works when the given column has a string type, see the 
description under @option{--equal} (above) for more.
 
 @item -s STR
 @item --sort=STR
@@ -9011,6 +10340,7 @@ When called with @option{--sort}, rows will be sorted in 
descending order.
 Only print the given number of rows from the @emph{top} of the final table.
 Note that this option only affects the @emph{output} table.
 For example if you use @option{--sort}, or @option{--range}, the printed rows 
are the first @emph{after} applying the sort sorting, or selecting a range of 
the full input.
+This option cannot be called with @option{--tail}, @option{--rowlimit} or 
@option{--rowrandom}.
 
 @cindex GNU Coreutils
 If the given value to @option{--head} is 0, the output columns won't have any 
rows and if its larger than the number of rows in the input table, all the rows 
are printed (this option is effectively ignored).
@@ -9020,7 +10350,541 @@ This behavior is taken from the @command{head} program 
in GNU Coreutils.
 @itemx --tail=INT
 Only print the given number of rows from the @emph{bottom} of the final table.
 See @option{--head} for more.
+This option cannot be called with @option{--head}, @option{--rowlimit} or 
@option{--rowrandom}.
+
+@item --rowlimit=INT,INT
+Only return the rows within the requested positional range (inclusive on both 
sides).
+Therefore, @code{--rowlimit=5,7} will return 3 of the input rows, row 5, 6 and 
7.
+This option will abort if any of the given values is larger than the total 
number of rows in the table.
+
+With the @option{--head} or @option{--tail} options you can only see the top 
or bottom few rows.
+However, with this option, you can limit the returned rows to a contiugous set 
of rows in the middle of the table.
+Therefore this option cannot be called with @option{--head}, @option{--tail}, 
or @option{--rowrandom}.
+
+@item --rowrandom=INT
+@cindex Random row selection
+@cindex Row selection, by random
+Select @code{INT} rows from the input table by random (assuming a uniform 
distribution).
+This option is applied @emph{after} the value-based selection options (like 
@option{--sort}, @option{--range}, @option{--polygon} and etc).
+On the other hand, only the row counters are randomly selected, this option 
doesn't change the order.
+Therefore, if @option{--rowrandom} is called together with @option{--sort}, 
the returned rows are still sorted.
+This option cannot be called with @option{--head}, @option{--tail}, or 
@option{--rowlimit}.
+
+This option will only have an effect if @code{INT} is larger than the number 
of rows when it is activated (after the value-based selection options have been 
applied).
+When there are fewer rows, a warning is printed, saying that this option has 
no effect.
+The warning can be disabled with the @option{--quiet} option.
+
+@cindex Reproducibility
+Due to its nature (to be random), the output of this option differs in each 
run.
+Therefore 5 calls to Table with @option{--rowrandom} on the same input table 
will generate 5 different outputs.
+If you want a reproducible random selection, set the @code{GSL_RNG_SEED} 
environment variable and also use the @option{--envseed} option, for more see 
@ref{Generating random numbers}.
+
+@item --envseed
+Read the random number generator seed from the @code{GSL_RNG_SEED} environment 
variable for @option{--rowrandom} (instead of generating a different seed 
internally on every run).
+This is useful if you want a reproducible random selection of the input rows.
+For more, see @ref{Generating random numbers}.
+
+@item -b STR[,STR[,STR]]
+@itemx --noblank=STR[,STR[,STR]]
+Remove all rows in the given @emph{output} columns that have a blank value.
+Like above, the columns can be specified by their name or number (counting 
from 1).
+For example if @file{table.fits} has blank values (NaN in floating point 
types) in the @code{magnitude} and @code{sn} columns, with 
@code{--noblank=magnitude,sn}, the output will not contain any rows with blank 
values in these columns.
+
+If you want @emph{all} columns to be checked, simply set the value to 
@code{_all} (in other words: @option{--noblank=_all}).
+This mode is useful when there are many columns in the table and you want a 
``clean'' output table (with no blank values in any column): entering their 
name or number one-by-one can be buggy and frustrating.
+In this mode, no other column name should be given.
+For example if you give @option{--noblank=_all,magnitude}, then Table will 
assume that your table actually has a column named @code{_all} and 
@code{magnitude}, and if it doesn't, it will abort with an error.
+
+This option is applied just before writing the final table (after 
@option{--colmetadata} has finished).
+So in case you changed the column metadata, or added new columns, you can use 
the new names, or the newly defined column numbers.
+
+@item -m STR/INT,STR[,STR[,STR]]
+@itemx --colmetadata=STR/INT,STR[,STR[,STR]]
+Update the specified column metadata in the output table.
+This option is applied after all other column-related operations are complete, 
for example column arithmetic, or column concatenation.
+The first value (before the first comma) given to this option is the column's 
identifier.
+It can either be a counter (positive integer, counting from 1), or a name (the 
column's name in the output if this option wasn't called).
+
+After the to-be-updated column is identified, at least one other string should 
be given, with a maximum of three strings.
+The first string after the original name will the the selected column's new 
name.
+The next (optional) string will be the selected column's unit and the third 
(optional) will be its comments.
+If the two optional strings aren't given, the original column's units or 
comments will remain unchanged.
+Some examples of this option are available in the tutorials, in particular 
@ref{Working with catalogs estimating colors}.
+Here are some more specific examples
+
+@table @option
+
+@item --colmetadata=MAGNITUDE,MAG_F160W
+This will convert name of the original @code{MAGNITUDE} column to 
@code{MAG_F160W}, leaving the unit and comments unchanged.
+
+@item --colmetadata=3,MAG_F160W,mag
+This will convert name of the third column of the final output to 
@code{MAG_F160W} and the units to @code{mag}, while leaving the comments 
untouched.
+
+@item --colmetadata=MAGNITUDE,MAG_F160W,mag,"Magnitude in F160W filter"
+This will convert name of the original @code{MAGNITUDE} column to 
@code{MAG_F160W}, and the units to @code{mag} and the comments to 
@code{Magnitude in F160W filter}.
+Note the double quotations around the comment string, they are necessary to 
preserve the white-space characters within the column comment from the 
command-line, into the program (otherwise, upon reaching a white-space 
character, the shell will consider this option to be finished and cause 
un-expected behavior).
+@end table
+
+If your table is large and generated by a script, you can first do all your 
operations on your table's data and write it into a temporary file (maybe 
called @file{temp.fits}).
+Then, look into that file's metadata (with @command{asttable temp.fits -i}) to 
see the exact column positions and possible names, then add the necessary calls 
to this option to your previous call to @command{asttable}, so it writes proper 
metadata in the same run (for example in a script or Makefile).
+Recall that when a name is given, this option will update the metadata of the 
first column that matches, so if you have multiple columns with the same name, 
you can call this options multiple times with the same first argument to change 
them all to different names.
+
+Finally, if you already have a FITS table by other means (for example by 
downloading) and you merely want to update the column metadata and leave the 
data intact, it is much more efficient to directly modify the respective FITS 
header keywords with @code{astfits}, using the keyword manipulation features 
described in @ref{Keyword inspection and manipulation}.
+@option{--colmetadata} is mainly intended for scenarios where you want to edit 
the data so it will always load the full/partial dataset into memory, then 
write out the resulting datasets with updated/corrected metadata.
+@end table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@node Query,  , Table, Data containers
+@section Query
+
+@cindex IVOA
+@cindex Query
+@cindex TAP (Table Access Protocol)
+@cindex ADQL (Astronomical Data Query Language)
+@cindex Astronomical Data Query Language (ADQL)
+There are many astronomical databases available for downloading astronomical 
data.
+Most follow the International Virtual Observatory Alliance (IVOA, 
@url{https://ivoa.net}) standards (and in particular the Table Access Protocol, 
or TAP@footnote{@url{https://ivoa.net/documents/TAP}}).
+With TAP, it is possible to submit your queries via a command-line downloader 
(for example @command{curl}) to only get specific tables, targets (rows in a 
table) or measurements (columns in a table): you don't have to download the 
full table (which can be very large in some cases)!
+These customizations are done through the Astronomical Data Query Language 
(ADQL@footnote{@url{https://ivoa.net/documents/ADQL}}).
+
+Therefore, if you are sufficiently familiar with TAP and ADQL, you can easily 
custom-download any part of an online dataset.
+However, you also need to keep a record of the URLs of each database and in 
many cases, the commands will become long and hard/buggy to type on the 
command-line.
+On the other hand, most astronomers don't know TAP or ADQL at all, and are 
forced to go to the database's web page which is slow (it needs to download so 
many images, and has too much annoying information), requires manual 
interaction (further making it slow and buggy), and can't be automated.
+
+Gnuastro's Query program is designed to be the middle-man in this process: it 
provides a simple high-level interface to let you specify your constraints on 
what you want to download.
+It then internally constructs the command to download the data based on your 
inputs and runs it to download your desired data.
+Query also prints the full command before it executes it (if not called with 
@option{--quiet}).
+Also, if you ask for a FITS output table, the full command is written into its 
0-th extension along with other input parameters to query (all Gnuastro 
programs generally keep their input configuration parameters as FITS keywords 
in the zero-th output).
+You can see it with Gnuastro's Fits program, like below:
+
+@example
+$ astfits query-output.fits -h0
+@end example
+
+With the full command used to download the dataset, you only need a minimal 
knowledge of ADQL to do lower-level customizations on your downloaded dataset.
+You can simply copy that command and change the parts of the query string you 
want: ADQL is very powerful!
+For example you can ask the server to do mathematical operations on the 
columns and apply selections after those operations, or combine/match multiple 
datasets and etc.
+We will try to add high-level interfaces for such capabilities, but generally, 
don't limit yourself to the high-level operations (that can't cover 
everything!).
+
+@menu
+* Available databases::         List of available databases to Query.
+* Invoking astquery::           Inputs, outputs and configuration of Query.
+@end menu
+
+@node Available databases, Invoking astquery, Query, Query
+@subsection Available databases
+
+The current list of databases supported by Query are listed at the end of this 
section.
+To get the list of available datasets within each database, you can use the 
@option{--information} option.
+For example with the command below you can get a list of the roughly 100 
datasets that are available within the ESA Gaia server with their description:
+
+@example
+$ astquery gaia --information
+@end example
+
+@noindent
+However, other databases like VizieR host many more datasets (tens of 
thousands!).
+Therefore it is very inconvenient to get the @emph{full} information every 
time you want to find your dataset of interest (the full metadata file VizieR 
is more than 20Mb).
+In such cases, you can limit the downloaded and displayed information with the 
@code{--limitinfo} option.
+For example with the first command below, you can get all datasets relating to 
the MUSE (an instrument on the Very Large Telescope), and those that include 
Roland Bacon (Principle Investigator of MUSE) as an author (@code{Bacon, R.}).
+Recall that @option{-i} is the short format of @option{--information}.
+
+@example
+$ astquery vizier -i --limitinfo=MUSE
+$ astquery vizier -i --limitinfo="Bacon R."
+@end example
+
+Once you find the recognized name of your desired dataset, you can see the 
column information of that dataset with adding the dataset name.
+For example, with the command below you can see the column metadata in the 
@code{J/A+A/608/A2/udf10} dataset (one of the datasets in the search above) 
using this command:
+
+@example
+$ astquery vizier --dataset=J/A+A/608/A2/udf10 -i
+@end example
+
+@cindex SDSS DR12
+For very popular datasets of a database, Query provides an easier-to-remember 
short name that you can feed to @option{--dataset}.
+This short name will map to the officially recognized name of the dataset on 
the server.
+In this mode, Query will also set positional columns accordingly.
+For example most VizieR datasets have an @code{RAJ2000} column (the RA and the 
epoch of 2000) so it is the default RA column name for coordinate search (using 
@option{--center} or @option{--overlapwith}).
+However, some datasets don't have this column (for example SDSS DR12).
+So when you use the short name and Query knows about this dataset, it will 
internally set the coordinate columns that SDSS DR12 has: @code{RA_ICRS} and 
@code{DEC_ICRS}.
+Recall that you can always change the coordinate columns with @option{--ccol}.
+
+For example in the VizieR and Gaia databases, the recognized name for the 
early data release 3 data is respectively @code{I/350/gaiaedr3} and 
@code{gaiaedr3.gaia_source}.
+These technical names can be hard to remember.
+Therefore Query provides @code{gaiaedr3} (for VizieR) and @code{edr3} (for 
ESA's Gaia) shortcuts which you can give to @option{--dataset} instead.
+They will be directly mapped to the fully recognized name by Query.
+In the list below that describes the available databases, the available short 
names are also listed.
+
+@cartouche
+@noindent
+@strong{Not all datasets support TAP:} Large databases like VizieR have TAP 
access for all their datasets.
+However, smaller databases haven't implemented TAP for all their tables.
+Therefore some datasets that are searchable in their web interface may not be 
available for a TAP search.
+To see the full list of TAP-ed datasets in a database, use the 
@option{--information} (or @option{-i}) option with the dataset name like the 
command below.
+
+@example
+$ astquery astron -i
+@end example
+
+@noindent
+If your desired dataset isn't in this list, but has web-access, contact the 
database maintainers and ask them to add TAP access for it.
+After they do it, you should see the name added to the output list of the 
command above.
+@end cartouche
+
+The list of databases recognized by Query (and their names in Query) is 
described below.
+Since Query is a new member of the Gnuastro family (first available in 
Gnuastro 0.14), this list will hopefully grow significantly in the next 
releases.
+If you have any particular datasets in mind, please let us know by sending an 
email to @code{bug-gnuastro@@gnu.org}.
+If the dataset supports IVOA's TAP (Table Access Protocol), it should be very 
easy to add.
+
+
+@table @code
+
+@item astron
+@cindex ASTRON
+@cindex Radio astronomy
+The ASTRON Virtual Observatory service (@url{https://vo.astron.nl}) is a 
database focused on radio astronomy data and images, primarily those collected 
by ASTRON itself.
+A query to @code{astron} is submitted to 
@code{https://vo.astron.nl/__system__/tap/run/tap/sync}.
+
+Here is the list of short names for dataset(s) in ASTRON's VO service:
+@itemize
+@item
+@code{tgssadr --> tgssadr.main}
+@end itemize
+
+@item gaia
+@cindex Gaia catalog
+@cindex Catalog, Gaia
+@cindex Database, Gaia
+The Gaia project (@url{https://www.cosmos.esa.int/web/gaia}) database which is 
a large collection of star positions on the celestial sphere, as well as 
peculiar velocities, parallaxes and magnitudes in some bands among many others.
+Besides scientific studies (like studying resolved stellar populations in the 
Galaxy and its halo), Gaia is also invaluable for raw data calibrations, like 
astrometry.
+A query to @code{gaia} is submitted to 
@code{https://gea.esac.esa.int/tap-server/tap/sync}.
+
+Here is the list of short names for popular datasets within Gaia:
+@itemize
+@item
+@code{edr3 --> gaiaedr3.gaia_source}
+@item
+@code{dr2 --> gaiadr2.gaia_source}
+@item
+@code{dr1 --> gaiadr1.gaia_source}
+@item
+@code{tycho2 --> public.tycho2}
+@item
+@code{hipparcos --> public.hipparcos}
+@end itemize
+
+@item ned
+@cindex NASA/IPAC Extragalactic Database (NED)
+@cindex NED (NASA/IPAC Extragalactic Database)
+The NASA/IPAC Extragalactic Database (NED, @url{http://ned.ipac.caltech.edu}) 
is a fusion database, integrating the information about extra-galactic sources 
from many large sky surveys into a single catalog.
+It covers the full spectrum, from Gamma rays to radio frequencies and is 
updated when new data arrives.
+A query to @code{ned} is submitted to 
@code{https://ned.ipac.caltech.edu/tap/sync}.
+
+Currently NED only has its main dataset for TAP access (shown below), more 
datasets will be added for TAP access in the future.
+@itemize
+@item
+@code{objdir --> NEDTAP.objdir}
+@end itemize
+
+@item vizier
+@cindex VizieR
+@cindex CDS, VizieR
+@cindex Catalog, Vizier
+@cindex Database, VizieR
+Vizier (@url{https://vizier.u-strasbg.fr}) is arguably the largest catalog 
database in astronomy: containing more than 20500 catalogs as of mid January 
2021.
+Almost all published catalogs in major projects, and even the tables in many 
papers are archived and accessible here.
+For example VizieR also has a full copy of the Gaia database mentioned below, 
with some additional standardized columns (like RA and Dec in J2000).
+
+The current implementation of @option{--limitinfo} only looks into the 
description of the datasets, but since VizieR is so large, there is still a lot 
of room for improvement.
+Until then, if @option{--limitinfo} isn't sufficient, you can use VizieR's own 
web-based search for your desired dataset: 
@url{http://cdsarc.u-strasbg.fr/viz-bin/cat}
+
+
+Because VizieR curates such a diverse set of data from tens of thousands of 
projects and aims for interoperability between them, the column names in VizieR 
may not be identical to the column names in the surveys' own databases (Gaia in 
the example above).
+A query to @code{vizier} is submitted to 
@code{http://tapvizier.u-strasbg.fr/TAPVizieR/tap/sync}.
+
+@cindex 2MASS All-Sky Catalog
+@cindex AKARI/FIS All-Sky Survey
+@cindex AllWISE Data Release
+@cindex AAVSO Photometric All Sky Survey, DR9
+@cindex CatWISE 2020 catalog
+@cindex Dark Energy Survey data release 1
+@cindex GAIA Data Release (2 or 3)
+@cindex All-sky Survey of GALEX DR5
+@cindex Naval Observatory Merged Astrometric Dataset
+@cindex Pan-STARRS Data Release 1
+@cindex SDSS Photometric Catalogue, Release 12
+@cindex Whole-Sky USNO-B1.0 Catalog
+@cindex U.S. Naval Observatory CCD Astrograph Catalog
+@cindex Band-merged unWISE Catalog
+@cindex WISE All-Sky data Release
+Here is the list of short names for popular datasets within VizieR (sorted 
alphabetically by their short name).
+Please feel free to suggest other major catalogs (covering a wide area or 
commonly used in your field)..
+For details on each dataset with necessary citations, and links to web pages, 
look into their details with their ViziR names in 
@url{https://vizier.u-strasbg.fr/viz-bin/VizieR}.
+@itemize
+@item
+@code{2mass --> II/246/out} (2MASS All-Sky Catalog)
+@item
+@code{akarifis --> II/298/fis} (AKARI/FIS All-Sky Survey)
+@item
+@code{allwise --> II/328/allwise} (AllWISE Data Release)
+@item
+@code{apass9 --> II/336/apass9} (AAVSO Photometric All Sky Survey, DR9)
+@item
+@code{catwise --> II/365/catwise} (CatWISE 2020 catalog)
+@item
+@code{des1 --> II/357/des_dr1} (Dark Energy Survey data release 1)
+@item
+@code{gaiadr2 --> I/345/gaia2} (GAIA Data Release 2)
+@item
+@code{gaiaedr3 --> I/350/gaiaedr3} (GAIA early Data Release 3)
+@item
+@code{galex5 --> II/312/ais} (All-sky Survey of GALEX DR5)
+@item
+@code{nomad --> I/297/out} (Naval Observatory Merged Astrometric Dataset)
+@item
+@code{panstarrs1 --> II/349/ps1} (Pan-STARRS Data Release 1).
+@item
+@code{ppmxl --> I/317/sample} (Positions and proper motions on the ICRS)
+@item
+@code{sdss12 --> V/147/sdss12} (SDSS Photometric Catalogue, Release 12)
+@item
+@code{usnob1 --> I/284/out} (Whole-Sky USNO-B1.0 Catalog)
+@item
+@code{ucac5 --> I/340/ucac5} (5th U.S. Naval Obs. CCD Astrograph Catalog)
+@item
+@code{unwise --> II/363/unwise} (Band-merged unWISE Catalog)
+@item
+@code{wise --> II/311/wise} (WISE All-Sky data Release)
+@end itemize
+@end table
+
+
+
+
+@node Invoking astquery,  , Available databases, Query
+@subsection Invoking Query
+
+Query provides a high-level interface to downloading subsets of data from 
databases.
+The executable name is @file{astquery} with the following general template
+
+@example
+$ astquery DATABASE-NAME [OPTION...] ...
+@end example
+
+@noindent
+One line examples:
+
+@example
+
+## Information about all datasets in ESA's GAIA database:
+$ astquery gaia --information
+
+## Only show catalogs in VizieR that have 'MUSE' in their
+## description. The '-i' is short for '--information'.
+$ astquery vizier -i --limitinfo=MUSE
+
+## List of columns in 'J/A+A/608/A2/udf10' (one of the above).
+$ astquery vizier --dataset=J/A+A/608/A2/udf10 -i
+
+## ID, RA and Dec of all Gaia sources within an image.
+$ astquery gaia --dataset=edr3 --overlapwith=image.fits \
+           -csource_id,ra,dec
+
+## RA, Dec and Spectroscopic redshifts of objects in SDSS DR12
+## spectroscopic redshift that overlap with 'image.fits'.
+$ astquery vizier --dataset=sdss12 --overlapwith=image.fits \
+           -cRA_ICRS,DE_ICRS,zsp --range=zsp,1e-10,inf
+
+## All columns of all entries in the Gaia eDR3 catalog (hosted at
+## VizieR) within 1 arc-minute of the given coordinate.
+$ astquery vizier --dataset=I/350/gaiaedr3 --output=my-gaia.fits \
+           --center=113.8729761,31.9027152 --radius=1/60 \
+
+## Similar to above, but only ID, RA and Dec columns for objects with
+## magnitude range 10 to 15. In VizieR, this column is called 'Gmag'.
+$ astquery vizier --dataset=I/350/gaiaedr3 --output=my-gaia.fits \
+           --center=113.8729761,31.9027152 --radius=1/60 \
+           --range=Gmag,10:15 -cEDR3Name,RAJ2000,DEJ2000
+@end example
+
+Query takes a single argument which is the name of the database.
+For the full list of available databases and accessing them, see 
@ref{Available databases}.
+There are two methods to query the databases, each is more fully discussed in 
its option's description below.
+@itemize
+@item
+@strong{Low-level:}
+With @option{--query} you can directly give a raw query statement that is 
recognized by the database.
+This is very low level and will require a good knowledge of the database's 
query language, but of course, it is much more powerful.
+If this option is given, the raw string is directly passed to the server and 
all other constraints/options (for Query's high-level interface) are ignored.
+@item
+@strong{High-level:}
+With the high-level options (like @option{--column}, @option{--center}, 
@option{--radius}, @option{--range} and other constraining options below), the 
low-level query will be constructed automatically for the particular database.
+This method is only limited to the generic capabilities that Query provides 
for all servers.
+So @option{--query} is more powerful, however, in this mode, you don't need 
any knowledge of the database's query language.
+You can see the internally generated query on the terminal (if 
@option{--quiet} is not used) or in the 0-th extension of the output (if its a 
FITS file).
+This full command contains the internally generated query.
+@end itemize
+
+The name of the downloaded output file can be set with @option{--output}.
+The requested output format can have any of the @ref{Recognized table formats} 
(currently @file{.txt} or @file{.fits}).
+Like all Gnuastro programs, if the output is a FITS file, the zero-th/first 
HDU of the output will contain all the command-line options given to Query as 
well as the full command used to access the server.
+When @option{--output} is not set, the output name will be in the format of 
@file{NAME-STRING.fits}, where @file{NAME} is the name of the database and 
@file{STRING} is a randomly selected 6-character set of numbers and alphabetic 
characters.
+With this feature, a second run of @command{astquery} that isn't called with 
@option{--output} will not over-write an already downloaded one.
+Generally, when calling Query more than once, it is recommended to set an 
output name for each call based on your project's context.
+
+The outputs of Query will have a common output format, irrespective of the 
used database.
+To achieve this, Query will ask the databases to provide a FITS table output 
(for larger tables, FITS can consume much less download volume).
+After downloading is complete, the raw downloaded file will be read into 
memory once by Query, and written into the file given to @option{--output}.
+The raw downloaded file will be deleted by default, but can be preserved with 
the @option{--keeprawdownload} option.
+This strategy avoids unnecessary surprises depending on database.
+For example some databases can download a compressed FITS table, even though 
we ask for FITS.
+But with the strategy above, the final output will be an uncompressed FITS 
file.
+The metadata that is added by Query (including the full download command) is 
also very useful for future usage of the downloaded data.
+Unfortunately many databases don't write the input queries into their 
generated tables.
+
+@table @option
+
+@item --dry-run
+Only print the final download command to contact the server, don't actually 
run it.
+This option is good when you want to check the finally constructed query or 
download options given to the download program.
+You may also want to use the constructed command as a base to do further 
customizations on it and run it yourself.
+
+@item -k
+@itemx --keeprawdownload
+Don't delete the raw downloaded file from the database.
+The name of the raw download will have a @file{OUTPUT-raw-download.fits} 
format.
+Where @file{OUTPUT} is either the base-name of the final output file (without 
a suffix).
+
+@item -i
+@itemx --information
+Print the information of all datasets (tables) within a database or all 
columns within a database.
+When @option{--dataset} is specified, the latter mode (all column information) 
is downloaded and printed and when its not defined, all dataset information 
(within the database) is printed.
+
+Some databases (like VizieR) contain tens of thousands of datasets, so you can 
limit the downloaded and printed information for available databases with the 
@option{--limitinfo} option (described below).
+Dataset descriptions are often large and contain a lot of text (unlike column 
descriptions).
+Therefore when printing the information of all datasets within a database, the 
information (e.g., database name) will be printed on separate lines before the 
description.
+However, when printing column information, the output has the same format as a 
similar option in Table (see @ref{Invoking asttable}).
+
+Important note to consider: the printed order of the datasets or columns is 
just for displaying in the printed output.
+You cannot ask for datasets or columns based on the printed order, you need to 
use dataset or column names.
+
+@item -L STR
+@itemx --limitinfo=STR
+Limit the information that is downloaded and displayed (with 
@option{--information}) to those that have the string given to this option in 
their description.
+Note that @emph{this is case-sensitive}.
+This option is only relevant when @option{--information} is also called.
+
+Databases may have thousands (or tens of thousands) of datasets.
+Therefore just the metadata (information) to show with @option{--information} 
can be tens of megabytes (for example the full VizieR metadata file is about 
23Mb as of January 2021).
+Once downloaded, it can also be hard to parse manually.
+With @option{--limitinfo}, only the metadata of datasets that contain this 
string @emph{in their description} will be downloaded and displayed, greatly 
improving the speed of finding your desired dataset.
+
+@item -Q "STR"
+@itemx --query="STR"
+Directly specify the query to be passed onto the database.
+The queries will generally contain space and other meta-characters, so we 
recommend placing the query within quotations.
+
+@item -s STR
+@itemx --dataset=STR
+The dataset to query within the database (not compatible with 
@option{--query}).
+This option is mandatory when @option{--query} or @option{--information} 
aren't provided.
+You can see the list of available datasets within a database using 
@option{--information} (possibly supplemented by @option{--limitinfo}).
+The output of @option{--information} will contain the recognized name of the 
datasets within that database.
+You can pass the recognized name directly to this option.
+For more on finding and using your desired database, see @ref{Available 
databases}.
+
+@item -c STR
+@itemx --column=STR[,STR[,...]]
+The column name(s) to retrieve from the dataset in the given order (not 
compatible with @option{--query}).
+If not given, all the dataset's columns for the selected rows will be queried 
(which can be large!).
+This option can take multiple values in one instance (for example 
@option{--column=ra,dec,mag}), or in multiple instances (for example 
@option{-cra -cdec -cmag}), or mixed (for example @option{-cra,dec -cmag}).
+
+In case, you don't know the full list of the dataset's column names a-priori, 
and you don't want to download all the columns (which can greatly decrease your 
download speed), you can use the @option{--information} option combined with 
the @option{--dataset} option, see @ref{Available databases}.
+
+@item -H INT
+@itemx --head=INT
+Only ask for the first @code{INT} rows of the finally selected columns, not 
all the rows.
+This can be good when your search can result a large dataset, but before 
downloading the full volume, you want to see the top rows and get a feeling of 
what the whole dataset looks like.
+
+@item -v FITS
+@itemx --overlapwith=FITS
+File name of FITS file containing an image (in the HDU given by 
@option{--hdu}) to use for identifying the region to query in the give database 
and dataset.
+Based on the image's WCS and pixel size, the sky coverage of the image is 
estimated and values to the @option{--center}, @option{--width} will be 
calculated internally.
+Hence this option cannot be used with @code{--center}, @code{--width} or 
@code{--radius}.
+Also, since it internally generates the query, it can't be used with 
@code{--query}.
+
+Note that if the image has WCS distortions and the reference point for the WCS 
is not within the image, the WCS will not be well-defined.
+Therefore the resulting catalog may not overlap, or correspond to a 
larger/small area in the sky.
+
+@item -C FLT,FLT
+@itemx --center=FLT,FLT
+The spatial center position (mostly RA and Dec) to use for the automatically 
generated query (not compatible with @option{--query}).
+The given values will be compared to two columns in the database to 
find/return rows within a certain region around this center position will be 
requested and downloaded.
+Pre-defined RA and Dec column names are defined in Query for every database, 
however you can use @option{--ccol} to select other columns to use instead.
+The region can either be a circle and the point (configured with 
@option{--radius}) or a box/rectangle around the point (configured with 
@option{--width}).
+
+@item --ccol=STR,STR
+The name of the coordinate-columns in the dataset to compare with the values 
given to @option{--center}.
+Query will use its internal defaults for each dataset (for example 
@code{RAJ2000} and @code{DEJ2000} for VizieR data).
+But each dataset is treated separately and it isn't guaranteed that these 
columns exist in all datasets.
+Also, more than one coordinate system/epoch may be present in a dataset and 
you can use this option to construct your spatial constraint based on the 
others coordinate systems/epochs.
 
+@item -r FLT
+@itemx --radius=FLT
+The radius about the requested center to use for the automatically generated 
query (not compatible with @option{--query}).
+The radius is in units of degrees, but you can use simple division with this 
option directly on the command-line.
+For example if you want a radius of 20 arc-minutes or 20 arc-seconds, you can 
use @option{--radius=20/60} or @option{--radius=20/3600} respectively (which is 
much more human-friendly than @code{0.3333} or @code{0.005556}).
+
+@item -w FLT[,FLT]
+@itemx --width=FLT[,FLT]
+The square (or rectangle) side length (width) about the requested center to 
use for the automatically generated query (not compatible with 
@option{--query}).
+If only one value is given to @code{--width} the region will be a square, but 
if two values are given, the widths of the query box along each dimension will 
be different.
+The value(s) is (are) in the same units as the coordinate column (see 
@option{--ccol}, usually RA and Dec which are degrees).
+You can use simple division for each value directly on the command-line if you 
want relatively small (and more human-friendly) sizes.
+For example if you want your box to be 1 arc-minutes along the RA and 2 
arc-minutes along Dec, you can use @option{--width=1/60,2/60}.
+
+@item -g STR,FLT,FLT
+@itemx --range=STR,FLT,FLT
+The column name and numerical range (inclusive) of acceptable values in that 
column (not compatible with @option{--query}).
+This option can be called multiple times for applying range limits on many 
columns in one call (thus greatly reducing the download size).
+For example when used on the ESA gaia database, you can use 
@code{--range=phot_g_mean_mag,10:15} to only get rows that have a value between 
10 and 15 (inclusive on both sides) in the @code{phot_g_mean_mag} column.
+
+If you want all rows larger, or smaller, than a certain number, you can use 
@code{inf}, or @code{-inf} as the first or second values respectively.
+For example, if you want objects with SDSS spectroscopic redshifts larger than 
2 (from the VizieR @code{sdss12} database), you can use 
@option{--range=zsp,2,inf}
+
+If you want the interval to not be inclusive on both sides, you can run 
@code{astquery} once and get the command that it executes.
+Then you can edit it to be non-inclusive on your desired side.
+
+@item -b STR[,STR]
+@item --noblank=STR[,STR]
+Only ask for rows that don't have a blank value in the @code{STR} column.
+This option can be called many times, and each call can have multiple column 
names (separated by a comma or @key{,}).
+For example if you want the retrieved rows to not have a blank value in 
columns @code{A}, @code{B}, @code{C} and @code{D}, you can use 
@command{--noblank=A -bB,C,D}.
+
+@item --sort=STR[,STR]
+Ask for the server to sort the downloaded data based on the given columns.
+For example let's assume your desired catalog has column @code{Z} for redshift 
and column @code{MAG_R} for magnitude in the R band.
+When you call @option{--sort=Z,MAG_R}, it will primarily sort the columns 
based on the redshift, but if two objects have the same redshift, they will be 
sorted by magnitude.
+You can add as many columns as you like for higher-level sorting.
 @end table
 
 
@@ -9039,6 +10903,9 @@ See @option{--head} for more.
 
 
 
+
+
+
 @node Data manipulation, Data analysis, Data containers, Top
 @chapter Data manipulation
 
@@ -9144,7 +11011,7 @@ In Image mode there are two options to define the 
vertices of a region to crop:
 The former is lower-level (doesn't accept floating point vertices, and only a 
rectangular region can be defined), it is also only available in Image mode.
 Please see @ref{Crop section syntax} for a full description of this method.
 
-The latter option (@option{--polygon}) is a higher-level method to define any 
convex polygon (with any number of vertices) with floating point values.
+The latter option (@option{--polygon}) is a higher-level method to define any 
polygon (with any number of vertices) with floating point values.
 Please see the description of this option in @ref{Invoking astcrop} for its 
syntax.
 @end table
 
@@ -9206,7 +11073,9 @@ This string is given to the @option{--section} option.
 Therefore, the pixels along the first axis that are @mymath{\geq}@command{X1} 
and @mymath{\leq}@command{X2} will be included in the cropped image.
 The same goes for the second axis.
 Note that each different term will be read as an integer, not a float.
-This is a low-level option, for a higher-level way to specify region (any 
polygon, not just a box), please see the @option{--polygon} option in @ref{Crop 
options}.
+
+The reason it only accepts integers is that @option{--section} is a low-level 
option (which is also very fast!).
+For a higher-level way to specify region (any polygon, not just a box), please 
see the @option{--polygon} option in @ref{Crop options}.
 Also note that in the FITS standard, pixel indexes along each axis start from 
unity(1) not zero(0).
 
 @cindex Crop section format
@@ -9376,14 +11245,16 @@ If you want an even sided crop, you can run Crop 
afterwards with @option{--secti
 
 @item -l STR
 @itemx --polygon=STR
-String of crop polygon vertices.
-Note that currently only convex polygons should be used.
-In the future we will make it work for all kinds of polygons.
-Convex polygons are polygons that do not have an internal angle more than 180 
degrees.
+String of vertices to define a polygon to crop.
+The vertices are used to define the polygon in the same order given to this 
option.
+When the vertices are not necessarily ordered in the proper order (for example 
one vertice in a square comes after its diagonal opposite), you can add the 
@option{--polygonsort} option which will attempt to sort the vertices before 
cropping.
+Note that for concave polygons, sorting is not recommended because there is no 
unique solution, for more, see the description under @option{--polygonsort}.
+
 This option can be used both in the image and WCS modes, see @ref{Crop modes}.
 The cropped image will be the size of the rectangular region that completely 
encompasses the polygon.
 By default all the pixels that are outside of the polygon will be set as blank 
values (see @ref{Blank pixels}).
-However, if @option{--outpolygon} is called all pixels internal to the 
vertices will be set to blank.
+However, if @option{--polygonout} is called all pixels internal to the 
vertices will be set to blank.
+In WCS-mode, you may provide many FITS images/tiles: Crop will stitch them to 
produce this cropped region, then apply the polygon.
 
 The syntax for the polygon vertices is similar to, and simpler than, that for 
@option{--section}.
 In short, the dimensions of each coordinate are separated by a comma (@key{,}) 
and each vertex is separated by a colon (@key{:}).
@@ -9391,16 +11262,15 @@ You can define as many vertices as you like.
 If you would like to use space characters between the dimensions and vertices 
to make them more human-readable, then you have to put the value to this option 
in double quotation marks.
 
 For example, let's assume you want to work on the deepest part of the WFC3/IR 
images of Hubble Space Telescope eXtreme Deep Field (HST-XDF).
-@url{https://archive.stsci.edu/prepds/xdf/, According to the 
webpage}@footnote{@url{https://archive.stsci.edu/prepds/xdf/}} the deepest part 
is contained within the coordinates:
+@url{https://archive.stsci.edu/prepds/xdf/, According to the web 
page}@footnote{@url{https://archive.stsci.edu/prepds/xdf/}} the deepest part is 
contained within the coordinates:
 
 @example
 [ (53.187414,-27.779152), (53.159507,-27.759633),
   (53.134517,-27.787144), (53.161906,-27.807208) ]
 @end example
 
-They have provided mask images with only these pixels in the WFC3/IR images, 
but what if you also need to work on the same region in the full resolution ACS 
images? Also what if you want to use the CANDELS data for the shallow region? 
Running Crop with @option{--polygon} will easily pull out this region of the 
image for you irrespective of the resolution.
+They have provided mask images with only these pixels in the WFC3/IR images, 
but what if you also need to work on the same region in the full resolution ACS 
images? Also what if you want to use the CANDELS data for the shallow region? 
Running Crop with @option{--polygon} will easily pull out this region of the 
image for you, irrespective of the resolution.
 If you have set the operating mode to WCS mode in your nearest configuration 
file (see @ref{Configuration files}), there is no need to call 
@option{--mode=wcs} on the command line.
-You may also provide many FITS images/tiles and Crop will stitch them to 
produce this cropped region:
 
 @example
 $ astcrop --mode=wcs desired-filter-image(s).fits           \
@@ -9428,7 +11298,7 @@ $ v=$(awk 'NR==4' ds9.reg | sed -e's/polygon(//'        \
 $ astcrop --mode=wcs image.fits --polygon=$v
 @end example
 
-@item --outpolygon
+@item --polygonout
 Keep all the regions outside the polygon and mask the inner ones with blank 
pixels (see @ref{Blank pixels}).
 This is practically the inverse of the default mode of treating polygons.
 Note that this option only works when you have only provided one input image.
@@ -9437,11 +11307,46 @@ This can lead to a very large area if large surveys 
like COSMOS are used.
 So Crop will abort and notify you.
 In such cases, it is best to crop out the larger region you want, then mask 
the smaller region with this option.
 
+@item --polygonsort
+Sort the given set of vertices to the @option{--polygon} option.
+For a concave polygon it will sort the vertices correctly, however for a 
convex polygon it there is no unique sorting, so be careful because the crop 
may not be what you expected.
+
+@cindex Convex polygons
+@cindex Concave polygons
+@cindex Polygons, Convex
+@cindex Polygons, Concave
+Polygons come in two classes: convex and concave (or generally, non-convex!), 
see below for a demonstration.
+Convex polygons are those where all inner angles are less than 180 degrees.
+By contrast, a convex polygon is one where an inner angle may be more than 180 
degrees.
+
+@example
+            Concave Polygon        Convex Polygon
+
+             D --------C          D------------- C
+              \        |        E /              |
+               \E      |          \              |
+               /       |           \             |
+              A--------B             A ----------B
+@end example
+
 @item -s STR
 @itemx --section=STR
 Section of the input image which you want to be cropped.
 See @ref{Crop section syntax} for a complete explanation on the syntax 
required for this input.
 
+@item -C FITS/TXT
+@itemx --catalog=FITS/TXT
+File name of catalog for making multiple crops from the input images/cubes.
+The catalog can be in any of Gnuastro's recognized @ref{Recognized table 
formats}.
+The columns containing the coordinates for the crop centers can be specified 
with the @option{--coordcol} option (using column names or numbers, see 
@ref{Selecting table columns}).
+The catalog can also contain the name of each crop, you can specify the column 
containing the name with the @option{--namecol}.
+
+@item --cathdu=STR/INT
+The HDU (extension) containing the catalog (if the file given to 
@option{--catalog} is a FITS file).
+This can either be the HDU name (if it has one) or number (counting from 0).
+By default (if this option is not given), the second HDU will be used 
(equivalent to @option{--cathdu=1}.
+For more on how to specify the HDU, see the explanation of the @option{--hdu} 
option in @ref{Input output options}.
+
 @item -x STR/INT
 @itemx --coordcol=STR/INT
 The column in a catalog to read as a coordinate.
@@ -9490,6 +11395,10 @@ One case where this might be helpful is when besides 
the science images, you wan
 So in one run, you can set the input images to the science images and 
@option{--suffix=_s.fits}.
 In the next run you can set the weight images as input and 
@option{--suffix=_w.fits}.
 
+@item --primaryimghdu
+Write the output into the primary (0-th) HDU/extension of the output.
+By default, like all Gnuastro's default outputs, no data is written in the 
primary extension because the FITS standard suggests keeping that extension 
free of data and only for meta data.
+
 @item -b
 @itemx --noblank
 Pixels outside of the input image that are in the crop box will not be used.
@@ -9528,7 +11437,7 @@ on how many crops were requested, see @ref{Crop modes}:
 @itemize
 @item
 When a catalog is given, the value of the @option{--output} (see @ref{Common 
options}) will be read as the directory to store the output cropped images.
-Hence if it doesn't already exist, Crop will abort with an error of a ``No 
such file or directory'' error.
+Hence if it doesn't already exist, Crop will abort with an ``No such file or 
directory'' error.
 
 The crop file names will consist of two parts: a variable part (the row number 
of each target starting from 1) along with a fixed string which you can set 
with the @option{--suffix} option.
 Optionally, you may also use the @option{--namecol} option to define a column 
in the input catalog to use as the file name instead of numbers.
@@ -9538,6 +11447,10 @@ When only one crop is desired, the value to 
@option{--output} will be read as a
 If no output is specified or if it is a directory, the output file name will 
follow the automatic output names of Gnuastro, see @ref{Automatic output}: The 
string given to @option{--suffix} will be replaced with the @file{.fits} suffix 
of the input.
 @end itemize
 
+By default, as suggested by the FITS standard and implemented in all Gnuastro 
programs, the first/primary extension of the output files will only contain 
meta data.
+The cropped images/cubes will be written into the 2nd HDU of their respective 
FITS file (which is actually counted as @code{1} because HDU counting starts 
from @code{0}).
+However, if you want the cropped data to be written into the primary (0-th) 
HDU, run Crop with the @option{--primaryimghdu} option.
+
 The header of each output cropped image will contain the names of the input 
image(s) it was cut from.
 If a name is longer than the 70 character space that the FITS standard allows 
for header keyword values, the name will be cut into several keywords from the 
nearest slash (@key{/}).
 The keywords have the following format: @command{ICFn_m} (for Crop File).
@@ -9650,65 +11563,224 @@ The conditional operators will return pixel, or 
numerical values of 0 (false) or
 
 @item +
 Addition, so ``@command{4 5 +}'' is equivalent to @mymath{4+5}.
+For example in the command below, the value 20000 is added to each pixel's 
value in @file{image.fits}:
+@example
+$ astarithmetic 20000 image.fits +
+@end example
+You can also use this operator is to sum the values of one pixel in two images 
(which have to be the same size).
+For example in the commands below (which are identical, see paragraph after 
the commands), each pixel of @file{sum.fits} is the sum of the same pixel's 
values in @file{a.fits} and @file{b.fits}.
+@example
+$ astarithmetic a.fits b.fits + -h1 -h1 --output=sum.fits
+$ astarithmetic a.fits b.fits + -g1     --output=sum.fits
+@end example
+The HDU/extension has to be specified for each image with @option{-h}.
+However, if the HDUs are the same in all inputs, you can use @option{-g} to 
only specify the HDU once
 
 @item -
 Subtraction, so ``@command{4 5 -}'' is equivalent to @mymath{4-5}.
+Usage of this operator is similar to @command{+} operator, for example:
+@example
+$ astarithmetic 20000 image.fits -
+$ astarithmetic a.fits b.fits - -g1 --output=sub.fits
+@end example
 
 @item x
 Multiplication, so ``@command{4 5 x}'' is equivalent to @mymath{4\times5}.
+For example in the command below, the value of each output pixel is 5 times 
its value in @file{image.fits}:
+@example
+$ astarithmetic image.fits 5 x
+@end example
+And you can multiply the value of each pixel in two images, like this:
+@example
+$ astarithmetic a.fits a.fits x -g1 –output=multip.fits
+@end example
 
 @item /
 Division, so ``@command{4 5 /}'' is equivalent to @mymath{4/5}.
+Like the multiplication, for example
+@example
+$ astarithmetic image.fits 5 -h1 /
+$ astarithmetic a.fits b.fits / -g1 –output=div.fits
+@end example
 
 @item %
-Modulo (remainder), so ``@command{3 2 %}'' is equivalent to @mymath{1}.
-Note that the modulo operator only works on integer types.
+Modulo (remainder), so ``@command{3 2 %}'' will return @mymath{1}.
+Note that the modulo operator only works on integer types (see @ref{Numeric 
data types}).
+This operator is therefore not defined for most processed astronomical 
astronomical images that have floating-point value.
+However it is useful in labeled images, for example @ref{Segment output}).
+In such cases, each pixel is the integer label of the object it is associated 
with hence with the example command below, we can change the labels to only be 
between 1 and 4 and decrease all objects on the image to 4/5th (all objects 
with a label that is a multiple of 5 will be set to 0).
+@example
+$ astarithmetic label.fits 5 1 %
+@end example
 
 @item abs
 Absolute value of first operand, so ``@command{4 abs}'' is equivalent to 
@mymath{|4|}.
+For example the output of the command bellow will not have any negative pixels 
(all negative pixels will be multiplied by @mymath{-1} to become positive)
+@example
+$ astarithmetic image.fits abs
+@end example
+
 
 @item pow
-First operand to the power of the second, so ``@command{4.3 5f pow}'' is 
equivalent to @mymath{4.3^{5}}.
-Currently @code{pow} will only work on single or double precision floating 
point numbers or images.
-To be sure that a number is read as a floating point (even if it doesn't have 
any non-zero decimals) put an @code{f} after it.
+First operand to the power of the second, so ``@command{4.3 5 pow}'' is 
equivalent to @mymath{4.3^{5}}.
+For example with the command below all pixels will be squared
+@example
+$ astarithmetic image.fits 2 pow
+@end example
 
 @item sqrt
 The square root of the first operand, so ``@command{5 sqrt}'' is equivalent to 
@mymath{\sqrt{5}}.
+Since the square root is only defined for positive values, any negative-valued 
pixel will become NaN (blank).
 The output will have a floating point type, but its precision is determined 
from the input: if the input is a 64-bit floating point, the output will also 
be 64-bit.
 Otherwise, the output will be 32-bit floating point (see @ref{Numeric data 
types} for the respective precision).
 Therefore if you require 64-bit precision in estimating the square root, 
convert the input to 64-bit floating point first, for example with @code{5 
float64 sqrt}.
+For example each pixel of the output of the command below will be the square 
root of that pixel in the input.
+@example
+$ astarithmetic image.fits sqrt
+@end example
+
+If you just want to scale an image with negative values using this operator 
(for better visual inspection, and the actual values don't matter for you), you 
can subtract the image from its minimum value, then take its square root:
+
+@example
+$ astarithmetic image.fits image.fits minvalue - sqrt -g1
+@end example
+
+Alternatively, to avoid reading the image into memory two times, you can use 
the @option{set-} operator to read it into the variable @option{i} and use 
@option{i} two times to speed up the operation (described below):
+
+@example
+$ astarithmetic image.fits set-i i i minvalue - sqrt
+@end example
 
 @item log
 Natural logarithm of first operand, so ``@command{4 log}'' is equivalent to 
@mymath{ln(4)}.
-The output type is determined from the input, see the explanation under 
@command{sqrt} for more.
+Negative pixels will become NaN, and the output type is determined from the 
input, see the explanation under @command{sqrt} for more on these features.
+For example the command below will take the natural logarithm of every pixel 
in the input.
+@example
+$ astarithmetic image.fits log --output=log.fits
+@end example
 
 @item log10
-Base-10 logarithm of first operand, so ``@command{4 log10}'' is equivalent to 
@mymath{\log(4)}.
-The output type is determined from the input, see the explanation under 
@command{sqrt} for more.
+Base-10 logarithm of first popped operand, so ``@command{4 log}'' is 
equivalent to @mymath{log_{10}(4)}.
+Negative pixels will become NaN, and the output type is determined from the 
input, see the explanation under @command{sqrt} for more on these features.
+For example the command below will take the base-10 logarithm of every pixel 
in the input.
+@example
+$ astarithmetic image.fits log10
+@end example
 
-@item minvalue
-Minimum (non-blank) value in the top operand on the stack, so 
``@command{a.fits minvalue}'' will push the minimum pixel value in this image 
onto the stack.
-Therefore this operator is mainly intended for data (for example images), if 
the top operand is a number, this operator just returns it without any change.
-So note that when this operator acts on a single image, the output will no 
longer be an image, but a number.
-The output of this operand is in the same type as the input.
+@item  sin
+@itemx cos
+@itemx tan
+@cindex Trigonometry
+Basic trigonometric functions.
+They take one operand, in units of degrees.
 
-@item maxvalue
-Maximum (non-blank) value of first operand in the same type, similar to 
@command{minvalue}.
+@item  asin
+@itemx acos
+@itemx atan
+Inverse trigonometric functions.
+They take one operand and the returned values are in units of degrees.
 
-@item numbervalue
-Number of non-blank elements in first operand in the @code{uint64} type, 
similar to @command{minvalue}.
+@item atan2
+Inverse tangent (output in units of degrees) that uses the signs of the input 
coordinates to distinguish between the quadrants.
+This operator therefore needs two operands: the first popped operand is 
assumed to be the X axis position of the point, and the second popped operand 
is its Y axis coordinate.
 
-@item sumvalue
-Sum of non-blank elements in first operand in the @code{float32} type, similar 
to @command{minvalue}.
+For example see the commands below.
+To be more clear, we are using Table's @ref{Column arithmetic} which uses 
exactly the same internal library function as the Arithmetic program for images.
+We are showing the results for four points in the four quadrants of the 2D 
space (if you want to try running them, you don't need to type/copy the parts 
after @key{#}).
+The first point (2,2) is in the first quadrant, therefore the returned angle 
is 45 degrees.
+But the second, third and fourth points are in the quadrants of the same 
order, and the returned angles reflect the quadrant.
 
-@item meanvalue
-Mean value of non-blank elements in first operand in the @code{float32} type, 
similar to @command{minvalue}.
+@example
+$ echo " 2  2" | asttable -c'arith $2 $1 atan2'   # -->   45
+$ echo " 2 -2" | asttable -c'arith $2 $1 atan2'   # -->  -45
+$ echo "-2 -2" | asttable -c'arith $2 $1 atan2'   # --> -135
+$ echo "-2  2" | asttable -c'arith $2 $1 atan2'   # -->  135
+@end example
+
+However, if you simply use the classic arc-tangent operator (@code{atan}) for 
the same points, the result will only be in two quadrants as you see below:
+
+@example
+$ echo " 2  2" | asttable -c'arith $2 $1 / atan'  # -->   45
+$ echo " 2 -2" | asttable -c'arith $2 $1 / atan'  # -->  -45
+$ echo "-2 -2" | asttable -c'arith $2 $1 / atan'  # -->   45
+$ echo "-2  2" | asttable -c'arith $2 $1 / atan'  # -->  -45
+@end example
+
+@item  sinh
+@itemx cosh
+@itemx tanh
+@cindex Hyperbolic functions
+Hyperbolic sine, cosine, and tangent.
+These operators take a single operand.
+
+@item  asinh
+@itemx acosh
+@itemx atanh
+Inverse Hyperbolic sine, cosine, and tangent.
+These operators take a single operand.
+
+@item minvalue
+Minimum value in the first popped operand, so ``@command{a.fits minvalue}'' 
will push the minimum pixel value in this image onto the stack.
+When this operator acts on a single image, the output (operand that is put 
back on the stack) will no longer be an image, but a number.
+The output of this operand is in the same type as the input.
+This operator is mainly intended for multi-element datasets (for example 
images or data cubes), if the popped operand is a number, it will just return 
it without any change.
+
+Note that when the final remaining/output operand is a single number, it is 
printed onto the standard output.
+For example with the command below the minimum pixel value in 
@file{image.fits} will be printed in the terminal:
+@example
+$ astarithmetic image.fits minvalue
+@end example
+
+However, the output above also includes a lot of extra information that are 
not relevant in this context.
+If you just want the final number, run Arithmetic in quiet mode:
+@example
+$ astarithmetic image.fits minvalue -q
+@end example
+
+Also see the description of @option{sqrt} for other example usages of this 
operator.
+
+@item maxvalue
+Maximum value of first operand in the same type, similar to 
@command{minvalue}, see the description there for more.
+For example
+@example
+$ astarithmetic image.fits maxvalue -q
+@end example
+
+@item numbervalue
+Number of non-blank elements in first operand in the @code{uint64} type (since 
it is always a positive integer, see @ref{Numeric data types}).
+Its usage is similar to @command{minvalue}, for example
+@example
+$ astarithmetic image.fits numbervalue -q
+@end example
+
+@item sumvalue
+Sum of non-blank elements in first operand in the @code{float32} type.
+Its usage is similar to @command{minvalue}, for example
+@example
+$ astarithmetic image.fits sumvalue -q
+@end example
+
+@item meanvalue
+Mean value of non-blank elements in first operand in the @code{float32} type.
+Its usage is similar to @command{minvalue}, for example
+@example
+$ astarithmetic image.fits meanvalue -q
+@end example
 
 @item stdvalue
-Standard deviation of non-blank elements in first operand in the 
@code{float32} type, similar to @command{minvalue}.
+Standard deviation of non-blank elements in first operand in the 
@code{float32} type.
+Its usage is similar to @command{minvalue}, for example
+@example
+$ astarithmetic image.fits stdvalue -q
+@end example
 
 @item medianvalue
-Median of non-blank elements in first operand with the same type, similar to 
@command{minvalue}.
+Median of non-blank elements in first operand with the same type.
+Its usage is similar to @command{minvalue}, for example
+@example
+$ astarithmetic image.fits medianvalue -q
+@end example
+
 
 @cindex NaN
 @item min
@@ -9743,31 +11815,72 @@ Therefore, if the input was an integer, C's internal 
type conversion will be use
 For each pixel, find the maximum value in all given datasets.
 The output will have the same type as the input.
 This operator is called similar to the @command{min} operator, please see 
there for more.
+For example
+@example
+$ astarithmetic a.fits b.fits c.fits 3 min -omax.fits
+@end example
+
 
 @item number
 For each pixel count the number of non-blank pixels in all given datasets.
 The output will be an unsigned 32-bit integer datatype (see @ref{Numeric data 
types}).
 This operator is called similar to the @command{min} operator, please see 
there for more.
+Note that some datasets may have blank values (which are also ignored in all 
similar operators like @command{min}, @command{sum}, @command{mean} or 
@command{median}).
+Hence final pixel values of this operator will not, in general, be equal to 
the number of inputs.
+For example
+@example
+$ astarithmetic a.fits b.fits c.fits 3 number -onum.fits
+@end example
 
 @item sum
 For each pixel, calculate the sum in all given datasets.
 The output will have the a single-precision (32-bit) floating point type.
 This operator is called similar to the @command{min} operator, please see 
there for more.
+For example
+@example
+$ astarithmetic a.fits b.fits c.fits 3 sum -ostack-sum.fits
+@end example
 
 @item mean
 For each pixel, calculate the mean in all given datasets.
 The output will have the a single-precision (32-bit) floating point type.
 This operator is called similar to the @command{min} operator, please see 
there for more.
+For example
+@example
+$ astarithmetic a.fits b.fits c.fits 3 mean -ocoadd-mean.fits
+@end example
 
 @item std
 For each pixel, find the standard deviation in all given datasets.
 The output will have the a single-precision (32-bit) floating point type.
 This operator is called similar to the @command{min} operator, please see 
there for more.
+For example
+@example
+$ astarithmetic a.fits b.fits c.fits 3 std -ostd.fits
+@end example
 
 @item median
 For each pixel, find the median in all given datasets.
 The output will have the a single-precision (32-bit) floating point type.
 This operator is called similar to the @command{min} operator, please see 
there for more.
+For example
+@example
+$ astarithmetic a.fits b.fits c.fits 3 mean \
+                --output=stack-median.fits
+@end example
+
+@item quantile
+For each pixel, find the quantile from all given datasets.
+The output will have the same numeric data type and size as the input datasets.
+Besides the input datasets, the quantile operator also needs a single 
parameter (the requested quantile).
+The parameter should be the first popped operand, with a value between (and 
including) 0 and 1.
+The second popped operand must be the number of datasets to use.
+
+In the example below, the first-popped operand (@command{0.7}) is the 
quantile, the second-popped operand (@command{3}) is the number of datasets to 
pop.
+
+@example
+astarithmetic a.fits b.fits c.fits 3 0.7 quantile
+@end example
 
 @item sigclip-number
 For each pixel, find the sigma-clipped number (after removing outliers) in all 
given datasets.
@@ -9778,7 +11891,7 @@ This operator is very similar to @command{min}, with the 
exception that it expec
 The first popped operand is the termination criteria and the second is the 
multiple of @mymath{\sigma}.
 
 For example in the command below, the first popped operand (@command{0.2}) is 
the sigma clipping termination criteria.
-If the termination criteria is larger than 1 it is interpreted as the number 
of clips to do.
+If the termination criteria is larger than, or equal to, 1 it is interpreted 
as the number of clips to do.
 But if it is between 0 and 1, then it is the tolerance level on the standard 
deviation (see @ref{Sigma clipping}).
 The second popped operand (@command{5}) is the multiple of sigma to use in 
sigma-clipping.
 The third popped operand (@command{10}) is number of datasets that will be 
used (similar to the first popped operand to @command{min}).
@@ -9791,16 +11904,28 @@ astarithmetic a.fits b.fits c.fits 3 5 0.2 
sigclip-number
 For each pixel, find the sigma-clipped median in all given datasets.
 The output will have the a single-precision (32-bit) floating point type.
 This operator is called similar to the @command{sigclip-number} operator, 
please see there for more.
+For example
+@example
+astarithmetic a.fits b.fits c.fits 3 5 0.2 sigclip-median
+@end example
 
 @item sigclip-mean
 For each pixel, find the sigma-clipped mean in all given datasets.
 The output will have the a single-precision (32-bit) floating point type.
 This operator is called similar to the @command{sigclip-number} operator, 
please see there for more.
+For example
+@example
+astarithmetic a.fits b.fits c.fits 3 5 0.2 sigclip-mean
+@end example
 
 @item sigclip-std
 For each pixel, find the sigma-clipped standard deviation in all given 
datasets.
 The output will have the a single-precision (32-bit) floating point type.
 This operator is called similar to the @command{sigclip-number} operator, 
please see there for more.
+For example
+@example
+astarithmetic a.fits b.fits c.fits 3 5 0.2 sigclip-std
+@end example
 
 @item filter-mean
 Apply mean filtering (or @url{https://en.wikipedia.org/wiki/Moving_average, 
moving average}) on the input dataset.
@@ -9857,9 +11982,49 @@ Apply a @mymath{\sigma}-clipped median filtering onto 
the input dataset.
 This operator and its necessary operands are almost identical to 
@code{filter-sigclip-mean}, except that after @mymath{\sigma}-clipping, the 
median value (which is less affected by outliers than the mean) is added back 
to the stack.
 
 @item interpolate-medianngb
-Interpolate all the blank elements of the second popped operand with the 
median of its nearest non-blank neighbors.
+Interpolate the blank elements of the second popped operand with the median of 
nearest non-blank neighbors to each.
 The number of the nearest non-blank neighbors used to calculate the median is 
given by the first popped operand.
-Note that the distance of the nearest non-blank neighbors is irrelevant in 
this interpolation.
+
+The distance of the nearest non-blank neighbors is irrelevant in this 
interpolation.
+The neighbors of each blank pixel will be parsed in expanding circular rings 
(for 2D images) or spherical surfaces (for 3D cube) and each non-blank element 
over them is stored in memory.
+When the requested number of non-blank neighbors have been found, their median 
is used to replace that blank element.
+For example the line below replaces each blank element with the the median of 
the nearest 5 pixels.
+
+@example
+$ astarithmetic image.fits 5 interpolate-medianngb
+@end example
+
+When you want to interpolate blank regions and you want each blank region to 
have a fixed value (for example the centers of saturated stars) this operator 
is not good.
+Because the pixels used to interpolate various parts of the region differ.
+For such scenarios, you may use @code{interpolate-maxofregion} or 
@code{interpolate-inofregion} (described below).
+
+@item interpolate-minngb
+Similar to @code{interpolate-medianngb}, but will fill the blank values of the 
dataset with the minimum value of the nearest neighbors.
+
+@item interpolate-maxngb
+Similar to @code{interpolate-medianngb}, but will fill the blank values of the 
dataset with the maximum value of the nearest neighbors.
+One useful implementation of this operator is to fill the saturated pixels of 
stars in images.
+
+@item interpolate-minofregion
+Interpolate all blank regions (consisting of many blank pixels that are 
touching) in the second popped operand with the minimum value of the pixels 
that are immediately bordering that region (a single value).
+The first popped operand is the connectivity (see description in 
@command{connected-components}).
+
+For example with the command below all the connected blank regions of 
@file{image.fits} will be filled.
+Its an image (2D dataset), so a 2 connectivity means that the independent 
blank regions are defined by 8-connected neighbors.
+If connectivity was 1, the regions would be defined by 4-connectivity: blank 
regions that may only be touching on the corner of one pixel would be 
identified as separate regions.
+
+@example
+$ astarithmetic image.fits 2 interpolate-minofregion
+@end example
+
+@item interpolate-maxofregion
+@cindex Saturated pixels
+Similar to @code{interpolate-minofregion}, but the maximum is used to fill the 
blank regions.
+
+This operator can be useful in filling saturated pixels in stars for example.
+Recall that the @option{interpolate-maxngb} operator looks for the maximum 
value with a given number of neighboring pixels and is more useful in small 
noisy regions.
+Therefore as the blank regions become larger, @option{interpolate-maxngb} can 
cause a fragmentation in the connected blank region because the nearest 
neighbor to one part of the blank region, may not fall within the pixels 
searched for the other regions.
+With this option, the size of the blank region is irrelevant: all the pixels 
bordering the blank region are parsed and their maximum value is used for the 
whole region.
 
 @item collapse-sum
 Collapse the given dataset (second popped operand), by summing all elements 
along the first popped operand (a dimension in FITS standard: counting from 
one, from fastest dimension).
@@ -9929,23 +12094,54 @@ The unique elements of the dataset will be stored in a 
single-dimensional datase
 Recall that by default, single-dimensional datasets are stored as a table 
column in the output.
 But you can use @option{--onedasimage} or @option{--onedonstdout} to 
respectively store them as a single-dimensional FITS array/image, or to print 
them on the standard output.
 
+Although you can use this operator on the floating point dataset, due to 
floating-point errors it may give non-reasonable values: because the tenth 
digit of the decimal point is also considered although it may be statistically 
meaningless, see @ref{Numeric data types}.
+It is therefore better/recommended to use it on the integer dataset like the 
labeled images of @ref{Segment output} where each pixel has the integer label 
of the object/clump it is associated with.
+For example let's assume you have cropped a region of a larger labeled image 
and want to find the labels/objects that are within the crop.
+With this operator, this job is trivial:
+@example
+$ astarithmetic seg-crop.fits unique
+@end example
+
 @item erode
 @cindex Erosion
 Erode the foreground pixels (with value @code{1}) of the input dataset (second 
popped operand).
 The first popped operand is the connectivity (see description in 
@command{connected-components}).
-Erosion is simply a flipping of all foreground pixels (to background; with 
value @code{0}) that are ``touching'' background pixels.
+Erosion is simply a flipping of all foreground pixels (with value @code{1}) to 
background (with value @code{0}) that are ``touching'' background pixels.
 ``Touching'' is defined by the connectivity.
-In effect, this carves off the outer borders of the foreground, making them 
thinner.
-This operator assumes a binary dataset (all pixels are @code{0} and @code{1}).
+
+In effect, this operator ``carves off'' the outer borders of the foreground, 
making them thinner.
+This operator assumes a binary dataset (all pixels are @code{0} or @code{1}).
+For example, imagine that you have an astronomical image with a mean/sky value 
of 0 units and a standard deviation (@mymath{\sigma}) of 100 units and many 
galaxies in it.
+With the first command below, you can apply a threshold of @mymath{2\sigma} on 
the image (by only keeping pixels that are greater than 200 using the 
@command{gt} operator).
+The output of thresholding the image is a binary image (each pixel is either 
smaller or equal to the threshold or larger than it).
+You can then erode the binary image with the second command below to remove 
very small false positives  (one or two pixel peaks).
+@example
+$ astarithmetic image.fits 100 gt -obinary.fits
+$ astarithmetic binary.fits 2 erode -oout.fits
+@end example
+
+In fact, you can merge these operations into one command thanks to the reverse 
polish notation (see @ref{Reverse polish notation}):
+@example
+$ astarithmetic image.fits 100 gt 2 erode -oout.fits
+@end example
+
+To see the effect of connectivity, try this:
+@example
+$ astarithmetic image.fits 100 gt 1 erode -oout-con-1.fits
+@end example
 
 @item dilate
 @cindex Dilation
-Dilate the foreground pixels (with value @code{1}) of the input dataset 
(second popped operand).
+Dilate the foreground pixels (with value @code{1}) of the binary input dataset 
(second popped operand).
 The first popped operand is the connectivity (see description in 
@command{connected-components}).
-Erosion is simply a flipping of all background pixels (with value @code{0}) to 
foreground that are ``touching'' foreground pixels.
+Dilation is simply a flipping of all background pixels (with value @code{0}) 
to foreground (with value @code{1}) that are ``touching'' foreground pixels.
 ``Touching'' is defined by the connectivity.
 In effect, this expands the outer borders of the foreground.
 This operator assumes a binary dataset (all pixels are @code{0} and @code{1}).
+The usage is similar to @code{erode}, for example:
+@example
+$ astarithmetic binary.fits 2 erode -oout.fits
+@end example
 
 @item connected-components
 @cindex Connected components
@@ -9972,56 +12168,113 @@ If your input dataset doesn't have a binary type, but 
you know all its values ar
 
 @item fill-holes
 Flip background (0) pixels surrounded by foreground (1) in a binary dataset.
-This operator takes two operands (similar to @code{connected-components}): the 
first popped operand is the connectivity (to define a hole) and the second is 
the binary (0 or 1 valued) dataset to fill holes in.
+This operator takes two operands (similar to @code{connected-components}): the 
second is the binary (0 or 1 valued) dataset to fill holes in and the first 
popped operand is the connectivity (to define a hole).
+Imagine that in your dataset there are some holes with zero value inside the 
objects with one value (for example the output of the thresholding example of 
@command{erode}) and you want to fill the holes:
+@example
+$ astarithmetic binary.fits 2 fill-holes
+@end example
 
 @item invert
-Invert an unsigned integer dataset.
+Invert an unsigned integer dataset (won't work on other data types, see 
@ref{Numeric data types}).
 This is the only operator that ignores blank values (which are set to be the 
maximum values in the unsigned integer types).
 
 This is useful in cases where the target(s) has(have) been imaged in 
absorption as raw formats (which are unsigned integer types).
 With this option, the maximum value for the given type will be subtracted from 
each pixel value, thus ``inverting'' the image, so the target(s) can be treated 
as emission.
 This can be useful when the higher-level analysis methods/tools only work on 
emission (positive skew in the noise, not negative).
+@example
+$ astarithmetic image.fits invert
+@end example
 
 @item lt
-Less than: If the second popped (or left operand in infix notation, see 
@ref{Reverse polish notation}) value is smaller than the first popped operand, 
then this function will return a value of 1, otherwise it will return a value 
of 0.
+Less than: creates a binary output (values either 0 or 1) where each pixel 
will be 1 if the second popped operand is smaller than the first popped operand 
and 0 otherwise.
 If both operands are images, then all the pixels will be compared with their 
counterparts in the other image.
+For example:
+@example
+$ astarithmetic image1.fits image2.fits -g1 lt
+@end example
 If only one operand is an image, then all the pixels will be compared with the 
single value (number) of the other operand.
+For example:
+@example
+$ astaithmetic image1.fits 1000 lt
+@end example
 Finally if both are numbers, then the output is also just one number (0 or 1).
-When the output is not a single number, it will be stored as an @code{unsigned 
char} type.
+@example
+$ astarithmetic 4 5 lt
+@end example
+
 
 @item le
 Less or equal: similar to @code{lt} (`less than' operator), but returning 1 
when the second popped operand is smaller or equal to the first.
+For example
+@example
+$ astaithmetic image1.fits 1000 le
+@end example
 
 @item gt
 Greater than: similar to @code{lt} (`less than' operator), but returning 1 
when the second popped operand is greater than the first.
+For example
+@example
+$ astaithmetic image1.fits 1000 gt
+@end example
 
 @item ge
 Greater or equal: similar to @code{lt} (`less than' operator), but returning 1 
when the second popped operand is larger or equal to the first.
+For example
+@example
+$ astaithmetic image1.fits 1000 ge
+@end example
 
 @item eq
-Equality: similar to @code{lt} (`less than' operator), but returning 1 when 
the two popped operands are equal (to double precision floating point
-accuracy).
+Equality: similar to @code{lt} (`less than' operator), but returning 1 when 
the two popped operands are equal (to double precision floating point accuracy).
+@example
+$ astaithmetic image1.fits 1000 eq
+@end example
 
 @item ne
 Non-Equality: similar to @code{lt} (`less than' operator), but returning 1 
when the two popped operands are @emph{not} equal (to double precision floating 
point accuracy).
+@example
+$ astaithmetic image1.fits 1000 ne
+@end example
 
 @item and
 Logical AND: returns 1 if both operands have a non-zero value and 0 if both 
are zero.
-Both operands have to be the same kind: either both images or both numbers.
+Both operands have to be the same kind: either both images or both numbers and 
it mostly makes meaningful values when the inputs are binary (with pixel values 
of 0 or 1).
+@example
+$ astarithmetic image1.fits image2.fits -g1 and
+@end example
+
+For example if you only want to see which pixels in an image have a value 
@emph{between} 50 (greater equal, or inclusive) and 200 (less than, or 
exclusive), you can use this command:
+@example
+$ astarithmetic image.fits set-i i 50 ge i 200 lt and
+@end example
 
 @item or
 Logical OR: returns 1 if either one of the operands is non-zero and 0 only 
when both operators are zero.
 Both operands have to be the same kind: either both images or both numbers.
+The usage is similar to @code{and}.
+
+For example if you only want to see which pixels in an image have a value 
@emph{outside of} -100 (greater equal, or inclusive) and 200 (less than, or 
exclusive), you can use this command:
+@example
+$ astarithmetic image.fits set-i i -100 lt i 200 ge or
+@end example
 
 @item not
-Logical NOT: returns 1 when the operand is zero and 0 when the operand is 
non-zero.
+Logical NOT: returns 1 when the operand is 0 and 0 when the operand is 
non-zero.
 The operand can be an image or number, for an image, it is applied to each 
pixel separately.
+For example if you want to know which pixels are not blank, you can use not on 
the output of the @command{isblank} operator described below:
+@example
+$ astarithmetic image.fits isblank not
+@end example
 
 @cindex Blank pixel
 @item isblank
 Test for a blank value (see @ref{Blank pixels}).
 In essence, this is very similar to the conditional operators: the output is 
either 1 or 0 (see the `less than' operator above).
 The difference is that it only needs one operand.
+For example:
+@example
+$ astarithmetic image.fits isblank
+@end example
 Because of the definition of a blank pixel, a blank value is not even equal to 
itself, so you cannot use the equal operator above to select blank pixels.
 See the ``Blank pixels'' box below for more on Blank pixels in Arithmetic.
 
@@ -10136,6 +12389,26 @@ The internal conversion of C will be used.
 Convert the type of the popped operand to 64-bit (double precision) floating 
point (see @ref{Numeric data types}).
 The internal conversion of C will be used.
 
+@item size
+Size of the dataset along a given FITS/Fortran dimension (counting from 1).
+The desired dimension should be the first popped operand and the dataset must 
be the second popped operand.
+The output will be a single unsigned integer (dimensions cannot be negative).
+For example, the following command will produce the size of the first 
extension/HDU (the default HDU) of @file{a.fits} along the second FITS axis.
+
+@example
+astarithmetic a.fits 2 size
+@end example
+
+@item makenew
+Create a new dataset that only has zero values.
+The number of dimensions is read as the first popped operand and the number of 
elements along each dimension are the next popped operand (in reverse of the 
popping order).
+The type of the new dataset is an unsigned 8-bit integer and all pixel values 
have a value of zero.
+For example, if you want to create a new 100 by 200 pixel image, you can run 
this command:
+
+@example
+astarithmetic 100 200 2 makenew
+@end example
+
 @item set-AAA
 Set the characters after the dash (@code{AAA} in the case shown here) as a 
name for the first popped operand on the stack.
 The named dataset will be freed from memory as soon as it is no longer needed, 
or if the name is reset to refer to another dataset later in the command.
@@ -10238,7 +12511,8 @@ $ astarithmetic image.fits image.fits - 
--out=skysub.fits           \
                 --hdu=1 --hdu=4
 
 ## Add two images, then divide them by 2 (2 is read as floating point):
-$ astarithmetic image1.fits image2.fits + 2f / --out=average.fits
+## Note that without the '.0', the '2' will be read/used as an integer.
+$ astarithmetic image1.fits image2.fits + 2.0 / --out=average.fits
 
 ## Use Arithmetic's average operator:
 $ astarithmetic image1.fits image2.fits average --out=average.fits
@@ -10292,8 +12566,8 @@ Use the value to this option as the HDU of all input 
FITS files.
 This option is very convenient when you have many input files and the dataset 
of interest is in the same HDU of all the files.
 When this option is called, any values given to the @option{--hdu} option 
(explained above) are ignored and will not be used.
 
-@item -w STR
-@itemx --wcsfile STR
+@item -w FITS
+@itemx --wcsfile FITS
 FITS Filename containing the WCS structure that must be written to the output.
 The HDU/extension should be specified with @option{--wcshdu}.
 
@@ -10336,7 +12610,8 @@ But when Arithmetic is called with 
@option{--dontdelete}, it will appended the d
 Arithmetic accepts two kinds of input: images and numbers.
 Images are considered to be any of the inputs that is a file name of a 
recognized type (see @ref{Arguments}) and has more than one element/pixel.
 Numbers on the command-line will be read into the smallest type (see 
@ref{Numeric data types}) that can store them, so @command{-2} will be read as 
a @code{char} type (which is signed on most systems and can thus keep negative 
values), @command{2500} will be read as an @code{unsigned short} (all positive 
numbers will be read as unsigned), while @code{3.1415926535897} will be read as 
a @code{double} and @code{3.14} will be read as a @code{float}.
-To force a number to be read as float, add a @code{f} after it, so 
@command{5f} will be added to the stack as @code{float} (see @ref{Reverse 
polish notation}).
+To force a number to be read as float, put a @code{.} after it (possibly 
followed by a zero for easier readability), or add an @code{f} after it.
+Hence while @command{5} will be read as an integer, @command{5.}, 
@command{5.0} or @command{5f} will be added to the stack as @code{float} (see 
@ref{Reverse polish notation}).
 
 Unless otherwise stated (in @ref{Arithmetic operators}), the operators can 
deal with numeric multiple data types (see @ref{Numeric data types}).
 For example in ``@command{a.fits b.fits +}'', the image types can be 
@code{long} and @code{float}.
@@ -10479,7 +12754,7 @@ Technically speaking, only if the kernel is flipped the 
process is known @emph{C
 If it isn't it is known as @emph{Correlation}.
 
 To be a weighted average, the sum of the weights (the pixels in the kernel) 
have to be unity.
-This will have the consequence that the convolved image of an object and 
unconvolved object will have the same brightness (see @ref{Flux Brightness and 
magnitude}), which is natural, because convolution should not eat up the object 
photons, it only disperses them.
+This will have the consequence that the convolved image of an object and 
unconvolved object will have the same brightness (see @ref{Brightness flux 
magnitude}), which is natural, because convolution should not eat up the object 
photons, it only disperses them.
 
 
 
@@ -10493,7 +12768,7 @@ Here we will explain the problem in the spatial domain.
 For a discussion of this problem from the frequency domain perspective, see 
@ref{Edges in the frequency domain}.
 The problem originates from the fact that on the edges, in 
practice@footnote{Because we assumed the overlapping pixels outside the input 
image have a value of zero.}, the sum of the weights we use on the actual image 
pixels is not unity.
 For example, as discussed above, a profile in the center of an image will have 
the same brightness before and after convolution.
-However, for partially imaged profile on the edge of the image, the brightness 
(sum of its pixel fluxes within the image, see @ref{Flux Brightness and 
magnitude}) will not be equal, some of the flux is going to be `eaten' by the 
edges.
+However, for partially imaged profile on the edge of the image, the brightness 
(sum of its pixel fluxes within the image, see @ref{Brightness flux magnitude}) 
will not be equal, some of the flux is going to be `eaten' by the edges.
 
 If you ran @command{$ make check} on the source files of Gnuastro, you can see 
the this effect by comparing the @file{convolve_frequency.fits} with 
@file{convolve_spatial.fits} in the @file{./tests/} directory.
 In the spatial domain, by default, no assumption will be made about pixels 
outside of the image or any blank pixels in the image.
@@ -11204,8 +13479,8 @@ Only within Convolve, there is an option to disable 
normalization, see @ref{Invo
 The two options to specify a kernel file name and its extension are shown 
below.
 These are common between all the programs that will do convolution.
 @table @option
-@item -k STR
-@itemx --kernel=STR
+@item -k FITS
+@itemx --kernel=FITS
 The convolution kernel file name.
 The @code{BITPIX} (data type) value of this file can be any standard type and 
it does not necessarily have to be normalized.
 Several operations will be done on the kernel image prior to the program's 
processing:
@@ -11644,7 +13919,7 @@ This technique requires no functions, it is thus 
non-parametric.
 It is also the closest we can get (make least assumptions) to what actually 
happens on the detector pixels.
 The basic idea is that you reverse-transform each output pixel to find which 
pixels of the input image it covers and what fraction of the area of the input 
pixels are covered.
 To find the output pixel value, you simply sum the value of each input pixel 
weighted by the overlapfraction (between 0 to 1) of the output pixel and that 
input pixel.
-Through this process, pixels are treated as an area not as a point (which is 
how detectors create the image), also the brightness (see @ref{Flux Brightness 
and magnitude}) of an object will be left completely unchanged.
+Through this process, pixels are treated as an area not as a point (which is 
how detectors create the image), also the brightness (see @ref{Brightness flux 
magnitude}) of an object will be left completely unchanged.
 
 If there are very high spatial-frequency signals in the image (for example 
fringes) which vary on a scale smaller than your output image pixel size, pixel 
mixing can cause 
ailiasing@footnote{@url{http://en.wikipedia.org/wiki/Aliasing}}.
 So if the input image has fringes, they have to be calculated and removed 
separately (which would naturally be done in any astronomical  application).
@@ -11844,6 +14119,8 @@ For example getting general or specific statistics of 
the dataset (with @ref{Sta
 * Segment::                     Segment detections based on signal structure.
 * MakeCatalog::                 Catalog from input and labeled images.
 * Match::                       Match two datasets.
+* Sort FITS files by night::    Sort and classify images in separate nights.
+* SAO DS9 region files from table::  Table's positional columns into DS9 
region file.
 @end menu
 
 @node Statistics, NoiseChisel, Data analysis, Data analysis
@@ -11861,13 +14138,14 @@ The Statistics program is designed for such 
situations.
 
 @menu
 * Histogram and Cumulative Frequency Plot::  Basic definitions.
+* 2D Histograms::               Plotting the distribution of two variables.
 * Sigma clipping::              Definition of @mymath{\sigma}-clipping.
 * Sky value::                   Definition and derivation of the Sky value.
 * Invoking aststatistics::      Arguments and options to Statistics.
 @end menu
 
 
-@node Histogram and Cumulative Frequency Plot, Sigma clipping, Statistics, 
Statistics
+@node Histogram and Cumulative Frequency Plot, 2D Histograms, Statistics, 
Statistics
 @subsection Histogram and Cumulative Frequency Plot
 
 @cindex Histogram
@@ -11906,8 +14184,236 @@ Formally, an interval/bin between a and b is 
represented by [a, b).
 When the over-all range of the dataset is specified (with the 
@option{--greaterequal}, @option{--lessthan}, or @option{--qrange} options), 
the acceptable values of the dataset are also defined with a similar 
inclusive-exclusive manner.
 But when the range is determined from the actual dataset (none of these 
options is called), the last element in the dataset is included in the last 
bin's count.
 
+@node 2D Histograms, Sigma clipping, Histogram and Cumulative Frequency Plot, 
Statistics
+@subsection 2D Histograms
+@cindex 2D histogram
+@cindex Histogram, 2D
+In @ref{Histogram and Cumulative Frequency Plot} the concept of histograms 
were introduced on a single dataset.
+But they are only useful for viewing the distribution of a single variable 
(column in a table).
+In many contexts, the distribution of two variables in relation to each other 
may be of interest.
+For example the color-magnitude diagrams in astronomy, where the horizontal 
axis is the luminosity or magnitude of an object, and the vertical axis is the 
color.
+Scatter plots are useful to see these relations between the objects of 
interest when the number of the objects is small.
+
+As the density of points in the scatter plot increases, the points will fall 
over each other and just make a large connected region hide potentially 
interesting behaviors/correlations in the densest regions.
+This is where 2D histograms can become very useful.
+A 2D histogram is composed of 2D bins (boxes or pixels), just as a 1D 
histogram consists of 1D bins (lines).
+The number of points falling within each box/pixel will then be the value of 
that box.
+Added with a color-bar, you can now clearly see the distribution independent 
of the density of points (for example you can even visualize it in log-scale if 
you want).
+
+Gnuastro's Statistics program has the @option{--histogram2d} option for this 
task.
+It takes a single argument (either @code{table} or @code{image}) that 
specifies the format of the output 2D histogram.
+The two formats will be reviewed separately in the sub-sections below.
+But let's start with the generalities that are common to both (related to the 
input, not the output).
+
+You can specify the two columns to be shown using the @option{--column} (or 
@option{-c}) option.
+So if you want to plot the color-magnitude diagram from a table with the 
@code{MAG-R} column on the horizontal and @code{COLOR-G-R} on the vertical 
column, you can use @option{--column=MAG-r,COLOR-G-r}.
+The number of bins along each dimension can be set with @option{--numbins} 
(for first input column) and @option{--numbins2} (for second input column).
+
+Without specifying any range, the full range of values will be used in each 
dimension.
+If you only want to focus on a certain interval of the values in the columns 
in any dimension you can use the @option{--greaterequal} and 
@option{--lessthan} options to limit the values along the first/horizontal 
dimension and @option{--greaterequal2} and @option{--lessthan2} options for the 
second/vertical dimension.
+
+@menu
+* 2D histogram as a table::     Format and usage in table format.
+* 2D histogram as an image::    Format and usage in image format
+@end menu
+
+@node 2D histogram as a table, 2D histogram as an image, 2D Histograms, 2D 
Histograms
+@subsubsection 2D histogram as a table
+
+When called with the @option{--histogram=table} option, Statistics will output 
a table file with three columns that have the information of every box as a 
column.
+If you asked for @option{--numbins=N} and @option{--numbins2=M}, all three 
columns will have @mymath{M\times N} rows (one row for every box/pixel of the 
2D histogram).
+The first and second columns are the position of the box along the first and 
second dimensions.
+The third column has the number of input points that fall within that 
box/pixel.
+
+For example, you can make high-quality plots within your paper (using the same 
@LaTeX{} engine, thus blending very nicely with your text) using 
@url{https://ctan.org/pkg/pgfplots, PGFPlots}.
+Below you can see one such minimal example, using your favorite text editor, 
save it into a file, make the two small corrections in it, then run the 
commands shown at the top.
+This assumes that you have @LaTeX{} installed, if not the steps to install a 
minimally sufficient @LaTeX{} package on your system, see the respective 
section in @ref{Bootstrapping dependencies}.
+
+The two parts that need to be corrected are marked with '@code{%% <--}': the 
first one (@code{XXXXXXXXX}) should be replaced by the value to the 
@option{--numbins} option which is the number of bins along the first dimension.
+The second one (@code{FILE.txt}) should be replaced with the name of the file 
generated by Statistics.
+
+@example
+%% Replace 'XXXXXXXXX' with your selected number of bins in the first
+%% dimension.
+%%
+%% Then run these commands to build the plot in a LaTeX command.
+%%    mkdir tikz
+%%    pdflatex -shell-escape -halt-on-error plot.tex
+\documentclass@{article@}
+
+%% Load PGFPlots and set it to build the figure separately in a 'tikz'
+%% directory (which has to exist before LaTeX is run). This
+%% "externalization" is very useful to include the commands of multiple
+%% plots in the middle of your paper/report, but also have the plots
+%% separately to use in slides or other scenarios.
+\usepackage@{pgfplots@}
+\usetikzlibrary@{external@}
+\tikzexternalize
+\tikzsetexternalprefix@{tikz/@}
+
+%% Start the document
+\begin@{document@}
+
+  You can actually write a full paper here and include many figures!
+  Feel free to change this text.
+
+  %% Define the colormap.
+  \pgfplotsset@{
+    /pgfplots/colormap=@{coldredux@}@{
+      [1cm]
+      rgb255(0cm)=(255,255,255)
+      rgb255(2cm)=(0,192,255)
+      rgb255(4cm)=(0,0,255)
+      rgb255(6cm)=(0,0,0)
+    @}
+  @}
+
+  %% Draw the plot.
+  \begin@{tikzpicture@}
+    \small
+    \begin@{axis@}[
+      width=\linewidth,
+      view=@{0@}@{90@},
+      colorbar horizontal,
+      xlabel=X axis,
+      ylabel=Y axis,
+      ylabel shift=-0.1cm,
+      colorbar style=@{at=@{(0,1.01)@}, anchor=south west,
+                      xticklabel pos=upper@},
+    ]
+      \addplot3[
+        surf,
+        shader=flat corner,
+        mesh/ordering=rowwise,
+        mesh/cols=XXXXXXXXX,     %% <-- Number of bins in 1st column.
+      ] file @{FILE.txt@};         %% <-- Name of aststatistics output.
+
+  \end@{axis@}
+\end@{tikzpicture@}
+
+\end@{document@}
+@end example
+
+@node 2D histogram as an image,  , 2D histogram as a table, 2D Histograms
+@subsubsection 2D histogram as an image
+
+When called with the @option{--histogram=image} option, Statistics will output 
a FITS file with an image/array extension.
+If you asked for @option{--numbins=N} and @option{--numbins2=M} the image will 
have a size of @mymath{N\times M} pixels (one pixel per 2D bin).
+Also, the FITS image will have a linear WCS that is scaled to the 2D bin size 
along each dimension.
+So when you hover your mouse over any part of the image with a FITS viewer 
(for example SAO DS9), besides the number of points in each pixel, you can 
directly also see ``coordinates'' of the pixels along the two axes.
+You can also use the optimized and fast FITS viewer features for many aspects 
of visually inspecting the distributions (which we won't go into further).
+
+@cindex Color-magnitude diagram
+@cindex Diagram, Color-magnitude
+For example let's assume you want to derive the color-magnitude diagram (CMD) 
of the @url{http://uvudf.ipac.caltech.edu, UVUDF survey}.
+You can run the first command below to download the table with magnitudes of 
objects in many filters and run the second command to see general column 
metadata after it is downloaded.
+
+@example
+$ wget http://asd.gsfc.nasa.gov/UVUDF/uvudf_rafelski_2015.fits.gz
+$ asttable uvudf_rafelski_2015.fits.gz -i
+@end example
+
+Let's assume you want to find the color to be between the @code{F606W} and 
@code{F775W} filters (roughly corresponding to the g and r filters in 
ground-based imaging).
+However, the original table doesn't have color columns (there would be too 
many combinations!).
+Therefore you can use the @ref{Column arithmetic} feature of Gnuastro's Table 
program for deriving a new table with the @code{F775W} magnitude in one column 
and the difference between the @code{F606W} and @code{F775W} on the other 
column.
+With the second command, you can see the actual values if you like.
+
+@example
+$ asttable uvudf_rafelski_2015.fits.gz -cMAG_F775W \
+           -c'arith MAG_F606W MAG_F775W -' \
+           --colmetadata=ARITH_1,F606W-F775W,"AB mag" -ocmd.fits
+$ asttable cmd.fits
+@end example
+
+@noindent
+You can now construct your 2D histogram as a @mymath{100\times100} pixel FITS 
image with this command (assuming you want @code{F775W} magnitudes between 22 
and 30, colors between -1 and 3 and 100 bins in each dimension).
+Note that without the @option{--manualbinrange} option the range of each axis 
will be determined by the values within the columns (which may be larger or 
smaller than your desired large).
+
+@example
+aststatistics cmd.fits -cMAG_F775W,F606W-F775W --histogram2d=image \
+              --numbins=100  --greaterequal=22  --lessthan=30 \
+              --numbins2=100 --greaterequal2=-1 --lessthan2=3 \
+              --manualbinrange --output=cmd-2d-hist.fits
+@end example
+
+@noindent
+If you have SAO DS9, you can now open this FITS file as a normal FITS image, 
for example with the command below.
+Try hovering/zooming over the pixels: not only will you see the number of 
objects in the UVUDF catalog that fall in each bin, but you also see the 
@code{F775W} magnitude and color of that pixel also.
+
+@example
+$ ds9 cmd-2d-hist.fits -cmap sls -zoom to fit
+@end example
+
+@noindent
+With the first command below, you can activate the grid feature of DS9 to 
actually see the coordinate grid, as well as values on each line.
+With the second command, DS9 will even read the labels of the axises and use 
them to generate an almost publication-ready plot.
+
+@example
+$ ds9 cmd-2d-hist.fits -cmap sls -zoom to fit -grid yes
+$ ds9 cmd-2d-hist.fits -cmap sls -zoom to fit -grid yes \
+      -grid type publication
+@end example
+
+If you are happy with the grid and coloring and etc, you can also use ds9 to 
save this as a JPEG image to directly use in your documents/slides with these 
extra DS9 options (DS9 will write the image to @file{cmd-2d.jpeg} and quit 
immediately afterwards):
+
+@example
+$ ds9 cmd-2d-hist.fits -cmap sls -zoom 4 -grid yes \
+      -grid type publication -saveimage cmd-2d.jpeg -quit
+@end example
+
+@cindex PGFPlots (@LaTeX{} package)
+This is good for a fast progress update.
+But for your paper or more official report, you want to show something with 
higher quality.
+For that, you can use the PGFPlots package in @LaTeX{} to add axises in the 
same font as your text, sharp grids and many other elegant/powerful features 
(like over-plotting interesting points, lines and etc).
+But to load the 2D histogram into PGFPlots first you need to convert the FITS 
image into a more standard format, for example PDF.
+We'll use Gnuastro's @ref{ConvertType} for this, and use the 
@code{sls-inverse} color map (which will map the pixels with a value of zero to 
white):
+
+@example
+$ astconvertt cmd-2d-hist.fits --colormap=sls-inverse \
+              --borderwidth=0 -ocmd-2d-hist.pdf
+@end example
+
+@noindent
+Below you can see a minimally working example of how to add axis numbers, 
labels and a grid to the PDF generated above.
+Copy and paste the @LaTeX{} code below into a plain-text file called 
@file{cmd-report.tex}
+Notice the @code{xmin}, @code{xmax}, @code{ymin}, @code{ymax} values and how 
they are the same as the range specified above.
+
+@example
+\documentclass@{article@}
+\usepackage@{pgfplots@}
+\dimendef\prevdepth=0
+\begin@{document@}
+
+You can write all you want here...
+
+\begin@{tikzpicture@}
+  \begin@{axis@}[
+      enlargelimits=false,
+      grid,
+      axis on top,
+      width=\linewidth,
+      height=\linewidth,
+      xlabel=@{Magnitude (F775W)@},
+      ylabel=@{Color (F606W-F775W)@}]
+
+    \addplot graphics[xmin=22, xmax=30, ymin=-1, ymax=3]
+             @{cmd-2d-hist.pdf@};
+  \end@{axis@}
+\end@{tikzpicture@}
+\end@{document@}
+@end example
+
+@noindent
+Run this command to build your PDF (assuming you have @LaTeX{} and PGFPlots).
+
+@example
+$ pdflatex cmd-report.tex
+@end example
 
-@node  Sigma clipping, Sky value, Histogram and Cumulative Frequency Plot, 
Statistics
+The improved quality, blending in with the text, vector-graphics resolution 
and other features make this plot pleasing to the eye, and let your readers 
focus on the main point of your scientific argument.
+PGFPlots can also built the PDF of the plot separately from the rest of the 
paper/report, see @ref{2D histogram as a table} for the necessary changes in 
the preamble.
+
+@node  Sigma clipping, Sky value, 2D Histograms, Statistics
 @subsection Sigma clipping
 
 Let's assume that you have pure noise (centered on zero) with a clear 
@url{https://en.wikipedia.org/wiki/Normal_distribution,Gaussian distribution}, 
or see @ref{Photon counting noise}.
@@ -12009,7 +14515,7 @@ A more crude (but simpler method) that is usable in 
such situations is discussed
 
 @cindex Sky value
 This analysis is taken from @url{https://arxiv.org/abs/1505.01664, Akhlaghi 
and Ichikawa (2015)}.
-Let's assume that all instrument defects -- bias, dark and flat -- have been 
corrected and the brightness (see @ref{Flux Brightness and magnitude}) of a 
detected object, @mymath{O}, is desired.
+Let's assume that all instrument defects -- bias, dark and flat -- have been 
corrected and the brightness (see @ref{Brightness flux magnitude}) of a 
detected object, @mymath{O}, is desired.
 The sources of flux on pixel@footnote{For this analysis the dimension of the 
data (image) is irrelevant.
 So if the data is an image (2D) with width of @mymath{w} pixels, then a pixel 
located on column @mymath{x} and row @mymath{y} (where all counting starts from 
zero and (0, 0) is located on the bottom left corner of the image), would have 
an index: @mymath{i=x+y\times{}w}.} @mymath{i} of the image can be written as 
follows:
 
@@ -12124,75 +14630,85 @@ Let's start by clarifying some definitions first: 
@emph{Data} is defined as the
 @emph{Signal} is defined as the mean of the noise on each element.
 We'll also assume that the @emph{background} (see @ref{Sky value definition}) 
is subtracted and is zero.
 
-When a data set doesn't have any signal (only noise), the mean, median and 
mode of the distribution are equal within statistical errors and approximately 
equal to the background value.
-Signal always has a positive value and will never become negative, see Figure 
1 in @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa (2015)}.
+When a dataset doesn't have any signal (only noise), the mean, median and mode 
of the distribution are equal within statistical errors and approximately equal 
to the background value.
+Signal from emitting objects, like astronomical targets, always has a positive 
value and will never become negative, see Figure 1 in 
@url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa (2015)}.
 Therefore, as more signal is added, the mean, median and mode of the dataset 
shift to the positive.
 The mean's shift is the largest.
 The median shifts less, since it is defined based on an ordered distribution 
and so is not affected by a small number of outliers.
 The distribution's mode shifts the least to the positive.
 
-@cindex Mode
+@cindex Mean
 @cindex Median
+@cindex Quantile
 Inverting the argument above gives us a robust method to quantify the 
significance of signal in a dataset.
 Namely, when the mean and median of a distribution are approximately equal, or 
the mean's quantile is around 0.5, we can argue that there is no significant 
signal.
 
-To allow for gradients (which are commonly present in ground-based images), we 
can consider the image to be made of a grid of tiles (see 
@ref{Tessellation}@footnote{The options to customize the tessellation are 
discussed in @ref{Processing options}.}).
+To allow for gradients (which are commonly present in raw exposures, or when 
there are large foreground galaxies in the field), we consider the image to be 
made of a grid of tiles@footnote{The options to customize the tessellation are 
discussed in @ref{Processing options}.} (see @ref{Tessellation}).
 Hence, from the difference of the mean and median on each tile, we can 
estimate the significance of signal in it.
 The median of a distribution is defined to be the value of the distribution's 
middle point after sorting (or 0.5 quantile).
-Thus, to estimate the presence of signal, we'll compare with the quantile of 
the mean with 0.5.
-If the absolute difference in a tile is larger than the value given to the 
@option{--meanmedqdiff} option, that tile will be ignored.
+Thus, to estimate the presence of signal, we just have to estimate the 
quantile of the mean (@mymath{q_{mean}}).
+If a tile's @mymath{|q_{mean}-0.5|} is larger than the value given to the 
@option{--meanmedqdiff} option, that tile will be considered to have 
significant signal and ignored for the next steps.
 You can read this option as ``mean-median-quantile-difference''.
 
-The raw dataset's distribution is noisy, so using the argument above on the 
raw input will give a noisy result.
-To decrease the noise/error in estimating the mode, we will use convolution 
(see @ref{Convolution process}).
+The raw dataset's pixel distribution (in each tile) is noisy, to decrease the 
noise/error in estimating @mymath{q_{mean}}, we convolve the image before 
tessellation (see @ref{Convolution process}.
 Convolution decreases the range of the dataset and enhances its skewness, See 
Section 3.1.1 and Figure 4 in @url{https://arxiv.org/abs/1505.01664, Akhlaghi 
and Ichikawa (2015)}.
 This enhanced skewness can be interpreted as an increase in the Signal to 
noise ratio of the objects buried in the noise.
 Therefore, to obtain an even better measure of the presence of signal in a 
tile, the mean and median discussed above are measured on the convolved image.
 
-Through the difference of the mean and median we have actually `detected' data 
in the distribution.
-However this ``detection'' was only based on the total distribution of the 
data in each tile (a much lower resolution).
-This is the main limitation of this technique.
-The best approach is thus to do detection over the dataset, mask all the 
detected pixels and use the undetected regions to estimate the sky and its 
standard deviation (possibly over a tessellation).
-This is how NoiseChisel works: it uses the argument above to find tiles that 
are used to find its thresholds.
-Several higher-level steps are done on the thresholded pixels to define the 
higher-level detections (see @ref{NoiseChisel}).
-
 @cindex Cosmic rays
 There is one final hurdle: raw astronomical datasets are commonly peppered 
with Cosmic rays.
 Images of Cosmic rays aren't smoothed by the atmosphere or telescope aperture, 
so they have sharp boundaries.
 Also, since they don't occupy too many pixels, they don't affect the mode and 
median calculation.
 But their very high values can greatly bias the calculation of the mean 
(recall how the mean shifts the fastest in the presence of outliers), for 
example see Figure 15 in @url{https://arxiv.org/abs/1505.01664, Akhlaghi and 
Ichikawa (2015)}.
-
 The effect of outliers like cosmic rays on the mean and standard deviation can 
be removed through @mymath{\sigma}-clipping, see @ref{Sigma clipping} for a 
complete explanation.
-Therefore, after asserting that the mode and median are approximately equal in 
a tile (see @ref{Tessellation}), the final Sky value and its standard deviation 
are determined after @mymath{\sigma}-clipping with the @option{--sigmaclip} 
option.
 
-In the end, some of the tiles will pass the mean and median quantile 
difference test.
+Therefore, after asserting that the mean and median are approximately equal in 
a tile (see @ref{Tessellation}), the measurements on the tile are determined 
after @mymath{\sigma}-clipping with the @option{--sigmaclip} option.
+In the end, some of the tiles will pass the mean and median quantile 
difference test and will be given a value.
+Others (that had signal in them) will just be assigned a NaN (not-a-number) 
value.
+So we need to use interpolation and assign a value to all the tiles.
+
 However, prior to interpolating over the failed tiles, another point should be 
considered: large and extended galaxies, or bright stars, have wings which sink 
into the noise very gradually.
-In some cases, the gradient over these wings can be on scales that is larger 
than the tiles.
-The mean-median distance test will pass on such tiles and will cause a strong 
peak in the interpolated tile grid, see @ref{Detecting large extended targets}.
+In some cases, the gradient over these wings can be on scales that is larger 
than the tiles and mean-median distance test will be successful.
+This will cause a footprint of such objects after the interpolation see 
@ref{Detecting large extended targets}.
 
-The tiles that exist over the wings of large galaxies or bright stars are 
outliers in the distribution of tiles that passed the mean-median quantile 
distance test.
-Therefore, the final step of ``quantifying signal in a tile'' is to look at 
this distribution and remove the outliers.
+These tiles (on the wings of large foreground galaxies for example) actually 
have signal in them and the mean-median difference isn't enough (due to the 
``small'' size of the tiles).
+Therefore, the final step of ``quantifying signal in a tile'' is to look at 
this distribution of successful tiles and remove the outliers.
 @mymath{\sigma}-clipping is a good solution for removing a few outliers, but 
the problem with outliers of this kind is that there may be many such tiles 
(depending on the large/bright stars/galaxies in the image).
-Therefore a novel outlier rejection algorithm will be used.
+We therefore apply the following local outlier rejection strategy.
+
+For each tile, we find the nearest @mymath{N_{ngb}} tiles that had a usable 
value (@mymath{N_{ngb}} is the value given to  @option{--interpnumngb}).
+We then sort them and find the difference between the largest and 
second-to-smallest elements (The minimum isn't used because the scatter can be 
large).
+Let's call this the tile's @emph{slope} (measured from its neighbors).
+All the tiles that are on a region of flat noise will have similar slope 
values, but if a few tiles fall on the wings of a bright star or large galaxy, 
their slope will be significantly larger than the tiles with no signal.
+We just have to find the smallest tile slope value that is an outlier compared 
to the rest and reject all tiles with a slope larger than that.
 
 @cindex Outliers
 @cindex Identifying outliers
-To identify the first outlier, we'll use the distribution of distances between 
sorted elements.
-If there are @mymath{N} successful tiles, for every tile, the distance between 
the adjacent @mymath{N/2} previous elements is found, giving a distribution 
containing @mymath{N/2-1} points.
-The @mymath{\sigma}-clipped median and standard deviation of this distribution 
is then found (@mymath{\sigma}-clipping is configured with 
@option{--outliersclip}).
-Finally, if the distance between the element and its previous element is more 
than @option{--outliersigma} multiples of the @mymath{\sigma}-clipped standard 
deviation added with the @mymath{\sigma}-clipped median, that element is 
considered an outlier and all tiles larger than that value are ignored.
+To identify the smallest outlier, we'll use the distribution of distances 
between sorted elements.
+Let's assume the total number tiles with a good mean-median quantile 
difference is @mymath{N}.
+We sort them in increasing order based on their @emph{slope} (defined above).
+So the array of tile slopes can be written as @mymath{@{s[0], s[1], ..., 
s[N]@}}, where @mymath{s[0]<s[1]} and so on.
+We then start from element @mymath{M} and calculate the distances between all 
two adjacent values before it: @mymath{@{s[1]-s[0], s[2]-s[1], s[M-1]-s[M-2]@}}.
+
+The @mymath{\sigma}-clipped median and standard deviation of this distribution 
is then found (@mymath{\sigma}-clipping is configured with 
@option{--outliersclip} which takes two values, see @ref{Sigma clipping}).
+If the distance between the element and its previous element is more than 
@option{--outliersigma} multiples of the @mymath{\sigma}-clipped standard 
deviation added with the @mymath{\sigma}-clipped median, that element is 
considered an outlier and all tiles larger than that value are ignored.
 
 Formally, if we assume there are @mymath{N} elements.
 They are first sorted.
-Searching for the outlier starts on element @mymath{N/2} (integer division).
-Let's take @mymath{v_i} to be the @mymath{i}-th element of the sorted input 
(with no blank values) and @mymath{m} and @mymath{\sigma} as the 
@mymath{\sigma}-clipped median and standard deviation from the distances of the 
previous @mymath{N/2-1} elements (not including @mymath{v_i}).
+Searching for the outlier starts on element @mymath{N/3} (integer division).
+Let's take @mymath{v_i} to be the @mymath{i}-th element of the sorted input 
(with no blank values) and @mymath{m} and @mymath{\sigma} as the 
@mymath{\sigma}-clipped median and standard deviation from the distances of the 
previous @mymath{N/3-1} elements (not including @mymath{v_i}).
 If the value given to @option{--outliersigma} is displayed with @mymath{s}, 
the @mymath{i}-th element is considered as an outlier when the condition below 
is true.
 
 @dispmath{{(v_i-v_{i-1})-m\over \sigma}>s}
 
-Since @mymath{i} begins from the median, the outlier has to be larger than the 
median.
-You can use the check images (for example @option{--checksky} in the 
Statistics program or @option{--checkqthresh}, @option{--checkdetsky} and 
@option{--checksky} options in NoiseChisel for any of its steps that uses this 
outlier rejection) to inspect the steps and see which tiles have been discarded 
as outliers prior to interpolation.
+Since @mymath{i} begins from the @mymath{N/3}-th element in the sorted array 
(a quantile of @mymath{1/3=0.33}), the outlier has to be larger than the 
@mymath{0.33} quantile value of the dataset.
 
+Once the outlying tiles have been successfully removed we use nearest neighbor 
interpolation to give a value to all tiles in the image.
+During interpolation, the median of the @mymath{N_{ngb}} nearest neighbors of 
each tile is found and given to the tile (@mymath{N_{ngb}} is the value given 
to  @option{--interpnumngb}).
+Once all the tiles are given a value, a smoothing step is implemented to 
remove the sharp value contrast that can happen between some tiles.
+The size of the smoothing box is set with the @option{--smoothwidth} option.
+
+You can use the check images to inspect the steps and see which tiles have 
been discarded as outliers prior to interpolation (@option{--checksky} in the 
Statistics program or @option{--checkqthresh} in NoiseChisel).
 
 
 
@@ -12565,6 +15081,11 @@ By default (when no @option{--output} is specified) a 
plain text table will be c
 If a FITS name is specified, you can use the common option 
@option{--tableformat} to have it as a FITS ASCII or FITS binary format, see 
@ref{Common options}.
 This table can then be fed into your favorite plotting tool and get a much 
more clean and nice histogram than what the raw command-line can offer you 
(with the @option{--asciihist} option).
 
+@item --histogram2d
+Save the 2D histogram of two input columns into an output file, see @ref{2D 
Histograms}.
+The output will have three columns: the first two are the coordinates of each 
box's center in the first and second dimensions/columns.
+The third will be number of input points that fall within that box.
+
 @item -C
 @itemx --cumulative
 Save the cumulative frequency plot of the usable values in the input dataset 
into a table, similar to @option{--histogram}.
@@ -12637,6 +15158,18 @@ The @option{--onebinstart} option has a higher 
priority than this option.
 In other words, @option{--onebinstart} takes effect after the range has been 
finalized and the initial bins have been defined, therefore it has the power to 
(possibly) shift the bins.
 If you want to manually set the range of the bins @emph{and} have one bin on a 
special value, it is thus better to avoid @option{--onebinstart}.
 
+@item --numbins2=INT
+Similar to @option{--numbins}, but for the second column when a 2D histogram 
is requested, see @option{--histogram2d}.
+
+@item --greaterequal2=FLT
+Similar to @option{--greaterequal}, but for the second column when a 2D 
histogram is requested, see @option{--histogram2d}.
+
+@item --lessthan2=FLT
+Similar to @option{--lessthan}, but for the second column when a 2D histogram 
is requested, see @option{--histogram2d}.
+
+@item --onebinstart2=FLT
+Similar to @option{--onebinstart}, but for the second column when a 2D 
histogram is requested, see @option{--histogram2d}.
+
 @end table
 
 
@@ -12683,8 +15216,8 @@ The parameters for estimating the sky value can be set 
with the following option
 
 @table @option
 
-@item -k=STR
-@itemx --kernel=STR
+@item -k=FITS
+@itemx --kernel=FITS
 File name of kernel to help in estimating the significance of signal in a
 tile, see @ref{Quantifying signal in a tile}.
 
@@ -12748,9 +15281,11 @@ The file will have two extensions for each step (one 
for the Sky and one for the
 @cindex Segmentation
 Once instrumental signatures are removed from the raw data (image) in the 
initial reduction process (see @ref{Data manipulation}).
 You are naturally eager to start answering the scientific questions that 
motivated the data collection in the first place.
-However, the raw dataset/image is just an array of values/pixels, that is all! 
These raw values cannot directly be used to answer your scientific questions: 
for example ``how many galaxies are there in the image?''.
+However, the raw dataset/image is just an array of values/pixels, that is all! 
These raw values cannot directly be used to answer your scientific questions: 
for example ``how many galaxies are there in the image?'', ``What is their 
brightness?'' and etc.
 
-The first high-level step in your analysis of your targets will thus be to 
classify, or label, the dataset elements (pixels) into two classes: 1) noise, 
where random effects are the major contributor to the value, and 2) signal, 
where non-random factors (for example light from a distant galaxy) are present.
+The first high-level step your analysis will therefore be to classify, or 
label, the dataset elements (pixels) into two classes:
+1) Noise, where random effects are the major contributor to the value, and
+2) Signal, where non-random factors (for example light from a distant galaxy) 
are present.
 This classification of the elements in a dataset is formally known as 
@emph{detection}.
 
 In an observational/experimental dataset, signal is always buried in noise: 
only mock/simulated datasets are free of noise.
@@ -12761,7 +15296,7 @@ Therefore when noise is significant, proper detection 
of your targets is a uniqu
 
 @cindex Erosion
 NoiseChisel is Gnuastro's program for detection of targets that don't have a 
sharp border (almost all astronomical objects).
-When the targets have a sharp edges/border (for example cells in biological 
imaging), a simple threshold is enough to separate them from noise and each 
other (if they are not touching).
+When the targets have sharp edges/borders (for example cells in biological 
imaging), a simple threshold is enough to separate them from noise and each 
other (if they are not touching).
 To detect such sharp-edged targets, you can use Gnuastro's Arithmetic program 
in a command like below (assuming the threshold is @code{100}, see 
@ref{Arithmetic}):
 
 @example
@@ -12770,20 +15305,23 @@ $ astarithmetic in.fits 100 gt 2 connected-components
 
 Since almost no astronomical target has such sharp edges, we need a more 
advanced detection methodology.
 NoiseChisel uses a new noise-based paradigm for detection of very extended and 
diffuse targets that are drowned deeply in the ocean of noise.
-It was initially introduced in @url{https://arxiv.org/abs/1505.01664, Akhlaghi 
and Ichikawa [2015]}.
+It was initially introduced in @url{https://arxiv.org/abs/1505.01664, Akhlaghi 
and Ichikawa [2015]} and improvements after the first four were published in 
@url{https://arxiv.org/abs/1909.11230, Akhlaghi [2019]}.
+Please take the time to go through these papers to most effectively understand 
the need of NoiseChisel and how best to use it.
+
 The name of NoiseChisel is derived from the first thing it does after 
thresholding the dataset: to erode it.
 In mathematical morphology, erosion on pixels can be pictured as carving-off 
boundary pixels.
 Hence, what NoiseChisel does is similar to what a wood chisel or stone chisel 
do.
 It is just not a hardware, but a software.
 In fact, looking at it as a chisel and your dataset as a solid cube of rock 
will greatly help in effectively understanding and optimally using it: with 
NoiseChisel you literally carve your targets out of the noise.
-Try running it with the @option{--checkdetection} option to see each step of 
the carving process on your input dataset.
+Try running it with the @option{--checkdetection} option, and open the 
temporary output as a multi-extension cube, to see each step of the carving 
process on your input dataset (see @ref{Viewing multiextension FITS images}).
 
 @cindex Segmentation
-NoiseChisel's primary output is a binary detection map with the same size as 
the input but only with two values: 0 and 1.
+NoiseChisel's primary output is a binary detection map with the same size as 
the input but its pixels only have two values: 0 (background) and 1 
(foreground).
 Pixels that don't harbor any detected signal (noise) are given a label (or 
value) of zero and those with a value of 1 have been identified as hosting 
signal.
 
 Segmentation is the process of classifying the signal into higher-level 
constructs.
-For example if you have two separate galaxies in one image, by default 
NoiseChisel will give a value of 1 to the pixels of both, but after 
segmentation, the pixels in each will get separate labels.
+For example if you have two separate galaxies in one image, NoiseChisel will 
give a value of 1 to the pixels of both (each forming an ``island'' of touching 
foreground pixels).
+After segmentation, the connected foreground pixels will get separate labels, 
enabling you to study them individually.
 NoiseChisel is only focused on detection (separating signal from noise), to 
@emph{segment} the signal (into separate galaxies for example), Gnuastro has a 
separate specialized program @ref{Segment}.
 NoiseChisel's output can be directly/readily fed into Segment.
 
@@ -12799,137 +15337,54 @@ You can then directly feed NoiseChisel's output into 
MakeCatalog for measurement
 Thanks to the published papers mentioned above, there is no need to provide a 
more complete introduction to NoiseChisel in this book.
 However, published papers cannot be updated any more, but the software has 
evolved/changed.
 The changes since publication are documented in @ref{NoiseChisel changes after 
publication}.
-Afterwards, in @ref{Invoking astnoisechisel}, the details of running 
NoiseChisel and its options are discussed.
+In @ref{Invoking astnoisechisel}, the details of running NoiseChisel and its 
options are discussed.
 
 As discussed above, detection is one of the most important steps for your 
scientific result.
 It is therefore very important to obtain a good understanding of NoiseChisel 
(and afterwards @ref{Segment} and @ref{MakeCatalog}).
-We thus strongly recommend that after reading the papers above and the 
respective sections of Gnuastro's book, you play a little with the settings (in 
the order presented in the paper and @ref{Invoking astnoisechisel}) on a 
dataset you are familiar with and inspect all the check images (options 
starting with @option{--check}) to see the effect of each parameter.
-
-We strongly recommend going over the two tutorials of @ref{General program 
usage tutorial} and @ref{Detecting large extended targets}.
+We strongly recommend reviewing two tutorials of @ref{General program usage 
tutorial} and @ref{Detecting large extended targets}.
 They are designed to show how to most effectively use NoiseChisel for the 
detection of small faint objects and large extended objects.
-In the meantime, they will show you the modular principle behind Gnuastro's 
programs and how they are built to complement, and build upon, each other.
+In the meantime, they also show the modular principle behind Gnuastro's 
programs and how they are built to complement, and build upon, each other.
+
 @ref{General program usage tutorial} culminates in using NoiseChisel to detect 
galaxies and use its outputs to find the galaxy colors.
 Defining colors is a very common process in most science-cases.
 Therefore it is also recommended to (patiently) complete that tutorial for 
optimal usage of NoiseChisel in conjunction with all the other Gnuastro 
programs.
 @ref{Detecting large extended targets} shows you can optimize NoiseChisel's 
settings for very extended objects to successfully carve out to signal-to-noise 
ratio levels of below 1/10.
+After going through those tutorials, play a little with the settings (in the 
order presented in the paper and @ref{Invoking astnoisechisel}) on a dataset 
you are familiar with and inspect all the check images (options starting with 
@option{--check}) to see the effect of each parameter.
 
-In @ref{NoiseChisel changes after publication}, we'll review the changes in 
NoiseChisel since the publication of @url{https://arxiv.org/abs/1505.01664, 
Akhlaghi and Ichikawa [2015]}.
-We will then review NoiseChisel's input, detection, and output options in 
@ref{NoiseChisel input}, @ref{Detection options}, and @ref{NoiseChisel output}.
+Below, in @ref{Invoking astnoisechisel}, we will review NoiseChisel's input, 
detection, and output options in @ref{NoiseChisel input}, @ref{Detection 
options}, and @ref{NoiseChisel output}.
+If you have used NoiseChisel within your research, please run it with 
@option{--cite} to list the papers you should cite and how to acknowledge its 
funding sources.
 
 @menu
-* NoiseChisel changes after publication::  NoiseChisel updates after paper's 
publication.
+* NoiseChisel changes after publication::  Updates since published papers.
 * Invoking astnoisechisel::     Options and arguments for NoiseChisel.
 @end menu
 
 @node NoiseChisel changes after publication, Invoking astnoisechisel, 
NoiseChisel, NoiseChisel
 @subsection NoiseChisel changes after publication
 
-NoiseChisel was initially introduced in @url{https://arxiv.org/abs/1505.01664, 
Akhlaghi and Ichikawa [2015]}.
-It is thus strongly recommended to read this paper for a good understanding of 
what it does and how each parameter influences the output.
-To help in understanding how it works, that paper has a large number of 
figures showing every step on multiple mock and real examples.
+NoiseChisel was initially introduced in @url{https://arxiv.org/abs/1505.01664, 
Akhlaghi and Ichikawa [2015]} and updates after the first four years were 
published in @url{https://arxiv.org/abs/1909.11230, Akhlaghi [2019]}.
+To help in understanding how it works, those papers have many figures showing 
every step on multiple mock and real examples.
+We recommended to read these papers for a good understanding of what it does 
and how each parameter influences the output.
 
-However, the paper cannot be updated anymore, but NoiseChisel has evolved (and 
will continue to do so): better algorithms or steps have been found, thus 
options will be added or removed.
+However, the papers cannot be updated anymore, but NoiseChisel has evolved 
(and will continue to do so): better algorithms or steps have been found and 
implemented and some options have been added, removed or changed behavior.
 This book is thus the final and definitive guide to NoiseChisel.
-The aim of this section is to make the transition from the paper to the 
installed version on your system, as smooth as possible with the list below.
-For a more detailed list of changes in previous Gnuastro releases/versions, 
please see the @file{NEWS} file@footnote{The @file{NEWS} file is present in the 
released Gnuastro tarball, see @ref{Release tarball}.}.
-
-The most important change since the publication of that paper is that from 
Gnuastro 0.6, NoiseChisel is only in charge on detection.
-Segmentation of the detected signal was spun-off into a separate program: 
@ref{Segment}.
-This spin-off allows much greater creativity and is in the spirit of 
Gnuastro's modular design (see @ref{Program design philosophy}).
-Below you can see the major changes since that paper was published.
-First, the removed options/features are discussed, then we review the new 
features that have been added.
+The aim of this section is to make the transition from the papers above to the 
installed version on your system, as smooth as possible with the list below.
+For a more detailed list of changes in each Gnuastro version, please see the 
@file{NEWS} file@footnote{The @file{NEWS} file is present in the released 
Gnuastro tarball, see @ref{Release tarball}.}.
 
-@noindent
-Removed features/options:
 @itemize
-
 @item
-@option{--skysubtracted}: This option was used to account for the extra noise 
that is added if the Sky value has already been subtracted.
-However, NoiseChisel estimates the Sky standard deviation based on the input 
data, not exposure maps or other theoretical considerations.
-Therefore the standard deviation of the undetected pixels also contains the 
errors due to any previous sky subtraction.
-This option is therefore no longer present in NoiseChisel.
-
-@option{--dilate}: In the paper, true detections were dilated for a final dig 
into the noise.
-However, the simple 8-connected dilation produced boxy results which were not 
realistic and could miss diffuse flux.
-The final dig into the noise is now done by ``grow''ing the true detections, 
similar to how true clumps were grown, see the description of 
@option{--detgrowquant} below and in @ref{Detection options} for more on the 
new alternative.
-
-@item
-Segmentation has been completely moved to a new program: @ref{Segment}.
+An improved outlier rejection for identifying tiles without any signal has 
been implemented in the quantile-threshold phase:
+Prior to version 0.14, outliers were defined globally: the distribution of all 
tiles with an acceptable @option{--meanmedqdiff} was inspected and outliers 
were found and rejected.
+However, this caused problems when there are strong gradients over the image 
(for example an image prior to flat-fielding, or in the presence of a large 
foreground galaxy).
+In these cases, the faint wings of galaxies/stars could be mistakenly 
identified as Sky (leaving a footprint of the object on the Sky output) and 
wrongly subtracted.
+
+It was possible to play with the parameters to correct this for that 
particular dataset, but that was frustrating.
+Therefore from version 0.14, instead of finding outliers from the full tile 
distribution, we now measure the @emph{slope} of the tile's nearby tiles and 
find outliers locally.
+For more on the outlier-by-distance algorithm and the definition of 
@emph{slope}, see @ref{Quantifying signal in a tile}.
+In our tests, this gave a much improved estimate of the quantile thresholds 
and final Sky values with default values.
 @end itemize
 
-@noindent
-Added features/options:
-@itemize
-
-@item
-The quantile difference to identify tiles with no significant signal is 
measured between the @emph{mean} and median.
-In the published paper, it was between the @emph{mode} and median.
-The quantile of the mean is more sensitive to skewness (the presence of 
signal), so it is preferable to the quantile of the mode.
-For more see @ref{Quantifying signal in a tile}.
-
-@item
-@option{--widekernel}: NoiseChisel uses the difference between the mean and 
median to identify if a tile should be used for estimating the quantile 
thresholds (see @ref{Quantifying signal in a tile}).
-Until now, NoiseChisel would convolve an image once and estimate the proper 
tiles for quantile estimations on the convolved image.
-The same convolved image would later be used for quantile estimation.
-A larger kernel does increase the skewness (and thus difference between the 
mean and median, therefore helps in detecting the presence signal), however, it 
disfigures the shapes/morphology of the objects.
-
-This new @option{--widekernel} option (and a corresponding @option{--wkhdu} 
option to specify its HDU) is added to solve such cases.
-When its given, the input will be convolved with both the sharp (given through 
the @option{--kernel} option) and wide kernels.
-The mean and median are calculated on the dataset that is convolved with the 
wider kernel, then the quantiles are estimated on the image convolved with the 
sharper kernel.
-
-@item
-Outlier rejection in quantile thresholds: When there are large galaxies or 
bright stars in the image, their gradient may be on a smaller scale than the 
selected tile size.
-In such cases, those tiles will be identified as tiles with no signal and thus 
preserved.
-An outlier identification algorithm has been added to NoiseChisel and can be 
configured with the following options: @option{--outliersigma} and 
@option{--outliersclip}.
-For a more complete description, see the latter half of @ref{Quantifying 
signal in a tile}.
-
-@item
-@option{--blankasforeground}: allows blank pixels to be treated as foreground 
in NoiseChisel's binary operations: the initial erosion (@option{--erode}) and 
opening (@option{--open}) as well as the filling holes and opening step for 
defining pseudo-detections (@option{--dthresh}).
-In the published paper, blank pixels were treated as foreground by default.
-To avoid too many false positive near blank/masked regions, blank pixels are 
now considered to be in the background.
-This option will create the old behavior.
-
-@item
-@option{--skyfracnoblank}: To reduce the bias caused by undetected wings of 
galaxies and stars in the Sky measurements, NoiseChisel only uses tiles that 
have a sufficiently large fraction of undetected pixels.
-Until now the reference for this fraction was the whole tile size.
-With this option, it is now possible to ask for ignoring blank pixels when 
calculating the fraction.
-This is useful when blank/masked pixels are distributed across the image.
-For more, see the description of this option in @ref{Detection options}.
-
-@item
-@option{--dopening}: Number of openings after applying @option{--dthresh}.
-For more, see the description of this option in @ref{Detection options}.
-
-@item
-@option{--dopeningngb}: The connectivity/neighbors used in the opening of 
@option{--dopening}.
-
-@item
-@option{--holengb}: The connectivity (defined by the number of neighbors) to 
fill holes after applying @option{--dthresh} (above) to find pseudo-detections.
-For more, see the description of this option in @ref{Detection options}.
-
-@item
-@option{--pseudoconcomp}: The connectivity (defined by the number of 
neighbors) to find individual pseudo-detections.
-For more, see the description of this option in @ref{Detection options}.
-
-@item
-@option{--snthresh}: Manually set the S/N of true pseudo-detections and thus 
avoid the need to automatically identify this value.
-For more, see the description of this option in @ref{Detection options}.
-
-@item
-@option{--detgrowquant}: is used to grow the final true detections until a 
given quantile in the same way that clumps are grown during segmentation 
(compare columns 2 and 3 in Figure 10 of the paper).
-It replaces the old @option{--dilate} option in the paper and older versions 
of Gnuastro.
-Dilation is a blind growth method which causes objects to be boxy or diamond 
shaped when too many layers are added.
-However, with the growth method that is defined now, we can follow the signal 
into the noise with any shape.
-The appropriate quantile depends on your dataset's correlated noise properties 
and how cleanly it was Sky subtracted.
-The new @option{--detgrowmaxholesize} can also be used to specify the maximum 
hole size to fill as part of this growth, see the description in @ref{Detection 
options} for more details.
-
-This new growth process can be much more successful in detecting diffuse flux 
around true detections compared to dilation and give more realistic results, 
but it can also increase the NoiseChisel run time (depending on the given value 
and input size).
-
-@item
-@option{--cleangrowndet}: Further clean/remove false detections after
-growth, see the descriptions under this option in @ref{Detection options}.
 
-@end itemize
 
 
 @node Invoking astnoisechisel,  , NoiseChisel changes after publication, 
NoiseChisel
@@ -13067,8 +15522,8 @@ Recall that you can always see the full list of 
Gnuastro's options with the @opt
 
 @table @option
 
-@item -k STR
-@itemx --kernel=STR
+@item -k FITS
+@itemx --kernel=FITS
 File name of kernel to smooth the image before applying the threshold, see 
@ref{Convolution kernel}.
 If no convolution is needed, give this option a value of @option{none}.
 
@@ -13096,7 +15551,7 @@ This can help getting faster results when you are 
playing/testing the higher-lev
 HDU containing the kernel in the file given to the @option{--kernel}
 option.
 
-@item --convolved=STR
+@item --convolved=FITS
 Use this file as the convolved image and don't do convolution (ignore 
@option{--kernel}).
 NoiseChisel will just check the size of the given dataset is the same as the 
input's size.
 If a wrong image (with the same size) is given to this option, the results 
(errors, bugs, etc) are unpredictable.
@@ -13141,8 +15596,8 @@ $ for g in 60 65 70 75 80 85 90; do                     
     \
 @item --chdu=STR
 The HDU/extension containing the convolved image in the file given to 
@option{--convolved}.
 
-@item -w STR
-@itemx --widekernel=STR
+@item -w FITS
+@itemx --widekernel=FITS
 File name of a wider kernel to use in estimating the difference of the mode 
and median in a tile (this difference is used to identify the significance of 
signal in that tile, see @ref{Quantifying signal in a tile}).
 As displayed in Figure 4 of @url{https://arxiv.org/abs/1505.01664, Akhlaghi 
and Ichikawa [2015]}, a wider kernel will help in identifying the skewness 
caused by data in noise.
 The image that is convolved with this kernel is @emph{only} used for this 
purpose.
@@ -13560,7 +16015,7 @@ Once signal is separated from noise (for example with 
@ref{NoiseChisel}), you ha
 Signal (for example every galaxy in your image) has been ``detected'', but all 
detections have a label of 1.
 Therefore while we know which pixels contain signal, we still can't find out 
how many galaxies they contain or which detected pixels correspond to which 
galaxy.
 At the lowest (most generic) level, detection is a kind of segmentation 
(segmenting the whole dataset into signal and noise, see @ref{NoiseChisel}).
-Here, we'll define segmentation only on signal: to separate and find 
sub-structure within the detections.
+Here, we'll define segmentation only on signal: to separate sub-structure 
within the detections.
 
 @cindex Connected component labeling
 If the targets are clearly separated, or their detected regions aren't 
touching, a simple connected 
components@footnote{@url{https://en.wikipedia.org/wiki/Connected-component_labeling}}
 algorithm (very basic segmentation) is enough to separate the regions that are 
touching/connected.
@@ -13590,7 +16045,7 @@ It is therefore necessary to do a more sophisticated 
segmentation and break up t
 
 Segment will use a detection map and its corresponding dataset to find 
sub-structure over the detected areas and use them for its segmentation.
 Until Gnuastro version 0.6 (released in 2018), Segment was part of 
@ref{NoiseChisel}.
-Therefore, similar to NoiseChisel, the best place to start reading about 
Segment and understanding what it does (with many illustrative figures) is 
Section 3.2 of @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa 
[2015]}.
+Therefore, similar to NoiseChisel, the best place to start reading about 
Segment and understanding what it does (with many illustrative figures) is 
Section 3.2 of @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa 
[2015]}, and continue with @url{https://arxiv.org/abs/1909.11230, Akhlaghi 
[2019]}.
 
 @cindex river
 @cindex Watershed algorithm
@@ -13601,84 +16056,38 @@ Using the undetected regions can be disabled by 
directly giving a signal-to-nois
 
 The true clumps are then grown to a certain threshold over the detections.
 Based on the strength of the connections (rivers/watersheds) between the grown 
clumps, they are considered parts of one @emph{object} or as separate 
@emph{object}s.
-See Section 3.2 of Akhlaghi and Ichikawa [2015] (link above) for more.
+See Section 3.2 of @url{https://arxiv.org/abs/1505.01664, Akhlaghi and 
Ichikawa [2015]} for more.
 Segment's main output are thus two labeled datasets: 1) clumps, and 2) objects.
 See @ref{Segment output} for more.
 
-To start learning about Segment, especially in relation to detection 
(@ref{NoiseChisel}) and measurement (@ref{MakeCatalog}), the recommended 
references are @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa 
[2015]} and @url{https://arxiv.org/abs/1611.06387, Akhlaghi [2016]}.
+To start learning about Segment, especially in relation to detection 
(@ref{NoiseChisel}) and measurement (@ref{MakeCatalog}), the recommended 
references are @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa 
[2015]}, @url{https://arxiv.org/abs/1611.06387, Akhlaghi [2016]} and 
@url{https://arxiv.org/abs/1909.11230, Akhlaghi [2019]}.
+If you have used Segment within your research, please run it with 
@option{--cite} to list the papers you should cite and how to acknowledge its 
funding sources.
 
 Those papers cannot be updated any more but the software will evolve.
 For example Segment became a separate program (from NoiseChisel) in 2018 
(after those papers were published).
 Therefore this book is the definitive reference.
-To help in the transition from those papers to the software you are using, see 
@ref{Segment changes after publication}.
+@c To help in the transition from those papers to the software you are using, 
see @ref{Segment changes after publication}.
 Finally, in @ref{Invoking astsegment}, we'll discuss Segment's inputs, outputs 
and configuration options.
 
 
 @menu
-* Segment changes after publication::  Segment updates after paper's 
publication.
 * Invoking astsegment::         Inputs, outputs and options to Segment
 @end menu
 
-@node Segment changes after publication, Invoking astsegment, Segment, Segment
-@subsection Segment changes after publication
-
-Segment's main algorithm and working strategy were initially defined and 
introduced in Section 3.2 of @url{https://arxiv.org/abs/1505.01664, Akhlaghi 
and Ichikawa [2015]}.
-Prior to Gnuastro version 0.6 (released 2018), one program (NoiseChisel) was 
in charge of detection @emph{and} segmentation.
-To increase creativity and modularity, NoiseChisel's segmentation features 
were spun-off into a separate program (Segment).
-It is strongly recommended to read that paper for a good understanding of what 
Segment does, how it relates to detection, and how each parameter influences 
the output.
-That paper has a large number of figures showing every step on multiple mock 
and real examples.
+@c @node Segment changes after publication, Invoking astsegment, Segment, 
Segment
+@c @subsection Segment changes after publication
 
-However, the paper cannot be updated anymore, but Segment has evolved (and 
will continue to do so): better algorithms or steps have been (and will be) 
found.
-This book is thus the final and definitive guide to Segment.
-The aim of this section is to make the transition from the paper to your 
installed version, as smooth as possible through the list below.
-For a more detailed list of changes in previous Gnuastro releases/versions, 
please follow the @file{NEWS} file@footnote{The @file{NEWS} file is present in 
the released Gnuastro tarball, see @ref{Release tarball}.}.
+@c Segment's main algorithm and working strategy were initially defined and 
introduced in Section 3.2 of @url{https://arxiv.org/abs/1505.01664, Akhlaghi 
and Ichikawa [2015]} and @url{https://arxiv.org/abs/1909.11230, Akhlaghi 
[2019]}.
+@c It is strongly recommended to read those papers for a good understanding of 
what Segment does, how it relates to detection, and how each parameter 
influences the output.
+@c They have many figures showing every step on multiple mock and real 
examples.
 
-@itemize
-
-@item
-Since the spin-off from NoiseChisel, the default kernel to smooth the input 
for convolution has a FWHM of 1.5 pixels (still a Gaussian).
-This is slightly less than NoiseChisel's default kernel (which has a FWHM of 2 
pixels).
-This enables the better detection of sharp clumps: as the kernel gets wider, 
the lower signal-to-noise (but sharp/small) clumps will be washed away into the 
noise.
-You can use MakeProfiles to build your own kernel if this is too sharp/wide 
for your purpose.
-For more, see the @option{--kernel} option in @ref{Segment input}.
+@c However, the papers cannot be updated anymore, but Segment has evolved (and 
will continue to do so): better algorithms or steps have been (and will be) 
found.
+@c This book is thus the final and definitive guide to Segment.
+@c The aim of this section is to make the transition from the paper to your 
installed version, as smooth as possible through the list below.
+@c For a more detailed list of changes in previous Gnuastro releases/versions, 
please follow the @file{NEWS} file@footnote{The @file{NEWS} file is present in 
the released Gnuastro tarball, see @ref{Release tarball}.}.
 
-The ability to use a different convolution kernel for detection and 
segmentation is one example of how separating detection from segmentation into 
separate programs can increase creativity.
-In detection, you want to detect the diffuse and extended emission, but in 
segmentation, you want to detect sharp peaks.
-
-@item
-The criteria to select true from false clumps is the peak significance.
-It is defined to be the difference between the clump's peak value 
(@mymath{C_c}) and the highest valued river pixel around that clump 
(@mymath{R_c}).
-Both are calculated on the convolved image (signified by the @mymath{c} 
subscript).
-To avoid absolute values (differing from dataset to dataset), @mymath{C_c-R_c} 
is then divided by the Sky standard deviation under the river pixel used 
(@mymath{\sigma_r}) as shown below:
-
-@dispmath{C_c-R_c\over \sigma_r}
-
-When @option{--minima} is given, the nominator becomes @mymath{R_c-C_c}.
-
-The input Sky standard deviation dataset (@option{--std}) is assumed to be for 
the unconvolved image.
-Therefore a constant factor (related to the convolution kernel) is necessary 
to convert this into an absolute peak significance@footnote{To get an estimate 
of the standard deviation correction factor between the input and convolved 
images, you can take the following steps:
-1) Mask (set to NaN) all detections on the convolved image with the 
@code{where} operator or @ref{Arithmetic}.
-2) Calculate the standard deviation of the undetected (non-masked) pixels of 
the convolved image with the @option{--sky} option of @ref{Statistics} (which 
also calculates the Sky standard deviation).
-Just make sure the tessellation settings of Statistics and NoiseChisel are the 
same (you can check with the @option{-P} option).
-3) Divide the two standard deviation datasets to get the correction factor.}.
-As far as Segment is concerned, the absolute value of this correction factor 
is irrelevant: because it uses the ambient noise (undetected regions) to find 
the numerical threshold of this fraction and applies that over the detected 
regions.
-
-A distribution's extremum (maximum or minimum) values, used in the new 
criteria, are strongly affected by scatter.
-On the other hand, the convolved image has much less scatter@footnote{For more 
on the effect of convolution on a distribution, see Section 3.1.1 of 
@url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]}.}.
-Therefore @mymath{C_c-R_c} is a more reliable (with less scatter) measure to 
identify signal than @mymath{C-R} (on the unconvolved image).
-
-Initially, the total clump signal-to-noise ratio of each clump was used, see 
Section 3.2.1 of @url{https://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa 
[2015]}.
-Therefore its completeness decreased dramatically when clumps were present on 
gradients.
-In tests, this measure proved to be more successful in detecting clumps on 
gradients and on flatter regions simultaneously.
-
-@item
-With the new @option{--minima} option, it is now possible to detect inverse 
clumps (for example absorption features).
-In such cases, the clump should be built from its smallest value.
-@end itemize
-
-
-@node Invoking astsegment,  , Segment changes after publication, Segment
-@subsection Invoking Segment
+@node Invoking astsegment,  , Segment, Segment
+@subsection Invoking Segment
 
 Segment will identify substructure within the detected regions of an input 
image.
 Segment's output labels can be directly used for measurements (for example 
with @ref{MakeCatalog}).
@@ -13805,8 +16214,8 @@ Please see the description of @option{--hdu} in 
@ref{Input output options} for t
 The input Sky standard deviation value/dataset is actually variance.
 When this option is called, the square root of input Sky standard deviation 
(see @option{--std}) is used internally, not its raw value(s).
 
-@item -d STR
-@itemx --detection=STR
+@item -d FITS
+@itemx --detection=FITS
 Detection map to use for segmentation.
 If given a value of @option{all}, Segment will assume the whole dataset must 
be segmented, see below.
 If a detection map is given, the extension can be specified with 
@option{--dhdu}.
@@ -13831,9 +16240,9 @@ In such cases, is possible to make a detection map that 
only has the value @code
 The HDU/extension containing the detection map given to @option{--detection}.
 Please see the description of @option{--hdu} in @ref{Input output options} for 
the different ways you can identify a special extension.
 
-@item -k STR
-@itemx --kernel=STR
-The kernel used to convolve the input image.
+@item -k FITS
+@itemx --kernel=FITS
+The name of file containing kernel that will be used to convolve the input 
image.
 The usage of this option is identical to NoiseChisel's @option{--kernel} 
option (@ref{NoiseChisel input}).
 Please see the descriptions there for more.
 To disable convolution, you can give it a value of @option{none}.
@@ -13842,8 +16251,8 @@ To disable convolution, you can give it a value of 
@option{none}.
 The HDU/extension containing the kernel used for convolution.
 For acceptable values, please see the description of @option{--hdu} in 
@ref{Input output options}.
 
-@item --convolved
-The convolved image to avoid internal convolution by Segment.
+@item --convolved=FITS
+The convolved image's file name to avoid internal convolution by Segment.
 The usage of this option is identical to NoiseChisel's @option{--convolved} 
option.
 Please see @ref{NoiseChisel input} for a thorough discussion of the usefulness 
and best practices of using this option.
 
@@ -13934,7 +16343,7 @@ The full distribution of clump signal-to-noise ratios 
over the undetected areas
 @item -v
 @itemx --keepmaxnearriver
 Keep a clump whose maximum (minimum if @option{--minima} is called) flux is 
8-connected to a river pixel.
-By default such clumps over detections are considered to be noise and are 
removed irrespective of their brightness (see @ref{Flux Brightness and 
magnitude}).
+By default such clumps over detections are considered to be noise and are 
removed irrespective of their brightness (see @ref{Brightness flux magnitude}).
 Over large profiles, that sink into the noise very slowly, noise can cause 
part of the profile (which was flat without noise) to become a very large and 
with a very high Signal to noise ratio.
 In such cases, the pixel with the maximum flux in the clump will be 
immediately touching a river pixel.
 
@@ -14210,7 +16619,7 @@ Hence, quantifying the detection and measurement 
limitations with a particular d
 
 Here, we'll review some of the most general limits that are important in any 
astronomical data analysis and how MakeCatalog makes it easy to find them.
 Depending on the higher-level analysis, there are more tests that must be 
done, but these are relatively low-level and usually necessary in most cases.
-In astronomy, it is common to use the magnitude (a unit-less scale) and 
physical units, see @ref{Flux Brightness and magnitude}.
+In astronomy, it is common to use the magnitude (a unit-less scale) and 
physical units, see @ref{Brightness flux magnitude}.
 Therefore the measurements discussed here are commonly used in units of 
magnitudes.
 
 @table @asis
@@ -14238,7 +16647,7 @@ Let's take @mymath{\sigma_m} as the median 
@mymath{\sigma} over the successful m
 On different instruments, pixels have different physical sizes (for example in 
micro-meters, or spatial angle over the sky).
 Nevertheless, a pixel is our unit of data collection.
 In other words, while quantifying the noise, the physical or projected size of 
the pixels is irrelevant.
-We thus define the Surface brightness limit or @emph{depth}, in units of 
magnitude/pixel, of a data-set, with zeropoint magnitude @mymath{z}, with the 
@mymath{n}th multiple of @mymath{\sigma_m} as (see @ref{Flux Brightness and 
magnitude}):
+We thus define the Surface brightness limit or @emph{depth}, in units of 
magnitude/pixel, of a data-set, with zeropoint magnitude @mymath{z}, with the 
@mymath{n}th multiple of @mymath{\sigma_m} as (see @ref{Brightness flux 
magnitude}):
 
 @dispmath{SB_{\rm Pixel}=-2.5\times\log_{10}{(n\sigma_m)}+z}
 
@@ -14255,14 +16664,38 @@ The low-level magnitude/pixel measurement above is 
only useful when all the data
 However, you will often find yourself using datasets from various instruments 
with different pixel scales (projected pixel sizes).
 If we know the pixel scale, we can obtain a more easily comparable surface 
brightness limit in units of: magnitude/arcsec@mymath{^2}.
 Let's assume that the dataset has a zeropoint value of @mymath{z}, and every 
pixel is @mymath{p} arcsec@mymath{^2} (so @mymath{A/p} is the number of pixels 
that cover an area of @mymath{A} arcsec@mymath{^2}).
-If the surface brightness is desired at the @mymath{n}th multiple of 
@mymath{\sigma_m}, the following equation (in units of magnitudes per A 
arcsec@mymath{^2}) can be used:
+If the surface brightness is desired at the @mymath{n}th multiple of 
@mymath{\sigma_m}, the following equation (in units of magnitudes per 
@mymath{A} arcsec@mymath{^2}) can be used:
 
 @dispmath{SB_{\rm Projected}=-2.5\times\log_{10}{\left(n\sigma_m\sqrt{A\over 
p}\right)+z}}
 
-Note that this is just an extrapolation of the per-pixel measurement 
@mymath{\sigma_m}.
+The @mymath{\sqrt{A/p}} term comes from the fact that noise is added in RMS: 
if you add three datasets with noise @mymath{\sigma_1}, @mymath{\sigma_2} and 
@mymath{\sigma_3}, the resulting noise level is 
@mymath{\sigma_t=\sqrt{\sigma_1^2+\sigma_2^2+\sigma_3^2}}, so when 
@mymath{\sigma_1=\sigma_2=\sigma_3=\sigma}, then 
@mymath{\sigma_t=\sqrt{3}\sigma}.
+As mentioned above, there are @mymath{A/p} pixels in the area @mymath{A}.
+Therefore, as @mymath{A/p} increases, the surface brightness limiting 
magnitude will become brighter.
+
+It is just important to understand that the surface brightness limit is the 
raw noise level, @emph{not} the signal-to-noise.
+To get a feeling for it you can try these commands on any FITS image (let's 
assume its called @file{image.fits}), the output of the first command 
(@file{zero.fits}) will be the same size as the input, but all pixels will have 
a value of zero.
+We then add an ideal noise to this image and warp it to a new pixel size (such 
that the area of the new pixels is @code{area_per_pixel} times the input's), 
then we print the standard deviation of the raw noise and warped noise.
+Please open the output images an compare them (their sizes, or their pixel 
values) to get a good feeling of what is going on.
+Just note that this demo only works when @code{area_per_pixel} is larger than 
one.
+
+@example
+area_per_pixel=25
+scale=$(echo $area_per_pixel | awk '@{print sqrt($1)@}')
+astarithmetic image.fits -h0 nan + isblank not -ozero.fits
+astmknoise zero.fits -onoise.fits
+astwarp --scale=1/$scale,1/$scale noise.fits -onoise-w.fits
+std_raw=$(aststatistics noise.fits --std)
+std_warped=$(aststatistics noise-w.fits --std)
+echo;
+echo "(warped pixel area) = $area_per_pixel x (pixel area)"
+echo "Raw STD:    $std_raw"
+echo "Warped STD: $std_warped"
+@end example
+
+As you see in this example, this is thus just an extrapolation of the 
per-pixel measurement @mymath{\sigma_m}.
 So it should be used with extreme care: for example the dataset must have an 
approximately flat depth or noise properties overall.
-A more accurate measure for each detection over the dataset is known as the 
@emph{upper-limit magnitude} which actually uses random positioning of each 
detection's area/footprint (see below).
-It doesn't extrapolate and even accounts for correlated noise patterns in 
relation to that detection.
+A more accurate measure for each detection is known as the @emph{upper-limit 
magnitude} which actually uses random positioning of each detection's 
area/footprint, see the respective item below.
+The upper-limit magnitude doesn't extrapolate and even accounts for correlated 
noise patterns in relation to that detection.
 Therefore, the upper-limit magnitude is a much better measure of your 
dataset's surface brightness limit for each particular object.
 
 MakeCatalog will calculate the input dataset's @mymath{SB_{\rm Pixel}} and 
@mymath{SB_{\rm Projected}} and write them as comments/meta-data in the output 
catalog(s).
@@ -14290,7 +16723,7 @@ However in such a study we must be really careful to 
choose model profiles as si
 @item Magnitude measurement error (of each detection)
 Any measurement has an error and this includes the derived magnitude for an 
object.
 Note that this value is only meaningful when the object's magnitude is 
brighter than the upper-limit magnitude (see the next items in this list).
-As discussed in @ref{Flux Brightness and magnitude}, the magnitude 
(@mymath{M}) of an object with brightness @mymath{B} and Zeropoint magnitude 
@mymath{z} can be written as:
+As discussed in @ref{Brightness flux magnitude}, the magnitude (@mymath{M}) of 
an object with brightness @mymath{B} and zero point magnitude @mymath{z} can be 
written as:
 
 @dispmath{M=-2.5\log_{10}(B)+z}
 
@@ -14521,7 +16954,10 @@ The second main function (@code{columns_fill}) writes 
the final value into the a
 As you can see in the many existing examples, you can define your processing 
on the raw/internal calculations here and save them in the output.
 
 @item mkcatalog.c
-As described before, this file contains the two main MakeCatalog work-horses: 
@code{mkcatalog_first_pass} and @code{mkcatalog_second_pass}, their names are 
descriptive enough and their internals are also clear and heavily commented.
+This file contains the low-level parsing functions.
+To be optimized, the parsing is done in parallel through the 
@code{mkcatalog_single_object} function.
+This function initializes the necessary arrays and calls the lower-level 
@code{parse_objects} and @code{parse_clumps} for actually going over the pixels.
+They are all heavily commented, so you should be able to follow where to add 
your necessary low-level calculations.
 
 @item doc/gnuastro.texi
 Update this manual and add a description for the new column.
@@ -14646,9 +17082,9 @@ The full list of input dataset options and general 
setting options are described
 
 @table @option
 
-@item -l STR
-@itemx --clumpsfile=STR
-The file containing the labeled clumps dataset when @option{--clumpscat} is 
called (see @ref{MakeCatalog output}).
+@item -l FITS
+@itemx --clumpsfile=FITS
+The FITS file containing the labeled clumps dataset when @option{--clumpscat} 
is called (see @ref{MakeCatalog output}).
 When @option{--clumpscat} is called, but this option isn't, MakeCatalog will 
look into the main input file (given as an argument) for the required 
extension/HDU (value to @option{--clumpshdu}).
 
 @item --clumpshdu=STR
@@ -14657,8 +17093,8 @@ Only pixels with values above zero will be considered.
 The clump labels dataset has to be an integer data type (see @ref{Numeric data 
types}) and only pixels with a value larger than zero will be used.
 See @ref{Segment output} for a description of the expected format.
 
-@item -v STR
-@itemx --valuesfile=STR
+@item -v FITS
+@itemx --valuesfile=FITS
 The file name of the (sky-subtracted) values dataset.
 When any of the columns need values to associate with the input labels (for 
example to measure the brightness/magnitude of a galaxy), MakeCatalog will look 
into a ``values'' for the respective pixel values.
 In most common processing, this is the actual astronomical image that the 
labels were defined, or detected, over.
@@ -14668,8 +17104,8 @@ If this option is not called, MakeCatalog will look for 
the given extension in t
 @item --valueshdu=STR/INT
 The name or number (counting from zero) of the extension containing the 
``values'' dataset, see the descriptions above and those in 
@option{--valuesfile} for more.
 
-@item -s STR/FLT
-@itemx --insky=STR/FLT
+@item -s FITS/FLT
+@itemx --insky=FITS/FLT
 Sky value as a single number, or the file name containing a dataset (different 
values per pixel or tile).
 The Sky dataset is only necessary when @option{--subtractsky} is called or 
when a column directly related to the Sky value is requested (currently 
@option{--sky}).
 This dataset may be a tessellation, with one element per tile (see 
@option{--oneelempertile} of NoiseChisel's @ref{Processing options}).
@@ -14703,10 +17139,27 @@ The HDU of the Sky value standard deviation image.
 @item --variance
 The dataset given to @option{--stdfile} (and @option{--stdhdu} has the Sky 
variance of every pixel, not the Sky standard deviation.
 
+@item --forcereadstd
+Read the input STD image even if it is not required by any of the requested 
columns.
+This is because some of the output catalog's metadata may need it, for example 
to calculate the dataset's surface brightness limit (see @ref{Quantifying 
measurement limits}, configured with @option{--sfmagarea} and 
@option{--sfmagnsigma} in @ref{MakeCatalog output}).
+
 @item -z FLT
 @itemx --zeropoint=FLT
-The zero point magnitude for the input image, see @ref{Flux Brightness and 
magnitude}.
+The zero point magnitude for the input image, see @ref{Brightness flux 
magnitude}.
+
+@item --sigmaclip FLT,FLT
+The sigma-clipping parameters when any of the sigma-clipping related columns 
are requested (for example @option{--sigclip-median} or 
@option{--sigclip-number}).
 
+This option takes two values: the first is the multiple of @mymath{\sigma}, 
and the second is the termination criteria.
+If the latter is larger than 1, it is read as an integer number and will be 
the number of times to clip.
+If it is smaller than 1, it is interpreted as the tolerance level to stop 
clipping. See @ref{Sigma clipping} for a complete explanation.
+
+@item --fracmax=FLT[,FLT]
+The fractions (one or two) of maximum value in objects or clumps to be used in 
the related columns, for example @option{--fracmaxarea1}, 
@option{--fracmaxsum1} or @option{--fracmaxradius1}, see @ref{MakeCatalog 
measurements}.
+For the maximum value, see the description of @option{--maximum} column below.
+The value(s) of this option must be larger than 0 and smaller than 1 (they are 
a fraction).
+When only @option{--fracmaxarea1} or @option{--fracmaxsum1} is requested, one 
value must be given to this option, but if @option{--fracmaxarea2} or 
@option{--fracmaxsum2} are also requested, two values must be given to this 
option.
+The values can be written as simple floating point numbers, or as fractions, 
for example @code{0.25,0.75} and @code{0.25,3/4} are the same.
 @end table
 
 
@@ -14740,7 +17193,7 @@ If your dataset can allow it (it is large enough), it 
is recommended to use a la
 
 @table @option
 
-@item --upmaskfile=STR
+@item --upmaskfile=FITS
 File name of mask image to use for upper-limit calculation.
 In some cases (especially when doing matched photometry), the object labels 
specified in the main input and mask image might not be adequate.
 In other words they do not necessarily have to cover @emph{all} detected 
objects: the user might have selected only a few of the objects in their 
labeled image.
@@ -14860,6 +17313,24 @@ The geometric center of all objects and clumps along 
the second FITS axis axis,
 @item --geoz
 The geometric center of all objects and clumps along the third FITS axis axis, 
see @option{--geox}.
 
+@item --minvx
+Position of pixel with minimum value in objects and clumps, along the first 
FITS axis.
+
+@item --maxvx
+Position of pixel with minimum value in objects and clumps, along the first 
FITS axis.
+
+@item --minvy
+Position of pixel with minimum value in objects and clumps, along the first 
FITS axis.
+
+@item --maxvy
+Position of pixel with minimum value in objects and clumps, along the first 
FITS axis.
+
+@item --minvz
+Position of pixel with minimum value in objects and clumps, along the first 
FITS axis.
+
+@item --maxvz
+Position of pixel with minimum value in objects and clumps, along the first 
FITS axis.
+
 @item --minx
 The minimum position of all objects and clumps along the first FITS axis.
 
@@ -14969,8 +17440,8 @@ integral field unit data cubes.
 
 @item -b
 @itemx --brightness
-The brightness (sum of all pixel values), see @ref{Flux Brightness and 
magnitude}.
-For clumps, the ambient brightness (flux of river pixels around the clump 
multiplied by the area of the clump) is removed, see @option{--riverflux}.
+The brightness (sum of all pixel values), see @ref{Brightness flux magnitude}.
+For clumps, the ambient brightness (flux of river pixels around the clump 
multiplied by the area of the clump) is removed, see @option{--riverave}.
 So the sum of all the clumps brightness in the clump catalog will be smaller 
than the total clump brightness in the @option{--clumpbrightness} column of the 
objects catalog.
 
 If no usable pixels are present over the clump or object (for example they are 
all blank), the returned value will be NaN (note that zero is meaningful).
@@ -14998,6 +17469,29 @@ For clumps, the average river flux is subtracted from 
the sky subtracted mean.
 The median sky subtracted value of pixels within the object or clump.
 For clumps, the average river flux is subtracted from the sky subtracted 
median.
 
+@item --maximum
+The maximum value of pixels within the object or clump.
+When the label (object or clump) is larger than three pixels, the maximum is 
actually derived by the mean of the brightest three pixels, not the largest 
pixel value of the same label.
+This is because noise fluctuations can be very strong in the extreme values of 
the objects/clumps due to Poisson noise (which gets stronger as the mean gets 
higher).
+Simply using the maximum pixel value will create a strong scatter in results 
that depend on the maximum (for example the @option{--fwhm} option also uses 
this value internally).
+
+@item --sigclip-number
+The number of elements/pixels in the dataset after sigma-clipping the object 
or clump.
+The sigma-clipping parameters can be set with the @option{--sigmaclip} option 
described in @ref{MakeCatalog inputs and basic settings}.
+For more on Sigma-clipping, see @ref{Sigma clipping}.
+
+@item --sigclip-median
+The sigma-clipped median value of the object of clump's pixel distribution.
+For more on sigma-clipping and how to define it, see @option{--sigclip-number}.
+
+@item --sigclip-mean
+The sigma-clipped mean value of the object of clump's pixel distribution.
+For more on sigma-clipping and how to define it, see @option{--sigclip-number}.
+
+@item --sigclip-std
+The sigma-clipped standard deviation of the object of clump's pixel 
distribution.
+For more on sigma-clipping and how to define it, see @option{--sigclip-number}.
+
 @item -m
 @itemx --magnitude
 The magnitude of clumps or objects, see @option{--brightness}.
@@ -15057,7 +17551,7 @@ So note that this value is @emph{not} sky subtracted.
 
 @item --rivernum
 [Clumps] The number of river pixels around this clump, see
-@option{--riverflux}.
+@option{--riverave}.
 
 @item -n
 @itemx --sn
@@ -15080,6 +17574,21 @@ This is the square root of the mean variance under the 
object, or the root mean
 @itemx --area
 The raw area (number of pixels/voxels) in any clump or object independent of 
what pixel it lies over (if it is NaN/blank or unused for example).
 
+@item --areaarcsec2
+The used (non-blank in values image) area of the labeled region in units of 
arc-seconds squared.
+This column is just the value of the @option{--area} column, multiplied by the 
area of each pixel in the input image (in units of arcsec^2).
+Similar to the @option{--ra} or @option{--dec} columns, for this option to 
work, the objects extension used has to have a WCS structure.
+
+@item --areaminv
+The number of pixels that are equal to the minimum value of the labeled region 
(clump or object).
+
+@item --areamaxv
+The number of pixels that are equal to the maximum value of the labeled region 
(clump or object).
+
+@item --surfacebrightness
+The surface brightness (in units of mag/arcsec@mymath{^2}) of the labeled 
region (objects or clumps).
+For more on the definition of the surface brightness, see @ref{Brightness flux 
magnitude}.
+
 @item --areaxy
 @cindex IFU: Integral Field Unit
 @cindex Integral Field Unit
@@ -15088,6 +17597,95 @@ first two dimensions. This is only available for 
3-dimensional
 datasets. When working with Integral Field Unit (IFU) datasets, this
 projection onto the first two dimensions would be a narrow-band image.
 
+@item --fwhm
+@cindex FWHM
+The full width at half maximum (in units of pixels, along the semi-major axis) 
of the labeled region (object or clump).
+The maximum value is estimated from the mean of the top-three pixels with the 
highest values, see the description under @option{--maximum}.
+The number of pixels that have half the value of that maximum are then found 
(value in the @option{--halfmaxarea} column) and a radius is estimated from the 
area.
+See the description under @option{--halfsumradius} for more on converting area 
to radius along major axis.
+
+Because of its non-parametric nature, this column is most reliable on clumps 
and should only be used in objects with great caution.
+This is because objects can have more than one clump (peak with true signal) 
and multiple peaks are not treated separately in objects, so the result of this 
column will be biased.
+
+Also, because of its non-parametric nature, this FWHM it does not account for 
the PSF, and it will be strongly affected by noise if the object is faint.
+So when half the maximum value (which can be requested using the 
@option{--maximum} column) is too close to the local noise level (which can be 
requested using the @option{--std} column), the value returned in this column 
is meaningless (its just noise peaks which are randomly distributed over the 
area).
+You can therefore use the @option{--maximum} and @option{--std} columns to 
remove unreliable FWHMs.
+For example if a labeled region's maximum is less than 2 times the sky 
standard deviation, the value will certainly be unreliable (half of that is 
@mymath{1\sigma}!).
+For a more reliable value, this fraction should be around 4 (so half the 
maximum is 2@mymath{\sigma}).
+
+@item --halfmaxarea
+The number of pixels with values larger than half the maximum flux within the 
labeled region.
+This option is used to estimate @option{--fwhm}, so please read the notes 
there for the caveats and necessary precautions.
+
+@item --halfmaxradius
+The radius of region containing half the maximum flux within the labeled 
region.
+This is just half the value reported by @option{--fwhm}.
+
+@item --halfmaxsum
+The sum of the pixel values containing half the maximum flux within the 
labeled region (or those that are counted in @option{--halfmaxarea}).
+This option uses the pixels within @option{--fwhm}, so please read the notes 
there for the caveats and necessary precautions.
+
+@item --halfmaxsb
+The surface brightness (in units of mag/arcsec@mymath{^2}) within the region 
that contains half the maximum value of the labeled region.
+For more on the definition of the surface brightness, see @ref{Brightness flux 
magnitude}.
+
+@item --halfsumarea
+The number of pixels that contain half the object or clump's total sum of 
pixels (half the value in the @option{--brightness} column).
+To count this area, all the non-blank values associated with the given label 
(object or clump) will be sorted and summed in order (starting from the 
maximum), until the sum becomes larger than half the total sum of the label's 
pixels.
+
+This option is thus good for clumps (which are defined to have a single peak 
in their morphology), but for objects you should be careful because if they may 
have multiple peaks and the sorting by value doesn't include morphology.
+So if the object includes multiple peaks/clumps at roughly the same level, 
then the area reported by this option will be distributed over all the peaks.
+
+@item --halfsumsb
+Surface brightness (in units of mag/arcsec@mymath{^2}) within the area that 
contains half the total sum of the label's pixels (object or clump).
+For more on the definition of the surface brightness, see @ref{Brightness flux 
magnitude}.
+
+This column just plugs in the values of half the value of the 
@option{--brightness} column and the @option{--halfsumarea} column, into the 
surface brightness equation.
+Therefore please see the description in @option{--halfsumarea} to understand 
the systematics of this column and potential biases.
+
+@item --halfsumradius
+Radius (in units of pixels) derived from the area that contains half the total 
sum of the label's pixels (value reported by @option{--halfsumarea}).
+If the area is @mymath{A_h} and the axis ratio is @mymath{q}, then the value 
returned in this column is @mymath{\sqrt{A_h/({\pi}q)}}.
+This option is a good measure of the concentration of the @emph{observed} 
(after PSF convolution and noisy) object or clump,
+But as described below it underestimates the effective radius.
+Also, it should be used in caution with objects, it reliable with clumps, see 
the note under @option{--halfradiusarea}.
+
+@cindex Ellipse area
+@cindex Area, ellipse
+Recall that in general, for an ellipse with semi-major axis @mymath{a}, 
semi-minor axis @mymath{b}, and axis ratio @mymath{q=b/a} the area (@mymath{A}) 
is @mymath{A={\pi}ab={\pi}qa^2}.
+For a circle (where @mymath{q=1}), this simplifies to the familiar 
@mymath{A={\pi}a^2}.
+
+@cindex S@'ersic profile
+@cindex Effective radius
+The suffix @code{obs} is added to the option name to avoid confusing this with 
a profile's effective radius for S@'ersic profiles, commonly written as 
@mymath{r_e}.
+For more on @mymath{r_e}, please see @ref{Galaxies}.
+Therefore, when @mymath{r_e} is meaningful for the target (the target is 
elliptically symmetric and can be parameterized as a S@'ersic profile), 
@mymath{r_e} should be derived from fitting the profile with a S@'ersic 
function which has been convolved with the PSF.
+But from the equation above, you see that this radius is derived from the raw 
image's labeled values (after convolution, with no parametric profile), so this 
column's value will generally be (much) smaller than @mymath{r_e}, depending on 
the PSF, depth of the dataset, the morphology, or if a fraction of the profile 
falls on the edge of the image.
+
+@item --fracmaxarea1
+@itemx --fracmaxarea2
+Number of pixels brighter than the given fraction(s) of the maximum pixel 
value.
+For the maximum value, see the description of @option{--maximum} column.
+The fraction(s) are given through the @option{--fracmax} option (that can take 
two values) and is described in @ref{MakeCatalog inputs and basic settings}.
+Recall that in @option{--halfmaxarea}, the fraction is fixed to 0.5.
+Hence, added with these two columns, you can sample three parts of the profile 
area.
+
+@item --fracmaxsum1
+@itemx --fracmaxsum2
+Sum of pixels brighter than the given fraction(s) of the maximum pixel value.
+For the maximum value, see the description of @option{--maximum} column below.
+The fraction(s) are given through the @option{--fracmax} option (that can take 
two values) and is described in @ref{MakeCatalog inputs and basic settings}.
+Recall that in @option{--halfmaxsum}, the fraction is fixed to 0.5.
+Hence, added with these two columns, you can sample three parts of the 
profile's sum of pixels.
+
+@item --fracmaxradius1
+@itemx --fracmaxradius2
+Radius (in units of pixels) derived from the area that contains the given 
fractions of the maximum valued pixel(s) of the label's pixels (value reported 
by @option{--fracmaxarea1} or @option{--fracmaxarea2}).
+For the maximum value, see the description of @option{--maximum} column below.
+The fractions are given through the @option{--fracmax} option (that can take 
two values) and is described in @ref{MakeCatalog inputs and basic settings}.
+Recall that in @option{--fwhm}, the fraction is fixed to 0.5.
+Hence, added with these two columns, you can sample three parts of the 
profile's radius.
+
 @item --clumpsarea
 [Objects] The total area of all the clumps in this object.
 
@@ -15175,6 +17773,13 @@ pixels that belong to other labels but overlap with 
the 2D projection. This
 can be used to see how reliable the emission line measurement is (on the
 projected spectra) and also if multiple lines (labeled regions) belong to
 the same physical object.
+
+@item --inbetweenints
+Output will contain one row for all integers between 1 and the largest label 
in the input (irrespective of their existance in the input image).
+By default, MakeCatalog's output will only contain rows with integers that 
actually corresponded to at least one pixel in the input dataset.
+
+For example if the input's only labeled pixel values are 11 and 13, 
MakeCatalog's default output will only have two rows.
+If you use this option, it will have 13 rows and all the columns corresponding 
to integer identifiers that didn't correspond to any pixel will be 0 or NaN 
(depending on context).
 @end table
 
 
@@ -15223,11 +17828,15 @@ $ awk '!/^#/' out_c.txt | sort -g -k1,1 -k2,2
 @end example
 
 @item --sfmagnsigma=FLT
-The median standard deviation (from a @command{MEDSTD} keyword in the Sky 
standard deviation image) will be multiplied by the value to this option and 
its magnitude will be reported in the comments of the output catalog.
+Value to multiply with the median standard deviation (from a @command{MEDSTD} 
keyword in the Sky standard deviation image) for estimating the surface 
brightness limit.
+Note that the surface brightness limit is only reported when a standard 
deviation image is read, in other words a column using it is requested (for 
example @option{--sn}) or @option{--forcereadstd} is called.
+
 This value is a per-pixel value, not per object/clump and is not found over an 
area or aperture, like the common @mymath{5\sigma} values that are commonly 
reported as a measure of depth or the upper-limit measurements (see 
@ref{Quantifying measurement limits}).
 
 @item --sfmagarea=FLT
-Area (in arcseconds squared) to convert the per-pixel estimation of 
@option{--sfmagnsigma} in the comments section of the output tables.
+Area (in arc-seconds squared) to convert the per-pixel estimation of 
@option{--sfmagnsigma} in the comments section of the output tables.
+Note that the surface brightness limit is only reported when a standard 
deviation image is read, in other words a column using it is requested (for 
example @option{--sn}) or @option{--forcereadstd} is called.
+
 Note that this is just a unit conversion using the World Coordinate System 
(WCS) information in the input's header.
 It does not actually do any measurements on this area.
 For random measurements on any area, please use the upper-limit columns of 
MakeCatalog (see the discussion on upper-limit measurements in @ref{Quantifying 
measurement limits}).
@@ -15236,7 +17845,7 @@ For random measurements on any area, please use the 
upper-limit columns of MakeC
 
 
 
-@node Match,  , MakeCatalog, Data analysis
+@node Match, Sort FITS files by night, MakeCatalog, Data analysis
 @section Match
 
 Data can come come from different telescopes, filters, software and even 
different configurations for a single software.
@@ -15283,7 +17892,7 @@ $ astmatch --aperture=2 input1.txt input2.fits          
         \
            --outcols=a1,aRA,aDEC,b/^MAG/,bBRG,a10
 
 ## Match the two catalogs within an elliptical aperture of 1 and 2
-## arcseconds along RA and Dec respectively.
+## arc-seconds along RA and Dec respectively.
 $ astmatch --aperture=1/3600,2/3600 in1.fits in2.txt
 
 ## Match the RA and DEC columns of the first input with the RA_D
@@ -15371,10 +17980,9 @@ But when using @option{--outcols}, you can't give 
@code{bRA}, or @code{b25}.
 
 @item -l
 @itemx --logasoutput
-The output file will have the contents of the log file: indexes 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 @file{astmatch.txt} will not be 
created.
-With this option, the default output behavior (two tables containing the 
re-arranged inputs) will be
+The output file will have the contents of the log file: indexes in the two 
catalogs that match with each other along with their distance, see description 
of the log file above.
+
+When this option is called, a separate log file will not be created and the 
output will not contain any of the input columns (either as two tables 
containing the re-arranged columns of each input, or a single table mixing 
columns), only their indexs in the log format.
 
 @item --notmatched
 Write the non-matching rows into the outputs, not the matched ones.
@@ -15390,92 +17998,443 @@ The values can be the column number (counting from 
1), exact column name or a re
 For more, see @ref{Selecting table columns}.
 See the one-line examples above for some usages of this option.
 
-@item -C INT/STR[,INT/STR]
-@itemx --ccol2=INT/STR[,INT/STR]
-The coordinate columns of the second input.
-See the example in @option{--ccol1} for more.
+@item -C INT/STR[,INT/STR]
+@itemx --ccol2=INT/STR[,INT/STR]
+The coordinate columns of the second input.
+See the example in @option{--ccol1} for more.
+
+@item -d FLT[,FLT]
+@itemx --coord=FLT[,FLT]
+Manually specify the coordinates to match against the given catalog.
+With this option, Match will not look for a second input file/table and will 
directly use the coordinates given to this option.
+
+When this option is called, the output changes in the following ways:
+1) when @option{--outcols} is specified, for the second input, it can only 
accept integer numbers that are less than the number of values given to this 
option, see description of that option for more.
+2) By default (when @option{--outcols} isn't used), only the matching row of 
the first table will be output (a single file), not two separate files (one for 
each table).
+
+This option is good when you have a (large) catalog and only want to match a 
single coordinate to it (for example to find the nearest catalog entry to your 
desired point).
+With this option, you can write the coordinates on the command-line and thus 
avoid the need to make a single-row file.
+
+@item -a FLT[,FLT[,FLT]]
+@itemx --aperture=FLT[,FLT[,FLT]]
+Parameters of the aperture for matching.
+The values given to this option can be fractions, for example when the 
position columns are in units of degrees, @option{1/3600} can be used to ask 
for one arc-second.
+The interpretation of the values depends on the requested dimensions 
(determined from @option{--ccol1} and @code{--ccol2}) and how many values are 
given to this option.
+
+When multiple objects are found within the aperture, the match is defined
+as the nearest one. In a multi-dimensional dataset, when the aperture is a
+general ellipse or ellipsoid (and not a circle or sphere), the distance is
+calculated in the elliptical space along the major axis. For the defintion
+of this distance, see @mymath{r_{el}} in @ref{Defining an ellipse and
+ellipsoid}.
+
+@table @asis
+@item 1D match
+The aperture/interval can only take one value: half of the interval around 
each point (maximum distance from each point).
+
+@item 2D match
+In a 2D match, the aperture can be a circle, an ellipse aligned in the axes or 
an ellipse with a rotated major axis.
+To simply the usage, you can determine the shape based on the number of free 
parameters for each.
+
+@table @asis
+@item 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 @option{--ccol1} and 
@option{--ccol2}).
+
+@item 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 
@mymath{4\times10^{-10}} (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 and ellipsoid}.
+
+@item 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 and ellipsoid}.
+@end table
+
+@item 3D match
+The aperture (matching volume) can be a sphere, an ellipsoid aligned on the 
three axises or a genenral ellipsoid rotated in any direction.
+To simplifythe usage, the shape can be determined based on the number of 
values given to this option.
+
+@table @asis
+@item 1 number
+For example @option{--aperture=3}.
+The matching volume will be a sphere of the given radius.
+The value is in the same units as the input coordinates.
+
+@item 3 numbers
+For example @option{--aperture=4,5,6e-10}.
+The aperture will be a general ellipsoid with the respective extent along each 
dimension.
+The numbers must be in the same units as each axis.
+This is very similar to the two number case of 2D inputs.
+See there for more.
+
+@item 6 numbers
+For example @option{--aperture=4,0.5,0.6,10,20,30}.
+The numbers represent the full general ellipsoid definition (in any 
orientation).
+For the definition of a general ellipsoid, see @ref{Defining an ellipse and 
ellipsoid}.
+The first number is the semi-major axis.
+The second and third are the two axis ratios.
+The last three are the three Euler angles in units of degrees in the ZXZ order 
as fully described in @ref{Defining an ellipse and ellipsoid}.
+@end table
+
+@end table
+@end table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@node Sort FITS files by night, SAO DS9 region files from table, Match, Data 
analysis
+@section Sort FITS files by night
+
+@cindex Calendar
+FITS images usually contain (several) keywords for preserving important dates.
+In particular, for lower-level data, this is usually the observation date and 
time (for example, stored in the @code{DATE-OBS} keyword value).
+When analyzing observed datasets, many calibration steps (like the dark, bias 
or flat-field), are commonly calculated on a per-observing-night basis.
+
+However, the FITS standard's date format (@code{YYYY-MM-DDThh:mm:ss.ddd}) is 
based on the western (Gregorian) calendar.
+Dates that are stored in this format are complicated for automatic processing: 
a night starts in the final hours of one calendar day, and extends to the early 
hours of the next calendar day.
+As a result, to identify datasets from one night, we commonly need to search 
for two dates.
+However calendar peculiarities can make this identification very difficult.
+For example when an observation is done on the night separating two months 
(like the night starting on March 31st and going into April 1st), or two years 
(like the night starting on December 31st 2018 and going into January 1st, 
2019).
+To account for such situations, it is necessary to keep track of how many days 
are in a month, and leap years, etc.
+
+@cindex Unix epoch time
+@cindex Time, Unix epoch
+@cindex Epoch, Unix time
+Gnuastro's @file{astscript-sort-by-night} script is created to help in such 
important scenarios.
+It uses @ref{Fits} to convert the FITS date format into the Unix epoch time 
(number of seconds since 00:00:00 of January 1st, 1970), using the 
@option{--datetosec} option.
+The Unix epoch time is a single number (integer, if not given in sub-second 
precision), enabling easy comparison and sorting of dates after January 1st, 
1970.
+
+You can use this script as a basis for making a much more highly customized 
sorting script.
+Here are some examples
+
+@itemize
+@item
+If you need to copy the files, but only need a single extension (not the whole 
file), you can add a step just before the making of the symbolic links, or 
copies, and change it to only copy a certain extension of the FITS file using 
the Fits program's @option{--copy} option, see @ref{HDU information and 
manipulation}.
+
+@item
+If you need to classify the files with finer detail (for example the purpose 
of the dataset), you can add a step just before the making of the symbolic 
links, or copies, to specify a file-name prefix based on other certain keyword 
values in the files.
+For example when the FITS files have a keyword to specify if the dataset is a 
science, bias, or flat-field image.
+You can read it and to add a @code{sci-}, @code{bias-}, or @code{flat-} to the 
created file (after the @option{--prefix}) automatically.
+
+For example, let's assume the observing mode is stored in the hypothetical 
@code{MODE} keyword, which can have three values of @code{BIAS-IMAGE}, 
@code{SCIENCE-IMAGE} and @code{FLAT-EXP}.
+With the step below, you can generate a mode-prefix, and add it to the 
generated link/copy names (just correct the filename and extension of the first 
line to the script's variables):
+
+@example
+modepref=$(astfits infile.fits -h1 \
+                   | sed -e"s/'/ /g" \
+                   | awk '$1=="MODE"@{ \
+                       if($3=="BIAS-IMAGE") print "bias-"; \
+                       else if($3=="SCIENCE-IMAGE") print "sci-"; \
+                       else if($3==FLAT-EXP) print "flat-"; \
+                       else print $3, "NOT recognized"; exit 1@}')
+@end example
+
+@cindex GNU AWK
+@cindex GNU Sed
+Here is a description of it.
+We first use @command{astfits} to print all the keywords in extension @code{1} 
of @file{infile.fits}.
+In the FITS standard, string values (that we are assuming here) are placed in 
single quotes (@key{'}) which are annoying in this context/use-case.
+Therefore, we pipe the output of @command{astfits} into @command{sed} to 
remove all such quotes (substituting them with a blank space).
+The result is then piped to AWK for giving us the final mode-prefix: with 
@code{$1=="MODE"}, we ask AWK to only consider the line where the first column 
is @code{MODE}.
+There is an equal sign between the key name and value, so the value is the 
third column (@code{$3} in AWK).
+We thus use a simple @code{if-else} structure to look into this value and 
print our custom prefix based on it.
+The output of AWK is then stored in the @code{modepref} shell variable which 
you can add to the link/copy name.
+
+With the solution above, the increment of the file counter for each night will 
be independent of the mode.
+If you want the counter to be mode-dependent, you can add a different counter 
for each mode and use that counter instead of the generic counter for each 
night (based on the value of @code{modepref}).
+But we'll leave the implementation of this step to you as an exercise.
+
+@end itemize
+
+@menu
+* Invoking astscript-sort-by-night::  Inputs and outputs to this script.
+@end menu
+
+@node Invoking astscript-sort-by-night,  , Sort FITS files by night, Sort FITS 
files by night
+@subsection Invoking astscript-sort-by-night
+
+This installed script will read a FITS date formatted value from the given 
keyword, and classify the input FITS files into individual nights.
+For more on installed scripts please see (see @ref{Installed scripts}).
+This script can be used with the following general template:
+
+@example
+$ astscript-sort-by-night [OPTION...] FITS-files
+@end example
+
+@noindent
+One line examples:
+
+@example
+## Use the DATE-OBS keyword
+$ astscript-sort-by-night --key=DATE-OBS /path/to/data/*.fits
+
+## Make links to the input files with the `img-' prefix
+$ astscript-sort-by-night --link --prefix=img- /path/to/data/*.fits
+@end example
+
+This script will look into a HDU/extension (@option{--hdu}) for a keyword 
(@option{--key}) in the given FITS files and interpret the value as a date.
+The inputs will be separated by "night"s (11:00a.m to next day's 10:59:59a.m, 
spanning two calendar days, exact hour can be set with @option{--hour}).
+
+The default output is a list of all the input files along with the following 
two columns: night number and file number in that night (sorted by time).
+With @option{--link} a symbolic link will be made (one for each input) that 
contains the night number, and number of file in that night (sorted by time), 
see the description of @option{--link} for more.
+When @option{--copy} is used instead of a link, a copy of the inputs will be 
made instead of symbolic link.
+
+Below you can see one example where all the @file{target-*.fits} files in the 
@file{data} directory should be separated by observing night according to the 
@code{DATE-OBS} keyword value in their second extension (number @code{1}, 
recall that HDU counting starts from 0).
+You can see the output after the @code{ls} command.
+
+@example
+$ astscript-sort-by-night -pimg- -h1 -kDATE-OBS data/target-*.fits
+$ ls
+img-n1-1.fits img-n1-2.fits img-n2-1.fits ...
+@end example
+
+The outputs can be placed in a different (already existing) directory by 
including that directory's name in the @option{--prefix} value, for example 
@option{--prefix=sorted/img-} will put them all under the @file{sorted} 
directory.
+
+This script can be configured like all Gnuastro's programs (through 
command-line options, see @ref{Common options}), with some minor differences 
that are described in @ref{Installed scripts}.
+The particular options to this script are listed below:
+
+@table @option
+@item -h STR
+@itemx --hdu=STR
+The HDU/extension to use in all the given FITS files.
+All of the given FITS files must have this extension.
+
+@item -k STR
+@itemx --key=STR
+The keyword name that contains the FITS date format to classify/sort by.
+
+@item -H FLT
+@itemx --hour=FLT
+The hour that defines the next ``night''.
+By default, all times before 11:00a.m are considered to belong to the previous 
calendar night.
+If a sub-hour value is necessary, it should be given in units of hours, for 
example @option{--hour=9.5} corresponds to 9:30a.m.
+
+@cartouche
+@noindent
+@cindex Time zone
+@cindex UTC (Universal time coordinate)
+@cindex Universal time coordinate (UTC)
+@strong{Dealing with time zones:}
+The time that is recorded in @option{--key} may be in UTC (Universal Time 
Coordinate).
+However, the organization of the images taken during the night depends on the 
local time.
+It is possible to take this into account by setting the @option{--hour} option 
to the local time in UTC.
+
+For example, consider a set of images taken in Auckland (New Zealand, UTC+12) 
during different nights.
+If you want to classify these images by night, you have to know at which time 
(in UTC time) the Sun rises (or any other separator/definition of a different 
night).
+For example if your observing night finishes before 9:00a.m in Auckland, you 
can use @option{--hour=21}.
+Because in Auckland the local time of 9:00 corresponds to 21:00 UTC.
+@end cartouche
+
+@item -l
+@itemx --link
+Create a symbolic link for each input FITS file.
+This option cannot be used with @option{--copy}.
+The link will have a standard name in the following format (variable parts are 
written in @code{CAPITAL} letters and described after it):
+
+@example
+PnN-I.fits
+@end example
+
+@table @code
+@item P
+This is the value given to @option{--prefix}.
+By default, its value is @code{./} (to store the links in the directory this 
script was run in).
+See the description of @code{--prefix} for more.
+@item N
+This is the night-counter: starting from 1.
+@code{N} is just incremented by 1 for the next night, no matter how many 
nights (without any dataset) there are between two subsequent observing nights 
(its just an identifier for each night which you can easily map to different 
calendar nights).
+@item I
+File counter in that night, sorted by time.
+@end table
+
+@item -c
+@itemx --copy
+Make a copy of each input FITS file with the standard naming convention 
described in @option{--link}.
+With this option, instead of making a link, a copy is made.
+This option cannot be used with @option{--link}.
+
+@item -p STR
+@itemx --prefix=STR
+Prefix to append before the night-identifier of each newly created link or 
copy.
+This option is thus only relevant with the @option{--copy} or @option{--link} 
options.
+See the description of @option{--link} for how its used.
+For example, with @option{--prefix=img-}, all the created file names in the 
current directory will start with @code{img-}, making outputs like 
@file{img-n1-1.fits} or @file{img-n3-42.fits}.
+
+@option{--prefix} can also be used to store the links/copies in another 
directory relative to the directory this script is being run (it must already 
exist).
+For example @code{--prefix=/path/to/processing/img-} will put all the 
links/copies in the @file{/path/to/processing} directory, and the files (in 
that directory) will all start with @file{img-}.
+@end table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@node SAO DS9 region files from table,  , Sort FITS files by night, Data 
analysis
+@section SAO DS9 region files from table
+
+Once your desired catalog (containing the positions of some objects) is 
created (for example with @ref{MakeCatalog}, @ref{Match}, or @ref{Table}) it 
often happens that you want to see your selected objects on an image for a 
feeling of the spatial properties of your objects.
+For example you want to see their positions relative to each other.
+
+In this section we describe a simple installed script that is provided within 
Gnuastro for converting your given columns to an SAO DS9 region file to help in 
this process.
+SAO DS9@footnote{@url{https://sites.google.com/cfa.harvard.edu/saoimageds9}} 
is one of the most common FITS image vizualization tools in astronomy and is 
free software.
+
+@menu
+* Invoking astscript-make-ds9-reg::  How to call astscript-make-ds9-reg
+@end menu
+
+@node Invoking astscript-make-ds9-reg,  , SAO DS9 region files from table, SAO 
DS9 region files from table
+@subsection Invoking astscript-make-ds9-reg
+
+This installed script will read two positional columns within an input table 
and generate an SAO DS9 region file to visualize the position of the given 
objects over an image.
+For more on installed scripts please see (see @ref{Installed scripts}).
+This script can be used with the following general template:
+
+@example
+## Use the RA and DEC columns of 'table.fits' for the region file.
+$ astscript-make-ds9-reg table.fits --column=RA,DEC \
+                          --output=ds9.reg
+
+## Select objects with a magnitude between 18 to 20, and generate the
+## region file directly (through a pipe), each region with radius of
+## 0.5 arcseconds.
+$ asttable table.fits --range=MAG,18:20 --column=RA,DEC \
+      | astscript-make-ds9-reg --column=1,2 --radius=0.5
+
+## With the first command, select objects with a magnitude of 25 to 26
+## as red regions in 'bright.reg'. With the second command, select
+## objects with a magnitude between 28 to 29 as a green region and
+## show both.
+$ asttable cat.fits --range=MAG_F160W,25:26 -cRA,DEC \
+      | ./astscript-make-ds9-reg -c1,2 --color=red -obright.reg
+$ asttable cat.fits --range=MAG_F160W,28:29 -cRA,DEC \
+      | ./astscript-make-ds9-reg -c1,2 --color=green \
+                    --command="ds9 image.fits -regions bright.reg"
+@end example
+
+The input can either be passed as a named file, or from standard input (a 
pipe).
+Only the @option{--column} option is mandatory (to specify the input table 
columns): two colums from the input table must be specified, either by name 
(recommended) or number.
+You can optionally also specify the region's radius, width and color of the 
regions with the @option{--radius}, @option{--width} and @option{--color} 
options, otherwise default values will be used for these (described under each 
option).
 
-@item -d FLT[,FLT]
-@itemx --coord=FLT[,FLT]
-Manually specify the coordinates to match against the given catalog.
-With this option, Match will not look for a second input file/table and will 
directly use the coordinates given to this option.
+The created region file will be written into the file name given to 
@option{--output}.
+When @option{--output} isn't called, the default name of @file{ds9.reg} will 
be used (in the running directory).
+If the file exists before calling this script, it will be overwritten, unless 
you pass the @option{--dontdelete} option.
+Optionally you can also use the @option{--command} option to give the full 
command that should be run to execute SAO DS9 (see example above and 
description below).
+In this mode, the created region file will be deleted once DS9 is closed 
(unless you pass the @option{--dontdelete} option).
+A full description of each option is given below.
 
-When this option is called, the output changes in the following ways:
-1) when @option{--outcols} is specified, for the second input, it can only 
accept integer numbers that are less than the number of values given to this 
option, see description of that option for more.
-2) By default (when @option{--outcols} isn't used), only the matching row of 
the first table will be output (a single file), not two separate files (one for 
each table).
+@table @option
 
-This option is good when you have a (large) catalog and only want to match a 
single coordinate to it (for example to find the nearest catalog entry to your 
desired point).
-With this option, you can write the coordinates on the command-line and thus 
avoid the need to make a single-row file.
+@item -h INT/STR
+@item --hdu INT/STR
+The HDU of the input table when a named FITS file is given as input.
+The HDU (or extension) can be either a name or number (counting from zero).
+For more on this option, see @ref{Input output options}.
+
+@item -c STR,STR
+@itemx --column=STR,STR
+Identifiers of the two positional columns to use in the DS9 region file from 
the table.
+They can either be in WCS (RA and Dec) or image (pixel) coordiantes.
+The mode can be specified with the @option{--mode} option, described below.
+
+@item -m wcs|img
+@itemx --mode=wcs|org
+The coordinate system of the positional columns (can be either 
@option{--mode=wcs} and @option{--mode=img}).
+In the WCS mode, the values within the columns are interpreted to be RA and 
Dec.
+In the image mode, they are interpreted to be pixel X and Y positions.
+This option also affects the interpretation of the value given to 
@option{--radius}.
+When this option isn't explicitly given, the columns are assumed to be in WCS 
mode.
 
-@item -a FLT[,FLT[,FLT]]
-@itemx --aperture=FLT[,FLT[,FLT]]
-Parameters of the aperture for matching.
-The values given to this option can be fractions, for example when the 
position columns are in units of degrees, @option{1/3600} can be used to ask 
for one arcsecond.
-The interpretation of the values depends on the requested dimensions 
(determined from @option{--ccol1} and @code{--ccol2}) and how many values are 
given to this option.
+@item -C STR
+@itemx --color=STR
+The color to use for created regions.
+These will be directly interpreted by SAO DS9 when it wants to open the region 
file so it must be recognizable by SAO DS9.
+As of SAO DS9 8.2, the recognized color names are @code{black}, @code{white}, 
@code{red}, @code{green}, @code{blue}, @code{cyan}, @code{magenta} and 
@code{yellow}.
+The default color (when this option is not called) is @code{green}
+
+@item -w INT
+@itemx --width=INT
+The line width of the regions.
+These will be directly interpreted by SAO DS9 when it wants to open the region 
file so it must be recognizable by SAO DS9.
+The default value is @code{1}.
 
-When multiple objects are found within the aperture, the match is defined
-as the nearest one. In a multi-dimensional dataset, when the aperture is a
-general ellipse or ellipsoid (and not a circle or sphere), the distance is
-calculated in the elliptical space along the major axis. For the defintion
-of this distance, see @mymath{r_{el}} in @ref{Defining an ellipse and
-ellipsoid}.
+@item -r FLT
+@itemx --radius=FLT
+The radius of all the regions.
+In WCS mode, the radius is assumed to be in arc-seconds, in image mode, it is 
in pixel units.
+If this option is not explicitly given, in WCS mode the default radius is 1 
arc-seconds and in image mode it is 3 pixels.
 
-@table @asis
-@item 1D match
-The aperture/interval can only take one value: half of the interval around 
each point (maximum distance from each point).
+@item --dontdelete
+If the output file name exists, abort the program and don't over-write the 
contents of the file.
+This option is thus good if you want to avoid accidentally writing over an 
important file.
+Also, don't delete the created region file when @option{--command} is given 
(by default, when @option{--command} is given, the created region file will be 
deleted after SAO DS9 closes).
 
-@item 2D match
-In a 2D match, the aperture can be a circle, an ellipse aligned in the axes or 
an ellipse with a rotated major axis.
-To simply the usage, you can determine the shape based on the number of free 
parameters for each.
+@item -o STR
+@itemx --output=STR
+Write the created SAO DS9 region file into the name given to this option.
+If not explicity given on the command-line, a default name of @file{ds9.reg} 
will be used.
+If the file already exists, it will be over-written, you can avoid the 
deletion (or over-writing) of an existing file with the @option{--dontdelete}.
 
-@table @asis
-@item 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 @option{--ccol1} and 
@option{--ccol2}).
+@item --command="STR"
+After creating the region file, run the string given to this option as a 
command-line command.
+The SAO DS9 region command will be appended to the end of the given command.
+Because the command will mostly likely contain white-space characters it is 
recommended to put the given string in double quotations.
 
-@item 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 
@mymath{4\times10^{-10}} (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 and ellipsoid}.
+For example, let's assume @option{--command="ds9 image.fits -zscale"}.
+After making the region file (assuming it is called @file{ds9.reg}), the 
following command will be executed:
 
-@item 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 and ellipsoid}.
+@example
+ds9 image.fits -zscale -regions ds9.reg
+@end example
+
+You can customize all aspects of SAO DS9 with its command-line options, 
therefore the value of this option can be as long and complicated as you like.
+For example if you also want the image to fit into the window, this option 
will be: @command{--command="ds9 image.fits -zscale -zoom to fit"}.
+You can see the SAO DS9 command-line descriptions by clicking on the ``Help'' 
menu and selecting ``Reference Manual''.
+In the opened window, click on ``Command Line Options''.
 @end table
 
-@item 3D match
-The aperture (matching volume) can be a sphere, an ellipsoid aligned on the 
three axises or a genenral ellipsoid rotated in any direction.
-To simplifythe usage, the shape can be determined based on the number of 
values given to this option.
 
-@table @asis
-@item 1 number
-For example @option{--aperture=3}.
-The matching volume will be a sphere of the given radius.
-The value is in the same units as the input coordinates.
 
-@item 3 numbers
-For example @option{--aperture=4,5,6e-10}.
-The aperture will be a general ellipsoid with the respective extent along each 
dimension.
-The numbers must be in the same units as each axis.
-This is very similar to the two number case of 2D inputs.
-See there for more.
 
-@item 6 numbers
-For example @option{--aperture=4,0.5,0.6,10,20,30}.
-The numbers represent the full general ellipsoid definition (in any 
orientation).
-For the definition of a general ellipsoid, see @ref{Defining an ellipse and 
ellipsoid}.
-The first number is the semi-major axis.
-The second and third are the two axis ratios.
-The last three are the three Euler angles in units of degrees in the ZXZ order 
as fully described in @ref{Defining an ellipse and ellipsoid}.
-@end table
-@end table
-@end table
+
+
+
 
 
 
@@ -15548,7 +18507,7 @@ After all the transformations are applied, using the 
WCS information in each ind
 @menu
 * Modeling basics::             Astronomical modeling basics.
 * If convolving afterwards::    Considerations for convolving later.
-* Flux Brightness and magnitude::  About these measures of energy.
+* Brightness flux magnitude::   About these measures of energy.
 * Profile magnitude::           Definition of total profile magnitude.
 * Invoking astmkprof::          Inputs and Options for MakeProfiles.
 @end menu
@@ -15641,6 +18600,8 @@ Everything else after that (when the pixel index and 
its radial profile have ent
 
 
 
+
+
 @node PSF, Stars, Defining an ellipse and ellipsoid, Modeling basics
 @subsubsection Point spread function
 
@@ -15663,7 +18624,7 @@ For example, the simple fact that we are sampling in a 
discrete space, namely th
 @cindex Image blurring
 @cindex PSF image size
 Convolution is the mathematical process by which we can apply a `spread' to an 
image, or in other words blur the image, see @ref{Convolution process}.
-The Brightness of an object should remain unchanged after convolution, see 
@ref{Flux Brightness and magnitude}.
+The Brightness of an object should remain unchanged after convolution, see 
@ref{Brightness flux magnitude}.
 Therefore, it is important that the sum of all the pixels of the PSF be unity.
 The PSF image also has to have an odd number of pixels on its sides so one 
pixel can be defined as the center.
 In MakeProfiles, the PSF can be set by the two methods explained below.
@@ -15773,8 +18734,8 @@ Today, most practitioners agree that the flux of 
galaxies can be modeled with on
 @cindex Radius, effective
 @cindex de Vaucouleur profile
 @cindex G@'erard de Vaucouleurs
-G@'erard de Vaucouleurs (1918-1995) was first to show in 1948 that this 
function best fits the galaxy light profiles, with the only difference that he 
held @mymath{n} fixed to a value of 4.
-20 years later in 1968, J. L. S@'ersic showed that @mymath{n} can have a 
variety of values and does not necessarily need to be 4.
+G@'erard de Vaucouleurs (1918-1995) was first to show in 1948 that this 
function resembles the galaxy light profiles, with the only difference that he 
held @mymath{n} fixed to a value of 4.
+Twenty years later in 1968, J. L. S@'ersic showed that @mymath{n} can have a 
variety of values and does not necessarily need to be 4.
 This profile depends on the effective radius (@mymath{r_e}) which is defined 
as the radius which contains half of the profile brightness (see @ref{Profile 
magnitude}).
 @mymath{I_e} is the flux at the effective radius.
 The S@'ersic index @mymath{n} is used to define the concentration of the 
profile within @mymath{r_e} and @mymath{b_n} is a constant dependent on 
@mymath{n}.
@@ -15863,7 +18824,7 @@ This is because unlike the PSF, the noise occurs in 
each output pixel, not on a
 
 
 
-@node If convolving afterwards, Flux Brightness and magnitude, Modeling 
basics, MakeProfiles
+@node If convolving afterwards, Brightness flux magnitude, Modeling basics, 
MakeProfiles
 @subsection If convolving afterwards
 
 In case you want to convolve the image later with a given point spread 
function, make sure to use a larger image size.
@@ -15879,8 +18840,8 @@ To facilitate this shift, MakeProfiles has the options 
@option{--xshift}, @optio
 
 
 
-@node Flux Brightness and magnitude, Profile magnitude, If convolving 
afterwards, MakeProfiles
-@subsection Flux Brightness and magnitude
+@node Brightness flux magnitude, Profile magnitude, If convolving afterwards, 
MakeProfiles
+@subsection Brightness, Flux, Magnitude and Surface brightness
 
 @cindex ADU
 @cindex Gain
@@ -15894,44 +18855,99 @@ We will continue the discussion assuming the pixels 
are in units of energy/time.
 @cindex Luminosity
 @cindex Brightness
 The @emph{brightness} of an object is defined as its total detected energy per 
time.
-This is simply the sum of the pixels that are associated with that detection 
by our detection tool for example @ref{NoiseChisel}@footnote{If further 
processing is done, for example the Kron or Petrosian radii are calculated, 
then the detected area is not sufficient and the total area that was within the 
respective radius must be used.}.
-The @emph{flux} of an object is in units of energy/time/area and for a 
detected object, it is defined as its brightness divided by the area used to 
collect the light from the source or the telescope aperture (for example in 
@mymath{cm^2})@footnote{For a full object that spans over several pixels, the 
telescope area should be used to find the flux.
-However, sometimes, only the brightness per pixel is desired.
-In such cases this book also @emph{loosely} uses the term flux.
-This is only approximately accurate however, since while all the pixels have a 
fixed area, the pixel size can vary with camera on the telescope.}.
-Knowing the flux (@mymath{f}) and distance to the object (@mymath{r}), we can 
calculate its @emph{luminosity}: @mymath{L=4{\pi}r^2f}.
-Therefore, flux and luminosity are intrinsic properties of the object, while 
brightness depends on our detecting tools (hardware and software).
-Here we will not be discussing luminosity, but brightness.
-However, since luminosity is the astrophysically interesting quantity, we also 
defined it here to avoid possible confusion between these two terms because 
they both have the same units.
-
-@cindex Magnitude zero-point
+In the case of an imaged source, this is simply the sum of the pixels that are 
associated with that detection by our detection tool (for example 
@ref{NoiseChisel}@footnote{If further processing is done, for example the Kron 
or Petrosian radii are calculated, then the detected area is not sufficient and 
the total area that was within the respective radius must be used.}).
+The @emph{flux} of an object is defined in units of 
energy/time/collecting-area.
+For an astronomical target, the flux is therefore defined as its brightness 
divided by the area used to collect the light from the source: or the telescope 
aperture (for example in units of @mymath{cm^2}).
+Knowing the flux (@mymath{f}) and distance to the object (@mymath{r}), we can 
define its @emph{luminosity}: @mymath{L=4{\pi}r^2f}.
+
+Therefore, while flux and luminosity are intrinsic properties of the object, 
brightness depends on our detecting tools (hardware and software).
+In low-level observational astronomy data analysis, we are usually more 
concerned with measuring the brightness, because it is the thing we directly 
measure from the image pixels and create in catalogs.
+On the other hand, luminosity is used in higher-level analysis (after image 
contents are measured as catalogs to deduce physical interpretations).
+It is just important avoid possible confusion between luminosity and 
brightness because both have the same units of energy per seconds.
+
 @cindex Magnitudes from flux
 @cindex Flux to magnitude conversion
 @cindex Astronomical Magnitude system
 Images of astronomical objects span over a very large range of brightness.
-With the Sun (as the brightest object) being roughly @mymath{2.5^{60}=10^{24}} 
times brighter than the faintest galaxies we can currently detect.
-Therefore discussing brightness will be very hard, and astronomers have chosen 
to use a logarithmic scale to talk about the brightness of astronomical objects.
-But the logarithm can only be usable with a unit-less and always positive 
value.
-Fortunately brightness is always positive and to remove the units we divide 
the brightness of the object (@mymath{B}) by a reference brightness 
(@mymath{B_r}).
-We then define the resulting logarithmic scale as @mymath{magnitude} through 
the following relation@footnote{The @mymath{-2.5} factor in the definition of 
magnitudes is a legacy of the our ancient colleagues and in particular 
Hipparchus of Nicaea (190-120 BC).}
+With the Sun (as the brightest object) being roughly @mymath{2.5^{60}=10^{24}} 
times brighter than the fainter galaxies we can currently detect in the deepest 
images.
+Therefore discussing brightness directly will involve a large range of values 
which is inconvenient.
+So astronomers have chosen to use a logarithmic scale to talk about the 
brightness of astronomical objects.
+
+@cindex Hipparchus of Nicaea
+But the logarithm can only be usable with a dimensionless value that is always 
positive.
+Fortunately brightness is always positive (at least in theory@footnote{In 
practice, for very faint objects, if the background brightness is 
over-subtracted, we may end up with a negative brightness in a real object.}).
+To remove the dimensions, we divide the brightness of the object (@mymath{B}) 
by a reference brightness (@mymath{B_r}).
+We then define a logarithmic scale as @mymath{magnitude} through the relation 
below.
+The @mymath{-2.5} factor in the definition of magnitudes is a legacy of the 
our ancient colleagues and in particular Hipparchus of Nicaea (190-120 BC).
 
 @dispmath{m-m_r=-2.5\log_{10} \left( B \over B_r \right)}
 
-@cindex Zero-point magnitude
+@cindex Zero point magnitude
+@cindex Magnitude zero point
 @noindent
 @mymath{m} is defined as the magnitude of the object and @mymath{m_r} is the 
pre-defined magnitude of the reference brightness.
-One particularly easy condition is when @mymath{B_r=1}.
-This will allow us to summarize all the hardware specific parameters discussed 
above into one number as the reference magnitude which is commonly known as the 
Zero-point@footnote{When @mymath{B=Br=1}, the right side of the magnitude 
definition will be zero.
-Hence the name, ``zero-point''.} magnitude.
+One particularly easy condition is when the reference brightness is unity 
(@mymath{B_r=1}).
+This brightness will thus summarize all the hardware-specific parameters 
discussed above (like the conversion of pixel values to physical units) into 
one number.
+That reference magnitude which is commonly known as the @emph{Zero point} 
magnitude (because when @mymath{B=Br=1}, the right side of the magnitude 
definition above will be zero).
+Using the zero point magnitude (@mymath{Z}), we can write the magnitude 
relation above in a more simpler format:
+
+@dispmath{m = -2.5\log_{10}(B) + Z}
+
+@cindex Janskys (Jy)
+@cindex AB magnitude
+@cindex Magnitude, AB
+Having the zero point of an image, you can convert its pixel values to 
physical units of microJanskys (or @mymath{\mu{}Jy}) to enable direct 
pixel-based comparisons with images from other instruments (just note that this 
assumes instrument and observation signatures are corrected, things like the 
flat-field or the Sky).
+This conversion can be done with the fact that in the AB magnitude 
standard@footnote{@url{https://en.wikipedia.org/wiki/AB_magnitude}}, 
@mymath{3631Jy} corresponds to the zero-th magnitude, therefore 
@mymath{B\equiv3631\times10^{6}\mu{Jy}} and @mymath{m\equiv0}.
+We can therefore estimate the brightness (@mymath{B_z}, in @mymath{\mu{Jy}}) 
corresponding to the image zero point (@mymath{Z}) using this equation:
+
+@dispmath{m - Z = -2.5\log_{10}(B/B_z)}
+@dispmath{0 - Z = -2.5\log_{10}({3631\times10^{6}\over B_z})}
+@dispmath{B_z = 3631\times10^{\left(6 - {Z \over 2.5} \right)} \mu{Jy}}
+
+@cindex SDSS
+Because the image zero point corresponds to a pixel value of @mymath{1}, the 
@mymath{B_z} value calculated above also corresponds to a pixel value of 
@mymath{1}.
+Therefore you simply have to multiply your image by @mymath{B_z} to convert it 
to @mymath{\mu{Jy}}.
+Don't forget that this only applies when your zero point was also estimated in 
the AB magnitude system.
+On the command-line, you can easily estimate this value for a certain zero 
point with AWK, then multiply it to all the pixels in the image with 
@ref{Arithmetic}.
+For example let's assume you are using an SDSS image with a zero point of 22.5:
+
+@example
+bz=$(echo 22.5 | awk '@{print 3631 * 10^(6-$1/2.5)@}')
+astarithmetic sdss.fits $bz x --output=sdss-in-muJy.fits
+@end example
+
+@cindex Steradian
+@cindex Angular coverage
+@cindex Celestial sphere
+@cindex Surface brightness
+@cindex SI (International System of Units)
+Another important concept is the distribution of an object's brightness over 
its area.
+For this, we define the @emph{surface brightness} to be the magnitude of an 
object's brightness divided by its solid angle over the celestial sphere (or 
coverage in the sky, commonly in units of arcsec@mymath{^2}).
+The solid angle is expressed in units of arcsec@mymath{^2} because 
astronomical targets are usually much smaller than one steradian.
+Recall that the steradian is the dimension-less SI unit of a solid angle and 1 
steradian covers @mymath{1/4\pi} (almost @mymath{8\%}) of the full celestial 
sphere.
+
+Surface brightness is therefore most commonly expressed in units of 
mag/arcsec@mymath{2}.
+For example when the brightness is measured over an area of A 
arcsec@mymath{^2}, then the surface brightness becomes:
+
+@dispmath{S = -2.5\log_{10}(B/A) + Z = -2.5\log_{10}(B) + 2.5\log_{10}(A) + Z}
+
+@noindent
+In other words, the surface brightness (in units of mag/arcsec@mymath{^2}) is 
related to the object's magnitude (@mymath{m}) and area (@mymath{A}, in units 
of arcsec@mymath{^2}) through this equation:
+
+@dispmath{S = m + 2.5\log_{10}(A)}
 
+A common mistake is to follow the mag/arcsec@mymath{^2} unit literally, and 
divide the object's magnitude by its area.
+But this is wrong because magnitude is a logarithmic scale while area is 
linear.
+It is the brightness that should be divided by the solid angle because both 
have linear scales.
+The magnitude of that ratio is then defined to be the surface brightness.
 
-@node Profile magnitude, Invoking astmkprof, Flux Brightness and magnitude, 
MakeProfiles
+@node Profile magnitude, Invoking astmkprof, Brightness flux magnitude, 
MakeProfiles
 @subsection Profile magnitude
 
 @cindex Brightness
 @cindex Truncation radius
 @cindex Sum for total flux
-To find the profile brightness or its magnitude, (see @ref{Flux Brightness and 
magnitude}), it is customary to use the 2D integration of the flux to infinity.
+To find the profile brightness or its magnitude, (see @ref{Brightness flux 
magnitude}), it is customary to use the 2D integration of the flux to infinity.
 However, in MakeProfiles we do not follow this idealistic approach and apply a 
more realistic method to find the total brightness or magnitude: the sum of all 
the pixels belonging to a profile within its predefined truncation radius.
 Note that if the truncation radius is not large enough, this can be 
significantly different from the total integrated light to infinity.
 
@@ -15972,7 +18988,7 @@ $ astmkprof catalog.txt
 $ astmkprof --background=image.fits catalog.txt
 
 ## Make a Moffat PSF with FWHM 3pix, beta=2.8, truncation=5
-$ astmkprof --kernel=moffat,2.8,5 --oversample=1
+$ astmkprof --kernel=moffat,3,2.8,5 --oversample=1
 
 ## Make profiles in catalog, using RA and Dec in the given column:
 $ astmkprof --ccol=RA_CENTER --ccol=DEC_CENTER --mode=wcs catalog.txt
@@ -15991,9 +19007,9 @@ The columns related to each parameter can be determined 
both by number, or by ma
 Without any file given to the @option{--background} option, MakeProfiles will 
make a zero-valued image and build the profiles on that (its size and main WCS 
parameters can also be defined through the options described in 
@ref{MakeProfiles output dataset}).
 Besides the main/merged image containing all the profiles in the catalog, it 
is also possible to build individual images for each profile (only enclosing 
one full profile to its truncation radius) with the @option{--individual} 
option.
 
-If an image is given to the @option{--background} option, the pixels of that 
image are used as the background value for every pixel.
-The flux value of each profile pixel will be added to the pixel in that 
background value.
-In this case, the values to all options relating to the output size and WCS 
will be ignored if specified (for example @option{--oversample}, 
@option{--mergedsize}, and @option{--prepforconv}) on the command-line or in 
the configuration files.
+If an image is given to the @option{--background} option, the pixels of that 
image are used as the background value for every pixel hence flux value of each 
profile pixel will be added to the pixel in that background value.
+You can disable this with the @code{--clearcanvas} option (which will 
initialize the background to zero-valued pixels and build profiles over that).
+With the @option{--background} option, the values to all options relating to 
the ``canvas'' (output size and WCS) will be ignored if specified, for example 
@option{--oversample}, @option{--mergedsize}, and @option{--prepforconv}.
 
 The sections below discuss the options specific to MakeProfiles based on 
context: the input catalog settings which can have many rows for different 
profiles are discussed in @ref{MakeProfiles catalog}, in @ref{MakeProfiles 
profile settings}, we discuss how you can set general profile settings (that 
are the same for all the profiles in the catalog).
 Finally @ref{MakeProfiles output dataset} and @ref{MakeProfiles log file} 
discuss the outputs of MakeProfiles and how you can configure them.
@@ -16060,7 +19076,7 @@ So by default, they will not be built in the output 
image but as separate files.
 The sum of pixels of these separate files will also be set to unity (1) so you 
are ready to convolve, see @ref{Convolution process}.
 As a summary, the position and magnitude of PSF profile will be ignored.
 This behavior can be disabled with the @option{--psfinimg} option.
-If you want to create all the profiles separately (with @option{--individual}) 
and you want the sum of the PSF profile pixels to be unity, you have to set 
their magnitudes in the catalog to the zero-point magnitude and be sure that 
the central positions of the profiles don't have any fractional part (the PSF 
center has to be in the center of the pixel).
+If you want to create all the profiles separately (with @option{--individual}) 
and you want the sum of the PSF profile pixels to be unity, you have to set 
their magnitudes in the catalog to the zero point magnitude and be sure that 
the central positions of the profiles don't have any fractional part (the PSF 
center has to be in the center of the pixel).
 
 The list of options directly related to the input catalog columns is shown 
below.
 
@@ -16109,6 +19125,10 @@ For this profile, the value in the magnitude column 
(@option{--mcol}) will be ig
 
 You can use this for checks or as a first approximation to define your own 
higher-level radial function.
 In the latter case, just note that the central values are going to be 
incorrect (see @ref{Sampling from a function}).
+
+@item
+Custom profile with `@code{custom}' or `@code{8}'.
+The values to use for each radial interval should be in the table given to 
@option{--customtable}. For more, see @ref{MakeProfiles profile settings}.
 @end itemize
 
 @item --rcol=STR/INT
@@ -16212,7 +19232,7 @@ Otherwise all the profiles will be scaled up based on 
the oversampling scale in
 
 @item --mcolisbrightness
 The value given in the ``magnitude column'' (specified by @option{--mcol}, see 
@ref{MakeProfiles catalog}) must be interpreted as brightness, not magnitude.
-The zeropoint magnitude (value to the @option{--zeropoint} option) is ignored 
and the given value must have the same units as the input dataset's pixels.
+The zero point magnitude (value to the @option{--zeropoint} option) is ignored 
and the given value must have the same units as the input dataset's pixels.
 
 Recall that the total profile magnitude or brightness that is specified with 
in the @option{--mcol} column of the input catalog is not an integration to 
infinity, but the actual sum of pixels in the profile (until the desired 
truncation radius).
 See @ref{Profile magnitude} for more on this point.
@@ -16236,6 +19256,55 @@ Particularly in sharper profiles, the flux in the peak 
pixel is strongly decreas
 Also note that in such cases, besides de-convolution, you will have to set 
@option{--oversample=1} otherwise after resampling your profile with Warp (see 
@ref{Warp}), the peak flux will be different.
 @end cartouche
 
+@item --customtable FITS/TXT
+The filename of the table to use in the custom profiles (see description of 
@option{--fcol} in @ref{MakeProfiles catalog}.
+This can be a plain-text table, or FITS table, see @ref{Tables}, if its a FITS 
table, you can use @option{--customtablehdu} to specify which HDU should be 
used (described below).
+
+A custom profile can have any value you want for a given radial profile.
+Each interval is defined by its minimum (inclusive) and maximum (exclusive) 
radius, when a pixel falls within this radius the value specified for that 
interval will be used.
+If a pixel is not in the given intervals, a value of 0 will be used for it.
+
+The table should have 3 columns as shown below.
+If the intervals are contiguous (the maximum value of the previous interval is 
equal to the minimum value of an interval) and the intervals all have the same 
size (difference between minimum and maximum values) the creation of these 
profiles will be fast.
+However, if the intervals are not sorted and contiguous, Makeprofiles will 
parse the intervals from the top of the table and use the first interval that 
contains the pixel center.
+
+@table @asis
+@item Column 1:
+The interval's minimum radius.
+@item Column 2:
+The interval's maximum radius.
+@item Column 3:
+The value to be used for pixels within the given interval.
+@end table
+
+For example let's assume you have the radial profile below in a file called 
@file{radial.txt}.
+The first column is the larger interval radius and the second column is the 
value in that interval:
+
+@example
+1    100
+2    90
+3    50
+4    10
+5    2
+6    0.1
+7    0.05
+@end example
+
+@noindent
+You can construct the table to give to @option{--customtable} with either of 
the commands below: the first one with Gnuastro's @ref{Column arithmetic} which 
can also work on FITS tables, and the second one with an AWK command that only 
works on plain-text tables..
+
+@example
+asttable radial.fits -c'arith $1 1 -' -c1,2 -ocustom.fits
+awk '@{print $1-1, $1, $2@}' radial.txt > custom.txt
+@end example
+
+@noindent
+In case the intervals are different from 1 (for example 0.5), change them 
respectively: for Gnuastro's table change @code{$1 1 -} to @code{$1 0.5 -} and 
for AWK change  @code{$1-1} to @code{$1-0.5}.
+
+
+@item --customtablehdu INT/STR
+The HDU/extension in the FITS file given to @option{--customtable}.
+
 @item -X INT,INT
 @itemx --shift=INT,INT
 Shift all the profiles and enlarge the image along each dimension.
@@ -16252,7 +19321,8 @@ If a background image is specified, any possible value 
to this option is ignored
 
 @item -z FLT
 @itemx --zeropoint=FLT
-The zero-point magnitude of the image.
+The zero point magnitude of the input.
+For more on the zero point magnitude, see @ref{Brightness flux magnitude}.
 
 @item -w FLT
 @itemx --circumwidth=FLT
@@ -16261,20 +19331,14 @@ See the explanations for this type of profile in 
@option{--fcol}.
 
 @item -R
 @itemx --replace
-Do not add the pixels of each profile over the background (possibly crowded by 
other profiles), replace them.
-By default, when two profiles overlap, the final pixel value is the sum of all 
the profiles that overlap on that pixel.
-When this option is given, the pixels are not added but replaced by the newer 
profile's pixel and any value under it is lost.
+Do not add the pixels of each profile over the background, or other profiles.
+But replace the values.
 
-@cindex CPU threads
-@cindex Threads, CPU
-When order matters, make sure to use this function with 
`@option{--numthreads=1}'.
-When multiple threads are used, the separate profiles are built asynchronously 
and not in order.
-Since order does not matter in an addition, this causes no problems by default 
but has to be considered when this option is given.
-Using multiple threads is no problem if the profiles are to be used as a mask 
with a blank or fixed value (see `@option{--mforflatpix}') since all their 
pixel values are the same.
-
-Note that only non-zero pixels are replaced.
-With radial profiles (for example S@'ersic or Moffat) only values above zero 
will be part of the profile.
-However, when using flat profiles with the `@option{--mforflatpix}' option, 
you should be careful not to give a @code{0.0} value as the flat profile's 
pixel value.
+By default, when two profiles overlap, the final pixel value is the sum of all 
the profiles that overlap on that pixel.
+This is the expected situation when dealing with physical object profiles like 
galaxies or stars/PSF.
+However, when MakeProfiles is used to build integer labeled images (for 
example in @ref{Aperture photometry}), this is not the expected situation: the 
sum of two labels will be a new label.
+With this option, the pixels are not added but the largest (maximum) value 
over that pixel is used.
+Because the maximum operator is independent of the order of values, the output 
is also thread-safe.
 
 @end table
 
@@ -16286,8 +19350,8 @@ The options of this section, allow you to configure the 
output dataset (or the c
 
 @table @option
 
-@item -k STR
-@itemx --background=STR
+@item -k FITS
+@itemx --background=FITS
 A background image FITS file to build the profiles on.
 The extension that contains the image should be specified with the 
@option{--backhdu} option, see below.
 When a background image is specified, it will be used to derive all the 
information about the output image.
@@ -16579,7 +19643,7 @@ Then the flux after adding noise is a random value 
taken from a Gaussian distrib
 
 @dispmath{\mu=B+I_{nn}, \quad \sigma=\sqrt{B+I_{nn}}}
 
-Since this type of noise is inherent in the objects we study, it is usually 
measured on the same scale as the astronomical objects, namely the magnitude 
system, see @ref{Flux Brightness and magnitude}.
+Since this type of noise is inherent in the objects we study, it is usually 
measured on the same scale as the astronomical objects, namely the magnitude 
system, see @ref{Brightness flux magnitude}.
 It is then internally converted to the flux scale for further processing.
 
 @node Instrumental noise, Final noised pixel value, Photon counting noise, 
Noise basics
@@ -16604,7 +19668,7 @@ When only this source of noise is present, the noised 
pixel value would be a ran
 This type of noise is independent of the signal in the dataset, it is only 
determined by the instrument.
 So the flux scale (and not magnitude scale) is most commonly used for this 
type of noise.
 In practice, this value is usually reported in analog-to-digital units or 
ADUs, not flux or electron counts.
-The gain value of the device can be used to convert between these two, see 
@ref{Flux Brightness and magnitude}.
+The gain value of the device can be used to convert between these two, see 
@ref{Brightness flux magnitude}.
 
 @node Final noised pixel value, Generating random numbers, Instrumental noise, 
Noise basics
 @subsubsection Final noised pixel value
@@ -16714,11 +19778,12 @@ $ astmknoise [OPTION ...] InputImage.fits
 One line examples:
 
 @example
-## Add noise with a standard deviation of 100 to image:
+## Add noise with a standard deviation of 100 to image.
+## (this is independent of the pixel value: not Poission noise)
 $ astmknoise --sigma=100 image.fits
 
 ## Add noise to input image assuming a background magnitude (with
-## zeropoint magnitude of 0) and a certain instrumental noise:
+## zero point magnitude of 0) and a certain instrumental noise:
 $ astmknoise --background=-10 -z0 --instrumental=20 mockimage.fits
 @end example
 
@@ -16731,25 +19796,40 @@ This is done for future reproducibility.
 
 @table @option
 
-@item -s FLT
-@item --sigma=FLT
-The total noise sigma in the same units as the pixel values.
-With this option, the @option{--background}, @option{--zeropoint} and 
@option{--instrumental} will be ignored.
-With this option, the noise will be independent of the pixel values (which is 
not realistic, see @ref{Photon counting noise}).
-Hence it is only useful if you are working on low surface brightness regions 
where the change in pixel value (and thus real noise) is insignificant.
-
 @item -b FLT
 @itemx --background=FLT
-The background pixel value for the image in units of magnitudes, see 
@ref{Photon counting noise} and @ref{Flux Brightness and magnitude}.
+The background value (per pixel) that will be added to each pixel value 
(internally) to estimate Poisson noise, see @ref{Photon counting noise}.
+By default the units of this value are assumed to be in magnitudes, hence a 
@option{--zeropoint} is also necessary.
+But if the background is in units of brightness, you need add 
@option{--bgisbrightness}, see @ref{Brightness flux magnitude}
+
+Internally, the value given to this option will be converted to brightness 
(@mymath{b}, when @option{--bgisbrightness} is called, the value will be used 
directly).
+Assuming the pixel value is @mymath{p}, the random value for that pixel will 
be taken from a Gaussian distribution with mean of @mymath{p+b} and standard 
deviation of @mymath{\sqrt{p+b}}.
+With this option, the noise will therefore be dependent on the pixel values: 
according to the Poission noise model, as the pixel value becomes larger, its 
noise will also become larger.
+This is thus a realistic way to model noise, see @ref{Photon counting noise}.
+
+@item -B
+@itemx --bgisbrightness
+The value given to @option{--background} should be interpretted as brightness, 
not as a magnitude.
 
 @item -z FLT
 @itemx --zeropoint=FLT
-The zeropoint magnitude used to convert the value of @option{--background} (in 
units of magnitude) to flux, see @ref{Flux Brightness and magnitude}.
+The zero point magnitude used to convert the value of @option{--background} 
(in units of magnitude) to flux, see @ref{Brightness flux magnitude}.
 
 @item -i FLT
 @itemx --instrumental=FLT
 The instrumental noise which is in units of flux, see @ref{Instrumental noise}.
 
+@item -s FLT
+@item --sigma=FLT
+The total noise sigma in the same units as the pixel values.
+With this option, the @option{--background}, @option{--zeropoint} and 
@option{--instrumental} will be ignored.
+With this option, the noise will be independent of the pixel values (which is 
not realistic, see @ref{Photon counting noise}).
+Hence it is only useful if you are working on low surface brightness regions 
where the change in pixel value (and thus real noise) is insignificant.
+
+Generally, @strong{usage of this option is discouraged} unless you understand 
the risks of not simulating real noise.
+This is because with this option, you will not get Poisson noise (the common 
noise model for astronomical imaging), where the noise varies based on pixel 
value.
+Use @option{--background} for adding Poission noise.
+
 @item -e
 @itemx --envseed
 @cindex Seed, Random number generator
@@ -17040,6 +20120,10 @@ $ astcosmiccal -z0.4 -LAg
 ## Assume Lambda and matter density of 0.7 and 0.3 and print
 ## basic cosmological parameters for redshift 2.1:
 $ astcosmiccal -l0.7 -m0.3 -z2.1
+
+## Print wavelength of all pre-defined spectral lines when
+## Lyman-alpha is observed at 4000 Angstroms.
+$ astcosmiccal --obsline=lyalpha,4000 --listlinesatz
 @end example
 
 The input parameters (for example current matter density, etc) can be given as 
command-line options or in the configuration files, see @ref{Configuration 
files}.
@@ -17047,18 +20131,22 @@ For a definition of the different parameters, please 
see the sections prior to t
 If no redshift is given, CosmicCalculator will just print its input parameters 
and abort.
 For a full list of the input options, please see @ref{CosmicCalculator input 
options}.
 
-When only a redshift is given, CosmicCalculator will print all calculations 
(one per line) with some explanations before each.
+Without any particular output requested (and only a given redshift), 
CosmicCalculator will print all basic cosmological calculations (one per line) 
with some explanations before each.
 This can be good when you want a general feeling of the conditions at a 
specific redshift.
-Alternatively, if any specific calculations are requested, only the requested 
values will be calculated and printed with one character space between them.
-In this case, no description will be printed.
-See @ref{CosmicCalculator specific calculations} for the full list of these 
options along with some explanations how when/how they can be useful.
+Alternatively, if any specific calculation(s) are requested (its possible to 
call more than one), only the requested value(s) will be calculated and printed 
with one character space between them.
+In this case, no description or units will be printed.
+See @ref{CosmicCalculator basic cosmology calculations} for the full list of 
these options along with some explanations how when/how they can be useful.
+
+Another common operation in observational cosmology is dealing with spectral 
lines at different redshifts.
+CosmicCalculator also has features to help in such situations, please see 
@ref{CosmicCalculator spectral line calculations}.
 
 @menu
 * CosmicCalculator input options::  Options to specify input conditions.
-* CosmicCalculator specific calculations::  Requesting specific outputs.
+* CosmicCalculator basic cosmology calculations::  Like distance modulus, 
distances and etc.
+* CosmicCalculator spectral line calculations::  How they get affected by 
redshift.
 @end menu
 
-@node CosmicCalculator input options, CosmicCalculator specific calculations, 
Invoking astcosmiccal, Invoking astcosmiccal
+@node CosmicCalculator input options, CosmicCalculator basic cosmology 
calculations, Invoking astcosmiccal, Invoking astcosmiccal
 @subsubsection CosmicCalculator input options
 
 The inputs to CosmicCalculator can be specified with the following options:
@@ -17066,7 +20154,18 @@ The inputs to CosmicCalculator can be specified with 
the following options:
 
 @item -z FLT
 @itemx --redshift=FLT
-The redshift of interest. This cannot be called with @option{--obsline}.
+The redshift of interest.
+There are two other ways that you can specify the target redshift:
+1) Spectral lines and their observed wavelengths, see @option{--obsline}.
+2) Velocity, see @option{--velocity}.
+Hence this option cannot be called with @option{--obsline} or 
@option{--velocity}.
+
+@item -y FLT
+@itemx --velocity=FLT
+Input velocity in km/s.
+The given value will be converted to redshift internally, and used in any 
subsequent calculation.
+This option is thus an alternative to @code{--redshift} or @code{--obsline}, 
it cannot be used with them.
+The conversion will be done with the more general and accurate relativistic 
equation of @mymath{1+z=\sqrt{(c+v)/(c-v)}}, not the simplified 
@mymath{z\approx v/c}.
 
 @item -H FLT
 @itemx --H0=FLT
@@ -17089,13 +20188,15 @@ Radiation density divided by the critical density in 
the current Universe (@myma
 @cindex Rest-frame wavelength
 @cindex Wavelength, rest-frame
 Find the redshift to use in next steps based on the rest-frame and observed 
wavelengths of a line.
+This option is thus an alternative to @code{--redshift} or @code{--velocity}, 
it cannot be used with them.
 Wavelengths are assumed to be in Angstroms.
 The first argument identifies the line.
 It can be one of the standard names below, or any rest-frame wavelength in 
Angstroms.
 The second argument is the observed wavelength of that line.
 For example @option{--obsline=lyalpha,6000} is the same as 
@option{--obsline=1215.64,6000}.
 
-The accepted names are listed below, sorted from red (longer wavelength) to 
blue (shorter wavelength).
+The pre-defined names are listed below, sorted from red (longer wavelength) to 
blue (shorter wavelength).
+You can get this list on the command-line with the @option{--listlines}.
 
 @table @code
 @item siired
@@ -17124,23 +20225,23 @@ The accepted names are listed below, sorted from red 
(longer wavelength) to blue
 @item niiblue
 [6548@AA{}] NII doublet's bluer line.
 
-@item oiiired
-[5007@AA{}] OIII doublet's redder line.
+@item oiiired-vis
+[5007@AA{}] OIII doublet's redder line in the visible.
 
-@item oiii
-@cindex Doublet: OIII
-@cindex OIII doublet
-[4983@AA{}] OIII doublet's mean center.
+@item oiii-vis
+@cindex Doublet: OIII (visible)
+@cindex OIII doublet in visible
+[4983@AA{}] OIII doublet's mean center in the visible.
 
-@item oiiiblue
-[4959@AA{}] OIII doublet's bluer line.
+@item oiiiblue-vis
+[4959@AA{}] OIII doublet's bluer line in the visible.
 
 @item hbeta
 @cindex H-beta
 [4861.36@AA{}] H-@mymath{\beta} line.
 
-@item heiired
-[4686@AA{}] HeII doublet's redder line.
+@item heii-vis
+[4686@AA{}] HeII doublet's redder line in the visible.
 
 @item hgamma
 @cindex H-gamma
@@ -17194,13 +20295,65 @@ The accepted names are listed below, sorted from red 
(longer wavelength) to blue
 @item ciiiblue
 [1907@AA{}] CIII doublet's bluer line.
 
-@item heiiblue
-[1640@AA{}] HeII doublet's bluer line.
+@item si_iiired
+[1892@AA{}] SiIII doublet's redder line.
+
+@item si_iii
+@cindex Doublet: SiIII
+@cindex SiIII doublet
+[1887.5@AA{}] SiIII doublet's mean center.
+
+@item si_iiiblue
+[1883@AA{}] SiIII doublet's bluer line.
+
+@item oiiired-uv
+[1666@AA{}] OIII doublet's redder line in the ultra-violet.
+
+@item oiii-uv
+@cindex Doublet: OIII (in UV)
+@cindex OIII doublet in UV
+[1663.5@AA{}] OIII doublet's mean center in the ultra-violet.
+
+@item oiiiblue-uv
+[1661@AA{}] OIII doublet's bluer line in the ultra-violet.
+
+@item heii-uv
+[1640@AA{}] HeII doublet's bluer line in the ultra-violet.
+
+@item civred
+[1551@AA{}] CIV doublet's redder line.
+
+@item civ
+@cindex Doublet: CIV
+@cindex CIV doublet
+[1549@AA{}] CIV doublet's mean center.
+
+@item civblue
+[1548@AA{}] CIV doublet's bluer line.
+
+@item nv
+[1240@AA{}] NV (four times ionized Sodium).
 
 @item lyalpha
 @cindex Lyman-alpha
 [1215.67@AA{}] Lyman-@mymath{\alpha} line.
 
+@item lybeta
+@cindex Lyman-beta
+[1025.7@AA{}] Lyman-@mymath{\beta} line.
+
+@item lygamma
+@cindex Lyman-gamma
+[972.54@AA{}] Lyman-@mymath{\gamma} line.
+
+@item lydelta
+@cindex Lyman-delta
+[949.74@AA{}] Lyman-@mymath{\delta} line.
+
+@item lyepsilon
+@cindex Lyman-epsilon
+[937.80@AA{}] Lyman-@mymath{\epsilon} line.
+
 @item lylimit
 @cindex Lyman limit
 [912@AA{}] Lyman limit.
@@ -17211,15 +20364,15 @@ The accepted names are listed below, sorted from red 
(longer wavelength) to blue
 
 
 
-@node CosmicCalculator specific calculations,  , CosmicCalculator input 
options, Invoking astcosmiccal
-@subsubsection CosmicCalculator specific calculations
+@node CosmicCalculator basic cosmology calculations, CosmicCalculator spectral 
line calculations, CosmicCalculator input options, Invoking astcosmiccal
+@subsubsection CosmicCalculator basic cosmology calculations
 By default, when no specific calculations are requested, CosmicCalculator will 
print a complete set of all its calculators (one line for each calculation, see 
@ref{Invoking astcosmiccal}).
 The full list of calculations can be useful when you don't want any specific 
value, but just a general view.
 In other contexts (for example in a batch script or during a discussion), you 
know exactly what you want and don't want to be distracted by all the extra 
information.
 
 You can use any number of the options described below in any order.
 When any of these options are requested, CosmicCalculator's output will just 
be a single line with a single space between the (possibly) multiple values.
-In the example below, only the tangential distance along one arcsecond (in 
kpc), absolute magnitude conversion, and age of the universe at redshift 2 are 
printed (recall that you can merge short options together, see @ref{Options}).
+In the example below, only the tangential distance along one arc-second (in 
kpc), absolute magnitude conversion, and age of the universe at redshift 2 are 
printed (recall that you can merge short options together, see @ref{Options}).
 
 @example
 $ astcosmiccal -z2 -sag
@@ -17253,7 +20406,7 @@ The other calculations will also be done, but they are 
so fast that you will not
 $ astcosmiccal --redshift=0.832 | grep volume
 @end example
 
-The full list of CosmicCalculator's specific calculations is present below.
+The full list of CosmicCalculator's specific calculations is present below in 
two groups: basic cosmology calculations and those related to spectral lines.
 In case you have forgot the units, you can use the @option{--help} option 
which has the units along with a short description.
 
 @table @option
@@ -17262,7 +20415,13 @@ In case you have forgot the units, you can use the 
@option{--help} option which
 @itemx --usedredshift
 The redshift that was used in this run.
 In many cases this is the main input parameter to CosmicCalculator, but it is 
useful in others.
-For example in combination with @option{--obsline} (where you give an observed 
and rest-frame wavelength and would like to know the redshift), or if you want 
to run CosmicCalculator in a loop while changing the redshift and you want to 
keep the redshift value.
+For example in combination with @option{--obsline} (where you give an observed 
and rest-frame wavelength and would like to know the redshift) or with 
@option{--velocity} (where you specify the velocity instead of redshift).
+Another example is when you run CosmicCalculator in a loop, while changing the 
redshift and you want to keep the redshift value with the resulting calculation.
+
+@item -Y
+@itemx --usedvelocity
+The velocity (in km/s) that was used in this run.
+The conversion from redshift will be done with the more general and accurate 
relativistic equation of @mymath{1+z=\sqrt{(c+v)/(c-v)}}, not the simplified 
@mymath{z\approx v/c}.
 
 @item -G
 @itemx --agenow
@@ -17283,8 +20442,8 @@ The angular diameter distance to object at given 
redshift in Megaparsecs (Mpc).
 
 @item -s
 @itemx --arcsectandist
-The tangential distance covered by 1 arcseconds at the given redshift in 
kiloparsecs (Kpc).
-This can be useful when trying to estimate the resolution or pixel scale of an 
instrument (usually in units of arcseconds) at a given redshift.
+The tangential distance covered by 1 arc-seconds at the given redshift in 
kiloparsecs (Kpc).
+This can be useful when trying to estimate the resolution or pixel scale of an 
instrument (usually in units of arc-seconds) at a given redshift.
 
 @item -L
 @itemx --luminositydist
@@ -17317,12 +20476,62 @@ The critical density at given redshift in grams per 
centimeter-cube (@mymath{g/c
 @itemx --onlyvolume
 The comoving volume in Megaparsecs cube (Mpc@mymath{^3}) until the desired 
redshift based on the input parameters.
 
+@end table
+
+
+
+
+@node CosmicCalculator spectral line calculations,  , CosmicCalculator basic 
cosmology calculations, Invoking astcosmiccal
+@subsubsection CosmicCalculator spectral line calculations
+
+@cindex Rest frame wavelength
+At different redshifts, observed spectral lines are shifted compared to their 
rest frame wavelengths with this simple relation: 
@mymath{\lambda_{obs}=\lambda_{rest}(1+z)}.
+Although this relation is very simple and can be done for one line in the head 
(or a simple calculator!), it slowly becomes tiring when dealing with a lot of 
lines or redshifts, or some precision is necessary.
+The options in this section are thus provided to greatly simplify usage of 
this simple equation, and also helping by storing a list of pre-defined 
spectral line wavelengths.
+
+For example if you want to know the wavelength of the @mymath{H\alpha} line 
(at 6562.8 Angstroms in rest frame), when @mymath{Ly\alpha} is at 8000 
Angstroms, you can call CosmicCalculator like the first example below.
+And if you want the wavelength of all pre-defined spectral lines at this 
redshift, you can use the second command.
+
+@example
+$ astcosmiccal --obsline=lyalpha,8000 --lineatz=halpha
+$ astcosmiccal --obsline=lyalpha,8000 --listlinesatz
+@end example
+
+Bellow you can see the printed/output calculations of CosmicCalculator that 
are related to spectral lines.
+Note that @option{--obsline} is an input parameter, so its discussed (with the 
full list of known lines) in @ref{CosmicCalculator input options}.
+
+@table @option
+
+@item --listlines
+List the pre-defined rest frame spectral line wavelengths and their names on 
standard output, then abort CosmicCalculator.
+When this option is given, other operations on the command-line will be 
ignored.
+This is convenient when you forget the specific name of the spectral line used 
within Gnuastro, or when you forget the exact wavelength of a certain line.
+
+These names can be used with the options that deal with spectral lines, for 
example @option{--obsline} and @option{--lineatz} (@ref{CosmicCalculator basic 
cosmology calculations}).
+
+The format of the output list is a two-column table, with Gnuastro's text 
table format (see @ref{Gnuastro text table format}).
+Therefore, if you are only looking for lines in a specific range, you can pipe 
the output into Gnuastro's table program and use its @option{--range} option on 
the @code{wavelength} (first) column.
+For example, if you only want to see the lines between 4000 and 6000 
Angstroms, you can run this command:
+
+@example
+$ astcosmiccal --listlines \
+               | asttable --range=wavelength,4000,6000
+@end example
+
+@noindent
+And if you want to use the list later and have it as a table in a file, you 
can easily add the @option{--output} (or @option{-o}) option to the 
@command{asttable} command, and specify the filename, for example 
@option{--output=lines.fits} or @option{--output=lines.txt}.
+
+@item --listlinesatz
+Similar to @option{--listlines} (above), but the printed wavelength is not in 
the rest frame, but redshifted to the given redshift.
+Recall that the redshift can be specified by @option{--redshift} directly or 
by @option{--obsline}, see @ref{CosmicCalculator input options}.
+
 @item -i STR/FLT
 @itemx --lineatz=STR/FLT
 The wavelength of the specified line at the redshift given to CosmicCalculator.
-The line can be specified either by its name (see description of 
@option{--obsline} in @ref{CosmicCalculator input options}), or directly as a 
number.
+The line can be specified either by its name or directly as a number (its 
wavelength).
+To get the list of pre-defined names for the lines and their wavelength, you 
can use the @option{--listlines} option, see @ref{CosmicCalculator input 
options}.
 In the former case (when a name is given), the returned number is in units of 
Angstroms.
-In the latter (when a number is given), the returned value is the same units 
of the input number.
+In the latter (when a number is given), the returned value is the same units 
of the input number (assuming its a wavelength).
 
 @end table
 
@@ -17345,8 +20554,6 @@ In the latter (when a number is given), the returned 
value is the same units of
 
 
 
-
-
 @node Library, Developing, High-level calculations, Top
 @chapter Library
 
@@ -17826,8 +21033,11 @@ You may also use Gnuastro's @ref{Configuration files} 
to specify other libraries
 
 The C compiler can be chosen with the @option{--cc} option, or environment 
variables, please see the description of @option{--cc} for more.
 The two common @code{LDFLAGS} and @code{CPPFLAGS} environment variables are 
also checked and used in the build by default.
+Note that they are placed after the values to the corresponding options 
@option{--includedir} and @option{--linkdir}.
+Therefore BuildProgram's own options take precedence.
 Using environment variables can be disabled with the @option{--noenv} option.
-Just note that BuildProgram also keeps the important flags in these 
environment variables in its configuration file, so in many cases, when you 
needed them to build Gnuastro, you won't need them in BuildProgram.
+Just note that BuildProgram also keeps the important flags in these 
environment variables in its configuration file.
+Therefore, in many cases, even though you may needed them to build Gnuastro, 
you won't need them in BuildProgram.
 
 The first argument is considered to be the C source file that must be compiled 
and linked.
 Any other arguments (non-option tokens on the command-line) will be passed 
onto the program when BuildProgram wants to run it.
@@ -18009,6 +21219,7 @@ If you use the Info version of this manual (see 
@ref{Info}), you don't have to w
 * Bounding box::                Finding the bounding box.
 * Polygons::                    Working with the vertices of a polygon.
 * Qsort functions::             Helper functions for Qsort.
+* K-d tree::                    Space partitioning in K dimensions.
 * Permutations::                Re-order (or permute) the values in a dataset.
 * Matching::                    Matching catalogs based on position.
 * Statistical operations::      Functions for basic statistics.
@@ -18017,6 +21228,7 @@ If you use the Info version of this manual (see 
@ref{Info}), you don't have to w
 * Convolution functions::       Library functions to do convolution.
 * Interpolation::               Interpolate (over blank values possibly).
 * Git wrappers::                Wrappers for functions in libgit2.
+* Unit conversion library (@file{units.h})::  Convert between units.
 * Spectral lines library::      Functions for operating on Spectral lines.
 * Cosmology library::           Cosmological calculations.
 @end menu
@@ -18065,6 +21277,18 @@ However, only more recent versions of WCSLIB also 
provide its version number.
 If the WCSLIB that is installed on the system provides its version (through 
the possibly existing @code{wcslib_version} function), this macro will have a 
value of one, otherwise it will have a value of zero.
 @end deffn
 
+@deffn Macro GAL_CONFIG_HAVE_WCSLIB_DIS_H
+This macro has a value of 1 if the host's WCSLIB has the @file{wcslib/dis.h} 
header for distortion-related operations.
+@end deffn
+
+@deffn Macro GAL_CONFIG_HAVE_WCSLIB_MJDREF
+This macro has a value of 1 if the host's WCSLIB reads and stores the 
@file{MJDREF} FITS header keyword as part of its core @code{wcsprm} structure.
+@end deffn
+
+@deffn Macro GAL_CONFIG_HAVE_WCSLIB_OBSFIX
+This macro has a value of 1 if the host's WCSLIB supports the @code{OBSFIX} 
feature (used by @code{wcsfix} function to parse the input WCS for known 
errors).
+@end deffn
+
 @deffn Macro GAL_CONFIG_HAVE_PTHREAD_BARRIER
 The POSIX threads standard define barriers as an optional requirement.
 Therefore, some operating systems choose to not include it.
@@ -18204,31 +21428,20 @@ strange and apparently random errors that are 
extremely hard to debug.
 @subsubsection Gnuastro's thread related functions
 
 @cindex POSIX Threads
-The POSIX Threads functions offered in the C library are very low-level and
-offer a great range of control over the properties of the threads. So if
-you are interested in customizing your tools for complicated thread
-applications, it is strongly encouraged to get a nice familiarity with
-them. Some resources were introduced in @ref{Multithreaded
-programming}.
+The POSIX Threads functions offered in the C library are very low-level and 
offer a great range of control over the properties of the threads.
+So if you are interested in customizing your tools for complicated thread 
applications, it is strongly encouraged to get a nice familiarity with them.
+Some resources were introduced in @ref{Multithreaded programming}.
 
-However, in many cases used in astronomical data analysis, you don't need
-communication between threads and each target operation can be done
-independently. Since such operations are very common, Gnuastro provides the
-tools below to facilitate the creation and management of jobs without any
-particular knowledge of POSIX Threads for such operations. The most
-interesting high-level functions of this section are the
-@code{gal_threads_number} and @code{gal_threads_spin_off} that identify the
-number of threads on the system and spin-off threads. You can see a
-demonstration of using these functions in @ref{Library demo -
-multi-threaded operation}.
+However, in many cases used in astronomical data analysis, you don't need 
communication between threads and each target operation can be done 
independently.
+Since such operations are very common, Gnuastro provides the tools below to 
facilitate the creation and management of jobs without any particular knowledge 
of POSIX Threads for such operations.
+The most interesting high-level functions of this section are the 
@code{gal_threads_number} and @code{gal_threads_spin_off} that identify the 
number of threads on the system and spin-off threads.
+You can see a demonstration of using these functions in @ref{Library demo - 
multi-threaded operation}.
 
 @deftp {C @code{struct}} gal_threads_params
-Structure keeping the parameters of each thread. When each thread is
-created, a pointer to this structure is passed to it. The @code{params}
-element can be the pointer to a structure defined by the user which
-contains all the necessary parameters to pass onto the worker function. The
-rest of the elements within this structure are set internally by
-@code{gal_threads_spin_off} and are relevant to the worker function.
+Structure keeping the parameters of each thread.
+When each thread is created, a pointer to this structure is passed to it.
+The @code{params} element can be the pointer to a structure defined by the 
user which contains all the necessary parameters to pass onto the worker 
function.
+The rest of the elements within this structure are set internally by 
@code{gal_threads_spin_off} and are relevant to the worker function.
 @example
 struct gal_threads_params
 @{
@@ -18241,60 +21454,82 @@ struct gal_threads_params
 @end deftp
 
 @deftypefun size_t gal_threads_number ()
-Return the number of threads that the operating system has available for
-your program. This number is usually fixed for a single machine and doesn't
-change. So this function is useful when you want to run your program on
-different machines (with different CPUs).
+Return the number of threads that the operating system has available for your 
program.
+This number is usually fixed for a single machine and doesn't change.
+So this function is useful when you want to run your program on different 
machines (with different CPUs).
 @end deftypefun
 
-@deftypefun void gal_threads_spin_off (void @code{*(*worker)(void *)}, void 
@code{*caller_params}, size_t @code{numactions}, size_t @code{numthreads})
-Distribute @code{numactions} jobs between @code{numthreads} threads and
-spin-off each thread by calling the @code{worker} function. The
-@code{caller_params} pointer will also be passed to @code{worker} as part
-of the @code{gal_threads_params} structure. For a fully working example of
-this function, please see @ref{Library demo - multi-threaded operation}.
+@deftypefun void gal_threads_spin_off (void @code{*(*worker)(void *)}, void 
@code{*caller_params}, size_t @code{numactions}, size_t @code{numthreads}, 
size_t @code{minmapsize}, int @code{quietmmap})
+Distribute @code{numactions} jobs between @code{numthreads} threads and 
spin-off each thread by calling the @code{worker} function.
+The @code{caller_params} pointer will also be passed to @code{worker} as part 
of the @code{gal_threads_params} structure.
+For a fully working example of this function, please see @ref{Library demo - 
multi-threaded operation}.
+
+If there are many jobs (millions or billions) to organize, memory issues may 
become important.
+With @code{minmapsize} you can specify the minimum byte-size to allocate the 
necessary space in a memory-mapped file or alternatively in RAM.
+If @code{quietmmap} is non-zero, then a warning will be printed upon creating 
a memory-mapped file.
+For more on Gnuastro's memory management, see @ref{Memory management}.
 @end deftypefun
 
 @deftypefun void gal_threads_attr_barrier_init (pthread_attr_t @code{*attr}, 
pthread_barrier_t @code{*b}, size_t @code{limit})
 @cindex Detached threads
-This is a low-level function in case you don't want to use
-@code{gal_threads_spin_off}. It will initialize the general thread
-attribute @code{attr} and the barrier @code{b} with @code{limit} threads to
-wait behind the barrier. For maximum efficiency, the threads initialized
-with this function will be detached.  Therefore no communication is
-possible between these threads and in particular @code{pthread_join} won't
-work on these threads. You have to use the barrier constructs to wait for
-all threads to finish.
-@end deftypefun
-
-@deftypefun void gal_threads_dist_in_threads (size_t @code{numactions}, size_t 
@code{numthreads}, size_t @code{**outthrds}, size_t @code{*outthrdcols})
-This is a low-level function in case you don't want to use
-@code{gal_threads_spin_off}. Identify the ``index''es (starting from 0) of
-the actions to be done on each thread in the @code{outthrds}
-array. @code{outthrds} is treated as a 2D array with @code{numthreads} rows
-and @code{outthrdcols} columns. The indexs in each row, identify the
-actions that should be done by one thread. Please see the explanation below
-to understand the purpose of this operation.
-
-Let's assume you have @mymath{A} actions (where there is only one function
-and the input values differ for each action) and @mymath{T} threads
-available to the system with @mymath{A>T} (common values for these two
-would be @mymath{A>1000} and @mymath{T<10}). Spinning off a thread is not a
-cheap job and requires a significant number of CPU cycles. Therefore,
-creating @mymath{A} threads is not the best way to address such a
-problem. The most efficient way to manage the actions is such that only
-@mymath{T} threads are created, and each thread works on a list of actions
-identified for it in series (one after the other). This way your CPU will
-get all the actions done with minimal overhead.
-
-The purpose of this function is to do what we explained above: each row in
-the @code{outthrds} array contains the indexs of actions which must be done
-by one thread. @code{outthrds} contains @code{outthrdcols} columns. In
-using @code{outthrds}, you don't have to know the number of columns. The
-@code{GAL_BLANK_SIZE_T} macro has a role very similar to a
-string's @code{\0}: every row finishes with this macro, so can easily stop
-parsing the indexes in the row when you confront it. Please see the example
-program in @file{tests/lib/multithread.c} for a demonstration.
+This is a low-level function in case you don't want to use 
@code{gal_threads_spin_off}.
+It will initialize the general thread attribute @code{attr} and the barrier 
@code{b} with @code{limit} threads to wait behind the barrier.
+For maximum efficiency, the threads initialized with this function will be 
detached.
+Therefore no communication is possible between these threads and in particular 
@code{pthread_join} won't work on these threads.
+You have to use the barrier constructs to wait for all threads to finish.
+@end deftypefun
+
+@deftypefun {char *} gal_threads_dist_in_threads (size_t @code{numactions}, 
size_t @code{numthreads}, size_t @code{minmapsize}, int @code{quietmmap}, 
size_t @code{**indexs}, size_t @code{*icols})
+This is a low-level function in case you don't want to use 
@code{gal_threads_spin_off}.
+The job of this function is to distribute @code{numactions} jobs/actions in 
@code{numthreads} threads.
+To do this, it will assign each job an ID, ranging from 0 to 
@code{numactions}-1.
+The output is the allocated @code{*indexs} array and the @code{*icols} number.
+In memory, its just a simple 1D array that has @code{numthreads} 
@mymath{\times} @code{*icols} elements.
+But you can visualize it as a 2D array with @code{numthreads} rows and 
@code{*icols} columns.
+For more on the logic of the distribution, see below.
+
+@cindex RAM
+@cindex Memory management
+When you have millions/billions of jobs to distribute, @code{indexs} will 
become very large.
+For memory management (when to use a memory-mapped file, and when to use RAM), 
you need to specify the @code{minmapsize} and @code{quietmmap} arguments.
+For more on memory management, see @ref{Memory management}.
+In general, if your distributed jobs will not be on the scale of billions (and 
you want everything to always be written in RAM), just set @code{minmapsize=-1} 
and @code{quietmmap=1}.
+
+When @code{indexs} is actually in a memory-mapped file, this function will 
return a string containing the name of the file (that you can later give to 
@code{gal_pointer_mmap_free} to free/delete).
+When @code{indexs} is in RAM, this function will return a @code{NULL} pointer.
+So after you are finished with @code{indexs}, you can free it like this:
+
+@example
+char *mmapname;
+int quietmmap=1;
+size_t *indexs, thrdcols;
+size_t numactions=5000, minmapsize=-1;
+size_t numthreads=gal_threads_number();
+
+/* Distribute the jobs. */
+mmapname=gal_threads_dist_in_threads(numactions, numthreads,
+                                     minmapsize, quietmmap,
+                                     &indexs, &thrdcols);
+
+/* Do any processing you want... */
+
+/* Free the 'indexs' array. */
+if(mmapname) gal_pointer_mmap_free(&mmapname, quietmmap);
+else         free(indexs);
+@end example
+
+Here is a brief description of the reasoning behind the @code{indexs} array 
and how the jobs are distributed.
+Let's assume you have @mymath{A} actions (where there is only one function and 
the input values differ for each action) and @mymath{T} threads available to 
the system with @mymath{A>T} (common values for these two would be 
@mymath{A>1000} and @mymath{T<10}).
+Spinning off a thread is not a cheap job and requires a significant number of 
CPU cycles.
+Therefore, creating @mymath{A} threads is not the best way to address such a 
problem.
+The most efficient way to manage the actions is such that only @mymath{T} 
threads are created, and each thread works on a list of actions identified for 
it in series (one after the other).
+This way your CPU will get all the actions done with minimal overhead.
+
+The purpose of this function is to do what we explained above: each row in the 
@code{indexs} array contains the indexs of actions which must be done by one 
thread (so it has @code{numthreads} rows with @code{*icols} columns).
+However, when using @code{indexs}, you don't have to know the number of 
columns.
+It is guaranteed that all the rows finish with @code{GAL_BLANK_SIZE_T} (see 
@ref{Library blank values}).
+The @code{GAL_BLANK_SIZE_T} macro plays a role very similar to a string's 
@code{\0}: every row finishes with this macro, so can easily stop parsing the 
indexes in the row as soon as you confront @code{GAL_BLANK_SIZE_T}.
+For some real examples, please see the example program in 
@file{tests/lib/multithread.c} for a demonstration.
 
 @end deftypefun
 
@@ -18590,15 +21825,16 @@ if( gal_type_from_string(&out, string, 
GAL_TYPE_FLOAT32) )
 @end deftypefun
 
 @deftypefun {void *} gal_type_string_to_number (char @code{*string}, uint8_t 
@code{*type})
-Read @code{string} into smallest type that can host the number, the
-allocated space for the number will be returned and the type of the number
-will be put into the memory that @code{type} points to. If @code{string}
-couldn't be read as a number, this function will return @code{NULL}.
+Read @code{string} into smallest type that can host the number, the allocated 
space for the number will be returned and the type of the number will be put 
into the memory that @code{type} points to.
+If @code{string} couldn't be read as a number, this function will return 
@code{NULL}.
+
+This function calls the C library's @code{strtod} function to read 
@code{string} as a double-precision floating point number.
+When successful, it will check the value to put it in the smallest numerical 
data type that can handle it.
+However, if @code{string} is successfully parsed as a number @emph{and} there 
is @code{.} in @code{string}, it will force the number into floating point 
types.
+For example @code{"5"} is read as an integer, while @code{"5."} or 
@code{"5.0"}, or @code{"5.00"} will be read as a floating point 
(single-precision).
 
-For the ranges acceptable by each type see @ref{Numeric data types}. For
-integers it is clear, for floating point types, this function will count
-the number of significant digits and determine if the given string is
-single or double precision as described in that section.
+For the ranges acceptable by each type see @ref{Numeric data types}.
+For integers, the range is clear, but for floating point types, this function 
will count the number of significant digits and determine if the given string 
is single or double precision as described in that section.
 @end deftypefun
 
 @node Pointers, Library blank values, Library data types, Gnuastro library
@@ -18635,42 +21871,38 @@ types}).
 @end deftypefun
 
 @deftypefun {void *} gal_pointer_allocate (uint8_t @code{type}, size_t 
@code{size}, int @code{clear}, const char @code{*funcname}, const char 
@code{*varname})
-Allocate an array of type @code{type} with @code{size} elements in RAM (for
-the type codes, see @ref{Library data types}). If @code{clear!=0}, then the
-allocated space is set to zero (cleared). This is effectively just a
-wrapper around C's @code{malloc} or @code{calloc} functions but takes
-Gnuastro's integer type codes and will also abort with a clear error if
-there the allocation was not successful.
+Allocate an array of type @code{type} with @code{size} elements in RAM (for 
the type codes, see @ref{Library data types}).
+If @code{clear!=0}, then the allocated space is set to zero (cleared).
+This is effectively just a wrapper around C's @code{malloc} or @code{calloc} 
functions but takes Gnuastro's integer type codes and will also abort with a 
clear error if there the allocation was not successful.
 
 @cindex C99
-When space cannot be allocated, this function will abort the program with a
-message containing the reason for the failure. @code{funcname} (name of the
-function calling this function) and @code{varname} (name of variable that
-needs this space) will be used in this error message if they are not
-@code{NULL}. In most modern compilers, you can use the generic
-@code{__func__} variable for @code{funcname}. In this way, you don't have
-to manually copy and paste the function name or worry about it changing
-later (@code{__func__} was standardized in C99).
-@end deftypefun
-
-@deftypefun {void *} gal_pointer_allocate_mmap (size_t @code{size}, uint8_t 
@code{type}, int @code{clear}, char @code{**mmapname})
-Allocate the necessary space to keep @code{size} elements of type
-@code{type} in HDD/SSD (a file, not in RAM). for the type codes, see
-@ref{Library data types}. If @code{clear!=0}, then the allocated space will
-also be cleared. The allocation is done using C's @code{mmap} function. The
-name of the file containing the allocated space is an allocated string that
-will be put in @code{*mmapname}.
-
-Note that the kernel doesn't allow an infinite number of memory mappings to
-files. So it is not recommended to use this function with every
-allocation. The best case scenario to use this function is for large arrays
-that are very large and can fill up the RAM. Keep the smaller arrays in
-RAM, which is faster and can have a (theoretically) unlimited number of
-allocations.
-
-When you are done with the dataset and don't need it anymore, don't use
-@code{free} (the dataset isn't in RAM). Just delete the file (and the
-allocated space for the filename) with the commands below:
+When space cannot be allocated, this function will abort the program with a 
message containing the reason for the failure.
+@code{funcname} (name of the function calling this function) and 
@code{varname} (name of variable that needs this space) will be used in this 
error message if they are not @code{NULL}.
+In most modern compilers, you can use the generic @code{__func__} variable for 
@code{funcname}.
+In this way, you don't have to manually copy and paste the function name or 
worry about it changing later (@code{__func__} was standardized in C99).
+@end deftypefun
+
+@deftypefun {void *} gal_pointer_allocate_ram_or_mmap (uint8_t @code{type}, 
size_t @code{size}, int @code{clear}, size_t @code{minmapsize}, char 
@code{**mmapname}, int @code{quietmmap}, const char @code{*funcname}, const 
char @code{*varname})
+Allocate the given space either in RAM or in a memory-mapped file.
+This function is just a high-level wrapper to @code{gal_pointer_allocate} (to 
allocate in RAM) or @code{gal_pointer_mmap_allocate} (to use a memory-mapped 
file).
+For more on memory management in Gnuastro, please see @ref{Memory management}.
+The various arguments are more fully explained in the two functions above.
+@end deftypefun
+
+@deftypefun {void *} gal_pointer_mmap_allocate (size_t @code{size}, uint8_t 
@code{type}, int @code{clear}, char @code{**mmapname})
+Allocate the necessary space to keep @code{size} elements of type @code{type} 
in HDD/SSD (a file, not in RAM).
+For the type codes, see @ref{Library data types}.
+If @code{clear!=0}, then the allocated space will also be cleared.
+The allocation is done using C's @code{mmap} function.
+The name of the file containing the allocated space is an allocated string 
that will be put in @code{*mmapname}.
+
+Note that the kernel doesn't allow an infinite number of memory mappings to 
files.
+So it is not recommended to use this function with every allocation.
+The best case scenario to use this function is for large arrays that are very 
large and can fill up the RAM.
+Keep the smaller arrays in RAM, which is faster and can have a (theoretically) 
unlimited number of allocations.
+
+When you are done with the dataset and don't need it anymore, don't use 
@code{free} (the dataset isn't in RAM).
+Just delete the file (and the allocated space for the filename) with the 
commands below, or simply use @code{gal_pointer_mmap_free}.
 
 @example
 remove(mmapname);
@@ -18678,6 +21910,12 @@ free(mmapname);
 @end example
 @end deftypefun
 
+@deftypefun void gal_pointer_mmap_free (char @code{**mmapname}, int 
@code{quietmmap})
+``Free'' (actually delete) the memory-mapped file that is named 
@code{*mmapname}, then free the string.
+If @code{quietmmap} is non-zero, then a warning will be printed for the user 
to know that the given file has been deleted.
+@end deftypefun
+
+
 @node Library blank values, Library data container, Pointers, Gnuastro library
 @subsection Library blank values (@file{blank.h})
 When the position of an element in a dataset is important (for example a
@@ -18850,10 +22088,10 @@ If you want to re-check a dataset with the 
blank-value-check flag already
 set (for example if you have made changes to it), then explicitly set the
 @code{GAL_DATA_FLAG_BLANK_CH} bit to zero before calling this
 function. When there are no other flags, you can just set the flags to zero
-(@code{input->flags=0}), otherwise you can use this expression:
+(@code{input->flag=0}), otherwise you can use this expression:
 
 @example
-input->flags &= ~GAL_DATA_FLAG_BLANK_CH;
+input->flag &= ~GAL_DATA_FLAG_BLANK_CH;
 @end example
 @end deftypefun
 
@@ -18873,29 +22111,44 @@ those that aren't.
 @end deftypefun
 
 @deftypefun void gal_blank_flag_apply (gal_data_t @code{*input}, gal_data_t 
@code{*flag})
-Set all non-zero and non-blank elements of @code{flag} to blank in
-@code{input}. @code{flag} has to have an unsigned 8-bit type and be the
-same size as @code{input}.
+Set all non-zero and non-blank elements of @code{flag} to blank in 
@code{input}.
+@code{flag} has to have an unsigned 8-bit type and be the same size as 
@code{input}.
 @end deftypefun
 
+@deftypefun void gal_blank_flag_remove (gal_data_t @code{*input}, gal_data_t 
@code{*flag})
+Remove all elements within @code{input} that are flagged, convert it to a 1D 
dataset and adjust the size properly (the number of non-flagged elements).
+In practice this function doesn't@code{realloc} the input array (see 
@code{gal_blank_remove_realloc} for shrinking/re-allocating also), it just 
shifts the blank elements to the end and adjusts the size elements of the 
@code{gal_data_t}, see @ref{Generic data container}.
+
+Note that elements that are blank, but not flagged will not be removed.
+This function will only remove flagged elements.
+
+If all the elements were flagged, then @code{input->size} will be zero.
+This is thus a good parameter to check after calling this function to see if 
there actually were any non-flagged elements in the input or not and take the 
appropriate measure.
+This check is highly recommended because it will avoid strange bugs in later 
steps.
+@end deftypefun
 
 @deftypefun void gal_blank_remove (gal_data_t @code{*input})
-Remove blank elements from a dataset, convert it to a 1D dataset, adjust
-the size properly (the number of non-blank elements), and toggle the
-blank-value-related bit-flags. In practice this function doesn't
-@code{realloc} the input array, it just shifts the blank elements to the
-end and adjusts the size elements of the @code{gal_data_t}, see
-@ref{Generic data container}.
+Remove blank elements from a dataset, convert it to a 1D dataset, adjust the 
size properly (the number of non-blank elements), and toggle the 
blank-value-related bit-flags.
+In practice this function doesn't@code{realloc} the input array (see 
@code{gal_blank_remove_realloc} for shrinking/re-allocating also), it just 
shifts the blank elements to the end and adjusts the size elements of the 
@code{gal_data_t}, see @ref{Generic data container}.
 
-If all the elements were blank, then @code{input->size} will be zero. This
-is thus a good parameter to check after calling this function to see if
-there actually were any non-blank elements in the input or not and take the
-appropriate measure. This check is highly recommended because it will avoid
-strange bugs in later steps.
+If all the elements were blank, then @code{input->size} will be zero.
+This is thus a good parameter to check after calling this function to see if 
there actually were any non-blank elements in the input or not and take the 
appropriate measure.
+This check is highly recommended because it will avoid strange bugs in later 
steps.
 @end deftypefun
 
+@deftypefun void gal_blank_remove_realloc (gal_data_t @code{*input})
+Similar to @code{gal_blank_remove}, but also shrinks/re-allocates the 
dataset's allocated memory.
+@end deftypefun
 
+@deftypefun {gal_data_t *} gal_blank_remove_rows (gal_data_t @code{*columns}, 
gal_list_sizet_t @code{*column_indexs})
+Remove any row that has at least one blank value in any of the input columns.
+The input @code{columns} is a list of @code{gal_data_t}s (see @ref{List of 
gal_data_t}).
+After this function, all the elements in @code{columns} will still have the 
same size as each other, but if any of the searched columns has blank elements, 
all their sizes will decrease together.
 
+If @code{column_indexs==NULL}, then all the columns (nodes in the list) will 
be checked for blank elements, and any row that has at least one blank element 
will be removed.
+When @code{column_indexs!=NULL}, only the columns whose index (counting from 
zero) is in @code{column_indexs} will be used to check for blank values (see 
@ref{List of size_t}.
+In any case (no matter which columns are checked for blanks), the selected 
rows from all columns will be removed.
+@end deftypefun
 
 
 
@@ -19039,63 +22292,47 @@ independent parameter. However, low-level operations 
with the dataset
 is designed to avoid calculating it every time.
 
 @item int quietmmap
-When this value is zero, and the dataset must not be allocated in RAM (see
-@code{mmapname} and @code{minmapsize}), a warning will be printed to inform
-the user when the file is created and when it is deleted. The warning
-includes the filename, the size in bytes, and the fact that they can toggle
-this behavior through @code{--minmapsize} option in Gnuastro's programs.
+When this value is zero, and the dataset must not be allocated in RAM (see 
@code{mmapname} and @code{minmapsize}), a warning will be printed to inform the 
user when the file is created and when it is deleted.
+The warning includes the filename, the size in bytes, and the fact that they 
can toggle this behavior through @code{--minmapsize} option in Gnuastro's 
programs.
 
 @item char *mmapname
-Name of file hosting the @code{mmap}'d contents of @code{array}. If the
-value of this variable is @code{NULL}, then the contents of @code{array}
-are actually stored in RAM, not in a file on the HDD/SSD. See the
-description of @code{minmapsize} below for more.
+Name of file hosting the @code{mmap}'d contents of @code{array}.
+If the value of this variable is @code{NULL}, then the contents of 
@code{array} are actually stored in RAM, not in a file on the HDD/SSD.
+See the description of @code{minmapsize} below for more.
 
-If a file is used, it will be kept in the hidden @file{.gnuastro}, or
-@file{.gnuastro_mmap} directories with a randomly selected name to allow
-multiple arrays to be kept there at the same time, see description of
-@option{--minmapsize} in @ref{Processing options}. When
-@code{gal_data_free} is called the randomly named file will be deleted.
+If a file is used, it will be kept in the @file{gnuastro_mmap} directory of 
the running directory.
+Its name is randomly selected to allow multiple arrays at the same time, see 
description of @option{--minmapsize} in @ref{Processing options}.
+When @code{gal_data_free} is called the randomly named file will be deleted.
 
 @item size_t minmapsize
-The minimum size of an array (in bytes) to store the contents of
-@code{array} as a file (on the non-volatile HDD/SSD), not in RAM. This can
-be very useful for large datasets which can be very memory intensive and
-the user's RAM might not be sufficient to keep/process it. A random
-filename is assigned to the array which is available in the @code{mmapname}
-element of @code{gal_data_t} (above), see there for more. @code{minmapsize}
-is stored in each @code{gal_data_t}, so it can be passed on to
-subsequent/derived datasets.
-
-See the description of the @option{--minmapsize} option in @ref{Processing
-options} for more on using this value.
+The minimum size of an array (in bytes) to store the contents of @code{array} 
as a file (on the non-volatile HDD/SSD), not in RAM.
+This can be very useful for large datasets which can be very memory intensive 
and the user's RAM might not be sufficient to keep/process it.
+A random filename is assigned to the array which is available in the 
@code{mmapname} element of @code{gal_data_t} (above), see there for more.
+@code{minmapsize} is stored in each @code{gal_data_t}, so it can be passed on 
to subsequent/derived datasets.
+
+See the description of the @option{--minmapsize} option in @ref{Processing 
options} for more on using this value.
 
 @item nwcs
 The number of WCS coordinate representations (for WCSLIB).
 
 @item struct wcsprm *wcs
-The main WCSLIB structure keeping all the relevant information necessary
-for WCSLIB to do its processing and convert data-set positions into
-real-world positions. When it is given a @code{NULL} value, all possible
-WCS calculations/measurements will be ignored.
+The main WCSLIB structure keeping all the relevant information necessary for 
WCSLIB to do its processing and convert data-set positions into real-world 
positions.
+When it is given a @code{NULL} value, all possible WCS 
calculations/measurements will be ignored.
 
 @item uint8_t flag
-Bit-wise flags to describe general properties of the dataset. The number of
-bytes available in this flag is stored in the @code{GAL_DATA_FLAG_SIZE}
-macro. Note that you should use bit-wise operators@footnote{See
-@url{https://en.wikipedia.org/wiki/Bitwise_operations_in_C}.} to check
-these flags. The currently recognized bits are stored in these macros:
+Bit-wise flags to describe general properties of the dataset.
+The number of bytes available in this flag is stored in the 
@code{GAL_DATA_FLAG_SIZE} macro.
+Note that you should use bit-wise operators@footnote{See 
@url{https://en.wikipedia.org/wiki/Bitwise_operations_in_C}.} to check these 
flags.
+The currently recognized bits are stored in these macros:
 
 @table @code
 
 @cindex Blank data
 @item GAL_DATA_FLAG_BLANK_CH
-Marking that the dataset has been checked for blank values or not. When a
-dataset doesn't have any blank values, the @code{GAL_DATA_FLAG_HASBLANK}
-bit will be zero. But upon initialization, all bits also get a value of
-zero. Therefore, a checker needs this flag to see if the value in
-@code{GAL_DATA_FLAG_HASBLANK} is reliable (dataset has actually been parsed
-for a blank value) or not.
+Marking that the dataset has been checked for blank values or not.
+When a dataset doesn't have any blank values, the 
@code{GAL_DATA_FLAG_HASBLANK} bit will be zero.
+But upon initialization, all bits also get a value of zero.
+Therefore, a checker needs this flag to see if the value in 
@code{GAL_DATA_FLAG_HASBLANK} is reliable (dataset has actually been parsed for 
a blank value) or not.
 
 Also, if it is necessary to re-check the presence of flags, you just have
 to set this flag to zero and call @code{gal_blank_present} for example to
@@ -19244,11 +22481,10 @@ not have any zero values (a dimension of length zero 
is not defined).
 @end deftypefun
 
 @deftypefun void gal_data_free_contents (gal_data_t @code{*data})
-Free all the non-@code{NULL} pointers in @code{gal_data_t} except for
-@code{next} and @code{block}. If @code{data} is actually a tile
-(@code{data->block!=NULL}, see @ref{Tessellation library}), then
-@code{data->array} is not freed. For a complete description of
-@code{gal_data_t} and its contents, see @ref{Generic data container}.
+Free all the non-@code{NULL} pointers in @code{gal_data_t} except for 
@code{next} and @code{block}.
+All freed arrays are set to @code{NULL}.
+If @code{data} is actually a tile (@code{data->block!=NULL}, see 
@ref{Tessellation library}), then @code{data->array} is not freed.
+For a complete description of @code{gal_data_t} and its contents, see 
@ref{Generic data container}.
 @end deftypefun
 
 @deftypefun void gal_data_free (gal_data_t @code{*data})
@@ -19298,6 +22534,15 @@ element of all the datasets will also be freed, see 
@ref{Generic data
 container}.
 @end deftypefun
 
+@deftypefun {gal_data_t **} gal_data_array_ptr_calloc (size_t @code{size})
+Allocate an array of pointers to Gnuastro's generic data structure and 
initialize all pointers to @code{NULL}.
+This is useful when you want to allocate individual datasets later (for 
example with @code{gal_data_alloc}).
+@end deftypefun
+
+@deftypefun void gal_data_array_ptr_free (gal_data_t @code{**dataptr}, size_t 
@code{size}, int @code{free_array});
+Free all the individual datasets within the elements of @code{dataptr}, then 
free @code{dataptr} itself (the array of pointers that was probably allocated 
with @code{gal_data_array_ptr_calloc}.
+@end deftypefun
+
 @node Copying datasets,  , Arrays of datasets, Library data container
 @subsubsection Copying datasets
 
@@ -19442,6 +22687,8 @@ already be allocated before calling this function.
 @end deftypefun
 
 @deftypefun size_t gal_dimension_dist_manhattan (size_t @code{*a}, size_t 
@code{*b}, size_t @code{ndim})
+@cindex Manhattan distance
+@cindex Distance, Manhattan
 Return the manhattan distance (see
 @url{https://en.wikipedia.org/wiki/Taxicab_geometry, Wikipedia}) between
 the two coordinates @code{a} and @code{b} (each an array of @code{ndim}
@@ -19453,6 +22700,22 @@ Return the radial distance between the two coordinates 
@code{a} and
 @code{b} (each an array of @code{ndim} elements).
 @end deftypefun
 
+@deftypefun float gal_dimension_dist_elliptical (double @code{*center}, double 
@code{*pa_deg}, double @code{*q}, size_t @code{ndim}, double @code{*point})
+@cindex Ellipse
+@cindex Ellipsoid
+@cindex Axis ratio
+@cindex Position angle
+@cindex Elliptical distance
+@cindex Ellipsoidal distance
+@cindex Distance, elliptical/ellipsoidal
+Return the elliptical/ellipsoidal distance of the single point @code{point} 
(containing @code{ndim} values: coordinates of the point in each dimension) 
from an ellipse that is defined by @code{center}, @code{pa_deg} and @code{q}.
+@code{center} is the coordinates of the ellipse center (also with @code{ndim} 
elements). @code{pa} is the position-angle in degrees (the angle of the 
semi-major axis from the first dimension in a 2D ellipse) and @code{q} is the 
axis ratio.
+
+In a 2D ellipse, @code{pa} and @code{q} are a single-element array.
+However, in a 3D ellipsoid, @code{pa} must have three elements, and @code{q} 
must have 2 elements.
+For more see @ref{Defining an ellipse and ellipsoid}.
+@end deftypefun
+
 @deftypefun {gal_data_t *} gal_dimension_collapse_sum (gal_data_t @code{*in}, 
size_t @code{c_dim}, gal_data_t @code{*weight})
 Collapse the input dataset (@code{in}) along the given dimension
 (@code{c_dim}, in C definition: starting from zero, from the slowest
@@ -20648,7 +23911,8 @@ this line is ignored.
 @end itemize
 @end deftypefun
 
-@deftypefun void gal_table_write (gal_data_t @code{*cols}, gal_list_str_t 
@code{*comments}, int @code{tableformat}, char @code{*filename}, char 
@code{*extname}, uint8_t @code{colinfoinstdout})
+@deftypefun void gal_table_write (gal_data_t @code{*cols}, struct 
gal_fits_list_key_t @code{**keywords}, gal_list_str_t @code{*comments}, int 
@code{tableformat}, char @code{*filename}, char @code{*extname}, uint8_t 
@code{colinfoinstdout})
+
 Write @code{cols} (a list of datasets, see @ref{List of gal_data_t}) into a
 table stored in @code{filename}. The format of the table can be determined
 with @code{tableformat} that accepts the macros defined above. When
@@ -20838,11 +24102,29 @@ extension will be returned.
 Return the number of HDUs/extensions in @file{filename}.
 @end deftypefun
 
+@deftypefun {unsigned long} gal_fits_hdu_datasum (char @code{*filename}, char 
@code{*hdu})
+@cindex @code{DATASUM}: FITS keyword
+Return the @code{DATASUM} of the given HDU in the given FITS file.
+For more on @code{DATASUM} in the FITS standard, see @ref{Keyword inspection 
and manipulation} (under the @code{checksum} component of @option{--write}).
+@end deftypefun
+
+@deftypefun {unsigned long} gal_fits_hdu_datasum_ptr (fitsfile @code{*fptr})
+@cindex @code{DATASUM}: FITS keyword
+Return the @code{DATASUM} of the already opened HDU in @code{fptr}.
+For more on @code{DATASUM} in the FITS standard, see @ref{Keyword inspection 
and manipulation} (under the @code{checksum} component of @option{--write}).
+@end deftypefun
+
 @deftypefun int gal_fits_hdu_format (char @code{*filename}, char @code{*hdu})
 Return the format of the HDU as one of CFITSIO's recognized macros:
 @code{IMAGE_HDU}, @code{ASCII_TBL}, or @code{BINARY_TBL}.
 @end deftypefun
 
+@deftypefun int gal_fits_hdu_is_healpix (fitsfile @code{*fptr})
+@cindex HEALPix
+Return @code{1} if the dataset may be a HEALpix grid and @code{0} otherwise.
+Technically, it is considered to be a HEALPix if the HDU isn't an ASCII table, 
and has the @code{NSIDE}, @code{FIRSTPIX} and @code{LASTPIX}.
+@end deftypefun
+
 @deftypefun {fitsfile *} gal_fits_hdu_open (char @code{*filename}, char 
@code{*hdu}, int @code{iomode})
 Open the HDU/extension @code{hdu} from @file{filename} and return a pointer
 to CFITSIO's @code{fitsfile}. @code{iomode} determines how the FITS file
@@ -20903,15 +24185,18 @@ for adding elements to the list.
 @example
 typedef struct gal_fits_list_key_t
 @{
-  int                        kfree;   /* ==1, free name.      */
-  int                        vfree;   /* ==1, free value.     */
-  int                        cfree;   /* ==1, free comment.   */
-  uint8_t                     type;   /* Keyword value type.  */
-  char                    *keyname;   /* Keyword Name.        */
-  void                      *value;   /* Keyword value.       */
-  char                    *comment;   /* Keyword comment.     */
-  char                       *unit;   /* Keyword unit.        */
-  struct gal_fits_list_key_t *next;   /* Next keyword.        */
+  int                        tfree;   /* ==1, free title string.   */
+  int                        kfree;   /* ==1, free keyword name.   */
+  int                        vfree;   /* ==1, free keyword value.  */
+  int                        cfree;   /* ==1, free comment.        */
+  int                        ufree;   /* ==1, free unit.           */
+  uint8_t                     type;   /* Keyword value type.       */
+  char                      *title;   /* !=NULL, only print title. */
+  char                    *keyname;   /* Keyword Name.             */
+  void                      *value;   /* Keyword value.            */
+  char                    *comment;   /* Keyword comment.          */
+  char                       *unit;   /* Keyword unit.             */
+  struct gal_fits_list_key_t *next;   /* Pointer next keyword.     */
 @} gal_fits_list_key_t;
 @end example
 @end deftp
@@ -20955,61 +24240,45 @@ string.
 
 @deftypefun {char *} gal_fits_key_date_to_struct_tm (char @code{*fitsdate}, 
struct tm @code{*tp})
 @cindex Date: FITS format
-Parse @code{fitsdate} as a FITS date format string (most generally:
-@code{YYYY-MM-DDThh:mm:ss.ddd...}) into the C library's broken-down time
-structure, or @code{struct tm} (declared in @file{time.h}) and return a
-pointer to the remainder of the string (containing the optional sub-second
-portion of @code{fitsdate}, or the @code{.ddd...} of the format).
-
-The returned @code{char *} points to part of the @code{fitsdate} string, so
-it must not be freed. For example, if @code{fitsdate} contains no
-sub-second portion, then the returned @code{char *} will point to the
-NULL-character of @code{fitsdate}.
-
-Note that the FITS date format mentioned above is the most complete
-representation. The following two formats are also acceptable:
-@code{YYYY-MM-DDThh:mm:ss} and @code{YYYY-MM-DD}. This option can also
-interpret the older FITS date format where only two characters are given to
-the year and the date format is reversed
-(@code{DD/MM/YYThh:mm:ss.ddd...}). In this case (following the GNU C
-Library), this option will make the following assumption: values 68 to 99
-correspond to the years 1969 to 1999, and values 0 to 68 as the years 2000
-to 2068.
+Parse @code{fitsdate} as a FITS date format string (most generally: 
@code{YYYY-MM-DDThh:mm:ss.ddd...}) into the C library's broken-down time 
structure, or @code{struct tm} (declared in @file{time.h}) and return a pointer 
to a newly allocated array for the sub-second part of the format 
(@code{.ddd...}).
+Therefore it needs to be freed afterwards (if it isn't @code{NULL})
+When there is no sub-second portion, this pointer will be @code{NULL}.
+
+This is a relatively low-level function, an easier function to use is 
@code{gal_fits_key_date_to_seconds} which will return the sub-seconds as double 
precision floating point.
+
+Note that the FITS date format mentioned above is the most complete 
representation.
+The following two formats are also acceptable: @code{YYYY-MM-DDThh:mm:ss} and 
@code{YYYY-MM-DD}.
+This option can also interpret the older FITS date format where only two 
characters are given to the year and the date format is reversed 
(@code{DD/MM/YYThh:mm:ss.ddd...}).
+In this case (following the GNU C Library), this option will make the 
following assumption: values 68 to 99 correspond to the years 1969 to 1999, and 
values 0 to 68 as the years 2000 to 2068.
 @end deftypefun
 
 @deftypefun size_t gal_fits_key_date_to_seconds (char @code{*fitsdate}, char 
@code{**subsecstr}, double @code{*subsec})
 @cindex Unix epoch time
 @cindex Epoch time, Unix
-Return the Unix epoch time (number of seconds that have passed since
-00:00:00 Thursday, January 1st, 1970) corresponding to the FITS date format
-string @code{fitsdate} (see description of
-@code{gal_fits_key_date_to_struct_tm} above).
+Return the Unix epoch time (number of seconds that have passed since 00:00:00 
Thursday, January 1st, 1970) corresponding to the FITS date format string 
@code{fitsdate} (see description of @code{gal_fits_key_date_to_struct_tm} 
above).
+This function will return @code{GAL_BLANK_SIZE_T} if the broken-down time 
couldn't be converted to seconds.
 
-The Unix epoch time is in units of seconds, but the FITS date format allows
-sub-second accuracy. The last two arguments are for the (optional)
-sub-second portion. If @code{fitsdate} contains sub-second accuracy, then
-the starting of the sub-second part is stored in the @code{char *} pointer
-that @code{subsecstr} points to, and @code{subsec} will the corresponding
-numerical value (between 0 and 1, in double precision floating point).
+The Unix epoch time is in units of seconds, but the FITS date format allows 
sub-second accuracy.
+The last two arguments are for the optional sub-second portion.
+If you don't want sub-second information, just set the second argument to 
@code{NULL}.
 
-This is a very useful function for operations on the FITS date values, for
-example sorting FITS files by their dates, or finding the time difference
-between two FITS files. The advantage of working with the Unix epoch time
-is that you don't have to worry about calendar details (for example the
-number of days in different months, or leap years, etc).
+If @code{fitsdate} contains sub-second accuracy and @code{subsecstr!=NULL}, 
then the starting of the sub-second part's string is stored in @code{subsecstr} 
(malloc'ed), and @code{subsec} will be the corresponding numerical value 
(between 0 and 1, in double precision floating point).
+So to avoid leaking memory, if a sub-second string is requested, it must be 
freed after calling this function.
+When a sub-second string doesn't exist (and it is requested), then a value of 
@code{NULL} and NaN will be written in @code{*subsecstr} and @code{*subsec} 
respectively.
+
+This is a very useful function for operations on the FITS date values, for 
example sorting FITS files by their dates, or finding the time difference 
between two FITS files.
+The advantage of working with the Unix epoch time is that you don't have to 
worry about calendar details (for example the number of days in different 
months, or leap years, etc).
 @end deftypefun
 
 @deftypefun void gal_fits_key_read_from_ptr (fitsfile @code{*fptr}, gal_data_t 
@code{*keysll}, int @code{readcomment}, int @code{readunit})
 
-Read the list of keyword values from a FITS pointer. The input should be a
-linked list of Gnuastro's generic data container
-(@code{gal_data_t}). Before calling this function, you just have to set the
-@code{name} and desired @code{type} values of each node in the list to the
-keyword you want it to keep the value of. The given @code{name} value will
-be directly passed to CFITSIO to read the desired keyword name. This
-function will allocate space to keep the value. If @code{readcomment} and
-@code{readunit} are non-zero, this function will also try to read the
-possible comments and units of the keyword.
+Read the list of keyword values from a FITS pointer.
+The input should be a linked list of Gnuastro's generic data container 
(@code{gal_data_t}).
+Before calling this function, you just have to set the @code{name}, and 
optinally, the desired @code{type} of the value of each keyword.
+The given @code{name} value will be directly passed to CFITSIO to read the 
desired keyword name.
+This function will allocate space to keep the value.
+If no pre-defined type is requested for a certain keyword's value, the 
smallest possible type to host the value will be found and used.
+If @code{readcomment} and @code{readunit} are non-zero, this function will 
also try to read the possible comments and units of the keyword.
 
 Here is one example of using this function:
 
@@ -21018,7 +24287,7 @@ Here is one example of using this function:
 gal_data_t *keysll=gal_data_array_calloc(N);
 
 /* Make the array usable as a list too (by setting `next'). */
-for(i=0;i<N-1;++i) keysll[i].next=keysll[i+1];
+for(i=0;i<N-1;++i) keysll[i].next=&keysll[i+1];
 
 /* Fill the datasets with a `name' and a `type'. */
 keysll[0].name="NAME1";     keysll[0].type=GAL_TYPE_INT32;
@@ -21038,23 +24307,15 @@ for(i=0;i<N;++i) keysll[i].name=NULL;
 gal_data_array_free(keysll, N, 1);
 @end example
 
-If the @code{array} pointer of each keyword's dataset is not @code{NULL},
-then it is assumed that the space to keep the value has already been
-allocated. If it is @code{NULL}, space will be allocated for the value by
-this function.
+If the @code{array} pointer of each keyword's dataset is not @code{NULL}, then 
it is assumed that the space to keep the value has already been allocated.
+If it is @code{NULL}, space will be allocated for the value by this function.
 
-Strings need special consideration: the reason is that generally,
-@code{gal_data_t} needs to also allow for array of strings (as it supports
-arrays of integers for example). Hence when reading a string value, two
-allocations may be done by this function (one if
-@code{array!=NULL}).
+Strings need special consideration: the reason is that generally, 
@code{gal_data_t} needs to also allow for array of strings (as it supports 
arrays of integers for example).
+Hence when reading a string value, two allocations may be done by this 
function (one if @code{array!=NULL}).
 
-Therefore, when using the values of strings after this function,
-@code{keysll[i].array} must be interpreted as @code{char **}: one
-allocation for the pointer, one for the actual characters. If you use
-something like the example, above you don't have to worry about the
-freeing, @code{gal_data_array_free} will free both allocations. So to read
-a string, one easy way would be the following:
+Therefore, when using the values of strings after this function, 
@code{keysll[i].array} must be interpreted as @code{char **}: one allocation 
for the pointer, one for the actual characters.
+If you use something like the example, above you don't have to worry about the 
freeing, @code{gal_data_array_free} will free both allocations.
+So to read a string, one easy way would be the following:
 
 @example
 char *str, **strarray;
@@ -21062,18 +24323,14 @@ strarr = keysll[i].array;
 str    = strarray[0];
 @end example
 
-If CFITSIO is unable to read a keyword for any reason the @code{status}
-element of the respective @code{gal_data_t} will be non-zero. If it is
-zero, then the keyword was found and successfully read. Otherwise, it is a
-CFITSIO status value. You can use CFITSIO's error reporting tools or
-@code{gal_fits_io_error} (see @ref{FITS macros errors filenames}) for
-reporting the reason of the failure. A tip: when the keyword doesn't exist,
-CFITSIO's status value will be @code{KEY_NO_EXIST}.
+If CFITSIO is unable to read a keyword for any reason the @code{status} 
element of the respective @code{gal_data_t} will be non-zero.
+If it is zero, then the keyword was found and successfully read.
+Otherwise, it is a CFITSIO status value.
+You can use CFITSIO's error reporting tools or @code{gal_fits_io_error} (see 
@ref{FITS macros errors filenames}) for reporting the reason of the failure.
+A tip: when the keyword doesn't exist, CFITSIO's status value will be 
@code{KEY_NO_EXIST}.
 
-CFITSIO will start searching for the keywords from the last place in the
-header that it searched for a keyword. So it is much more efficient if the
-order that you ask for keywords is based on the order they are stored in
-the header.
+CFITSIO will start searching for the keywords from the last place in the 
header that it searched for a keyword.
+So it is much more efficient if the order that you ask for keywords is based 
on the order they are stored in the header.
 
 @end deftypefun
 
@@ -21085,22 +24342,42 @@ filename and HDU as input instead of an already 
opened CFITSIO
 @end deftypefun
 
 
-@deftypefun void gal_fits_key_list_add (gal_fits_list_key_t @code{**list}, 
uint8_t @code{type}, char @code{*keyname}, int @code{kfree}, void 
@code{*value}, int @code{vfree}, char @code{*comment}, int @code{cfree}, char 
@code{*unit})
-Add a keyword to the top of list of header keywords that need to be
-written into a FITS file. In the end, the keywords will have to be freed,
-so it is important to know before hand if they were allocated or not (hence
-the presence of the arguments ending in @code{free}). If the space for the
-respective element is not allocated, set these arguments to @code{0}
-(zero).
+@deftypefun void gal_fits_key_list_add (gal_fits_list_key_t @code{**list}, 
uint8_t @code{type}, char @code{*keyname}, int @code{kfree}, void 
@code{*value}, int @code{vfree}, char @code{*comment}, int @code{cfree}, char 
@code{*unit}, int @code{ufree})
+Add a keyword to the top of list of header keywords that need to be written 
into a FITS file.
+In the end, the keywords will have to be freed, so it is important to know 
before hand if they were allocated or not (hence the presence of the arguments 
ending in @code{free}).
+If the space for the respective element is not allocated, set these arguments 
to @code{0} (zero).
+
+You can call this function multiple times on a single list add several keys 
that will be written in one call to @code{gal_fits_key_write} or 
@code{gal_fits_key_write_in_ptr}.
+However, the resulting list will be a last-in-first-out list (for more on 
lists, see @ref{Linked lists}).
+Hence, the written keys will have the inverse order of your calls to this 
function.
+To avoid this problem, you can either use @code{gal_fits_key_list_add_end} 
instead (which will add each key to the end of the list, not to the top like 
this function).
+Alternatively, you can use @code{gal_fits_key_list_reverse} after adding all 
the keys with this function.
+
+@strong{Important note for strings}: the value should be the pointer to the 
string its-self (@code{char *}), not a pointer to a pointer (@code{char **}).
+@end deftypefun
+
+@deftypefun void gal_fits_key_list_add_end (gal_fits_list_key_t @code{**list}, 
uint8_t @code{type}, char @code{*keyname}, int @code{kfree}, void 
@code{*value}, int @code{vfree}, char @code{*comment}, int @code{cfree}, char 
@code{*unit}, int @code{ufree})
+Similar to @code{gal_fits_key_list_add}, but add the given keyword to the end 
of the list, see the description of @code{gal_fits_key_list_add} for more.
+Use this function if you want the keywords to be written in the same order 
that you add nodes to the list of keywords.
+@end deftypefun
+
+@deftypefun void gal_fits_key_list_title_add (gal_fits_list_key_t 
@code{**list}, char @code{*title}, int @code{tfree})
+Add a special ``title'' keyword (with the @code{title} string) to the top of 
the keywords list.
+If @code{cfree} is non-zero, the space allocated for @code{comment} will be 
freed immediately after writing the keyword (in another function).
+@end deftypefun
+
+@deftypefun void gal_fits_key_list_title_add_end (gal_fits_list_key_t 
@code{**list}, char @code{*title}, int @code{tfree})
+Similar to @code{gal_fits_key_list_title_add}, but put the comments at the end 
of the list.
+@end deftypefun
 
-@strong{Important note for strings}: the value should be the pointer to the
-string its-self (@code{char *}), not a pointer to a pointer (@code{char **}).
+@deftypefun void gal_fits_key_list_comment_add (gal_fits_list_key_t 
@code{**list}, char @code{*comment}, int @code{fcfree})
+Add a @code{COMMENT} keyword to the top of the keywords list.
+If the comment is longer than 70 characters, CFITSIO will automatically break 
it into multiple @code{COMMENT} keywords.
+If @code{fcfree} is non-zero, the space allocated for @code{comment} will be 
freed immediately after writing the keyword (in another function).
 @end deftypefun
 
-@deftypefun void gal_fits_key_list_add_end (gal_fits_list_key_t @code{**list}, 
uint8_t @code{type}, char @code{*keyname}, int @code{kfree}, void 
@code{*value}, int @code{vfree}, char @code{*comment}, int @code{cfree}, char 
@code{*unit})
-Similar to @code{gal_fits_key_list_add} (see above) but add the keyword to
-the end of the list. Use this function if you want the keywords to be
-written in the same order that you add nodes to the list of keywords.
+@deftypefun void gal_fits_key_list_comment_add_end (gal_fits_list_key_t 
@code{**list}, char @code{*comment}, int @code{fcfree})
+Similar to @code{gal_fits_key_list_comment_add}, but put the comments at the 
end of the list.
 @end deftypefun
 
 @deftypefun void gal_fits_key_list_reverse (gal_fits_list_key_t @code{**list})
@@ -21108,12 +24385,10 @@ Reverse the input list of keywords.
 @end deftypefun
 
 @deftypefun void gal_fits_key_write_title_in_ptr (char @code{*title}, fitsfile 
@code{*fptr})
-Add two lines of ``title'' keywords to the given CFITSIO @code{fptr}
-pointer. The first line will be blank and the second will have the string
-in @code{title} roughly in the middle of the line (a fixed distance from
-the start of the keyword line). A title in the list of keywords helps in
-classifying the keywords into groups and inspecting them by eye. If
-@code{title==NULL}, this function won't do anything.
+Add two lines of ``title'' keywords to the given CFITSIO @code{fptr} pointer.
+The first line will be blank and the second will have the string in 
@code{title} roughly in the middle of the line (a fixed distance from the start 
of the keyword line).
+A title in the list of keywords helps in classifying the keywords into groups 
and inspecting them by eye.
+If @code{title==NULL}, this function won't do anything.
 @end deftypefun
 
 @deftypefun void gal_fits_key_write_filename (char @code{*keynamebase}, char 
@code{*filename}, gal_fits_list_key_t @code{**list}, int @code{top1end0})
@@ -21131,22 +24406,45 @@ break it into several keywords (breaking up the 
string on directory
 separators).
 @end deftypefun
 
-@deftypefun void gal_fits_key_write_wcsstr (fitsfile @code{*fptr}, char 
@code{*wcsstr}, int @code{nkeyrec})
-Write the WCS header string (produced with WCSLIB's @code{wcshdo} function)
-into the CFITSIO @code{fitsfile} pointer. @code{nkeyrec} is the number of
-FITS header keywords in @code{wcsstr}. This function will put a few blank
-keyword lines along with a comment @code{WCS information} before writing
-each keyword record.
+@deftypefun void gal_fits_key_write_wcsstr (fitsfile @code{*fptr}, struct 
wcsprm @code{wcs}, char @code{*wcsstr}, int @code{nkeyrec})
+
+Write the WCS header string (produced with WCSLIB's @code{wcshdo} function) 
into the CFITSIO @code{fitsfile} pointer.
+@code{nkeyrec} is the number of FITS header keywords in @code{wcsstr}.
+This function will put a few blank keyword lines along with a comment 
@code{WCS information} before writing each keyword record.
 @end deftypefun
 
 @deftypefun void gal_fits_key_write (gal_fits_list_key_t @code{**keylist}, 
char @code{*title}, char @code{*filename}, char @code{*hdu})
-Write the list of keywords in @code{keylist} into the @code{hdu} extension
-of the file called @code{filename} (it must already exist).
+Write the list of keywords in @code{keylist} into the @code{hdu} extension of 
the file called @code{filename} (the file must already exist) and free the list.
+
+The list nodes are meant to be dynamically allocated (because they will be 
freed after being written).
+We thus recommend using the @code{gal_fits_key_list_add} or 
@code{gal_fits_key_list_add_end} to create and fill the list.
+Below is one fully working example of using this function to write a keyword 
into an existing FITS file.
+
+@example
+#include <stdio.h>
+#include <stdlib.h>
+#include <gnuastro/fits.h>
+
+int main()
+@{
+  char *filename="test.fits";
+  gal_fits_list_key_t *keylist=NULL;
+
+  char *unit="unit";
+  float value=123.456;
+  char *keyname="MYKEY";
+  char *comment="A good description of the key";
+  gal_fits_key_list_add_end(&keylist, GAL_TYPE_FLOAT32, keyname, 0,
+                            &value, 0, comment, 0, unit, 0);
+  gal_fits_key_write(&keylist, "Matching metadata", filename, "1");
+  return EXIT_SUCCESS;
+@}
+@end example
 @end deftypefun
 
 @deftypefun void gal_fits_key_write_in_ptr (gal_fits_list_key_t 
@code{**keylist}, fitsfile @code{*fptr})
-Write the list of keywords in @code{keylist} into the given CFITSIO
-@code{fitsfile} pointer.
+Write the list of keywords in @code{keylist} into the given CFITSIO 
@code{fitsfile} pointer and free keylist.
+For more on the input @code{keylist}, see the description and example for 
@code{gal_fits_key_write}, above.
 @end deftypefun
 
 @deftypefun void gal_fits_key_write_version (gal_fits_list_key_t 
@code{**keylist}, char @code{*title}, char @code{*filename}, char @code{*hdu})
@@ -21354,7 +24652,7 @@ under the same name in @ref{Generic data container}.
 Each column will have @code{numrows} rows and @code{colinfo} contains any
 further information about the columns (returned by
 @code{gal_fits_tab_info}, described above). Note that this is a low-level
-function, so the output data linked list is the inverse of the input indexs
+function, so the output data linked list is the inverse of the input indexes
 linked list. It is recommended to use @code{gal_table_read} for generic
 reading of tables, see @ref{Table input output}.
 @end deftypefun
@@ -21400,167 +24698,130 @@ different file formats that Gnuastro's library 
recognizes.
 @node Text files, TIFF files, File input output, File input output
 @subsubsection Text files (@file{txt.h})
 
-The most universal and portable format for data storage are plain text
-files. They can be viewed and edited on any text editor or even on the
-command-line. This section are describes some functions that help in
-reading from and writing to plain text files.
+The most universal and portable format for data storage are plain text files.
+They can be viewed and edited on any text editor or even on the command-line.
+This section are describes some functions that help in reading from and 
writing to plain text files.
 
 @cindex CRLF line terminator
 @cindex Line terminator, CRLF
-Lines are one of the most basic building blocks (delimiters) of a text
-file. Some operating systems like Microsoft Windows, terminate their ASCII
-text lines with a carriage return character and a new-line character (two
-characters, also known as CRLF line terminators). While Unix-like operating
-systems just use a single new-line character. The functions below that read
-an ASCII text file are able to identify lines with both kinds of line
-terminators.
+Lines are one of the most basic building blocks (delimiters) of a text file.
+Some operating systems like Microsoft Windows, terminate their ASCII text 
lines with a carriage return character and a new-line character (two 
characters, also known as CRLF line terminators).
+While Unix-like operating systems just use a single new-line character.
+The functions below that read an ASCII text file are able to identify lines 
with both kinds of line terminators.
 
-Gnuastro defines a simple format for metadata of table columns in a plain
-text file that is discussed in @ref{Gnuastro text table format}. The
-functions to get information from, read from and write to plain text files
-also follow those conventions.
+Gnuastro defines a simple format for metadata of table columns in a plain text 
file that is discussed in @ref{Gnuastro text table format}.
+The functions to get information from, read from and write to plain text files 
also follow those conventions.
 
 
 @deffn Macro GAL_TXT_LINESTAT_INVALID
 @deffnx Macro GAL_TXT_LINESTAT_BLANK
 @deffnx Macro GAL_TXT_LINESTAT_COMMENT
 @deffnx Macro GAL_TXT_LINESTAT_DATAROW
-Status codes for lines in a plain text file that are returned by
-@code{gal_txt_line_stat}. Lines which have a @key{#} character as their
-first non-white character are considered to be comments. Lines with nothing
-but white space characters are considered blank. The remaining lines are
-considered as containing data.
+Status codes for lines in a plain text file that are returned by 
@code{gal_txt_line_stat}.
+Lines which have a @key{#} character as their first non-white character are 
considered to be comments.
+Lines with nothing but white space characters are considered blank.
+The remaining lines are considered as containing data.
 @end deffn
 
 @deftypefun int gal_txt_line_stat (char @code{*line})
-Check the contents of @code{line} and see if it is a blank, comment, or
-data line. The returned values are the macros that start with
-@code{GAL_TXT_LINESTAT}.
+Check the contents of @code{line} and see if it is a blank, comment, or data 
line.
+The returned values are the macros that start with @code{GAL_TXT_LINESTAT}.
+@end deftypefun
+
+@deftypefun {char *} gal_txt_trim_space (char @code{*str})
+Trim the white space characters before and after the given string.
+The operation is done within the allocated space of the string, so if you need 
the string untouched, please pass an allocated copy of the string to this 
function.
+The returned pointer is within the input string.
+If the input pointer is @code{NULL}, or the string only has white-space 
characters, the returned pointer will be @code{NULL}.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_txt_table_info (char @code{*filename}, 
gal_list_str_t @code{*lines}, size_t @code{*numcols}, size_t @code{*numrows})
-Store the information of each column in a text file @code{filename}, or
-list of strings (@code{lines}) into an array of data structures with
-@code{numcols} elements (one data structure for each column) see
-@ref{Arrays of datasets}. The total number of rows in the table is also put
-into the memory that @code{numrows} points to.
+Store the information of each column in a text file @code{filename}, or list 
of strings (@code{lines}) into an array of data structures with @code{numcols} 
elements (one data structure for each column) see @ref{Arrays of datasets}.
+The total number of rows in the table is also put into the memory that 
@code{numrows} points to.
 
-@code{lines} is a list of strings with each node representing one line
-(including the new-line character), see @ref{List of strings}. It will
-mostly be the output of @code{gal_txt_stdin_read}, which is used to read
-the program's input as separate lines from the standard input (see
-below). Note that @code{filename} and @code{lines} are mutually exclusive
-and one of them must be @code{NULL}.
+@code{lines} is a list of strings with each node representing one line 
(including the new-line character), see @ref{List of strings}.
+It will mostly be the output of @code{gal_txt_stdin_read}, which is used to 
read the program's input as separate lines from the standard input (see below).
+Note that @code{filename} and @code{lines} are mutually exclusive and one of 
them must be @code{NULL}.
 
-This function is just for column information. Therefore it only stores
-meta-data like column name, units and comments. No actual data (contents of
-the columns for example the @code{array} or @code{dsize} elements) will be
-allocated by this function. This is a low-level function particular to
-reading tables in plain text format. To be generic, it is recommended to
-use @code{gal_table_info} which will allow getting information from a
-variety of table formats based on the filename (see @ref{Table input
-output}).
+This function is just for column information.
+Therefore it only stores meta-data like column name, units and comments.
+No actual data (contents of the columns for example the @code{array} or 
@code{dsize} elements) will be allocated by this function.
+This is a low-level function particular to reading tables in plain text format.
+To be generic, it is recommended to use @code{gal_table_info} which will allow 
getting information from a variety of table formats based on the filename (see 
@ref{Table input output}).
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_txt_table_read (char @code{*filename}, 
gal_list_str_t @code{*lines}, size_t @code{numrows}, gal_data_t 
@code{*colinfo}, gal_list_sizet_t @code{*indexll}, size_t @code{minmapsize}, 
int @code{quietmmap})
-Read the columns given in the list @code{indexll} from a plain text file
-(@code{filename}) or list of strings (@code{lines}), into a linked list of
-data structures (see @ref{List of size_t} and @ref{List of gal_data_t}). If
-the necessary space for each column is larger than @code{minmapsize}, don't
-keep it in the RAM, but in a file on the HDD/SSD. For more one
-@code{minmapsize} and @code{quietmmap}, see the description under the same
-name in @ref{Generic data container}.
+Read the columns given in the list @code{indexll} from a plain text file 
(@code{filename}) or list of strings (@code{lines}), into a linked list of data 
structures (see @ref{List of size_t} and @ref{List of gal_data_t}).
+If the necessary space for each column is larger than @code{minmapsize}, don't 
keep it in the RAM, but in a file on the HDD/SSD.
+For more one @code{minmapsize} and @code{quietmmap}, see the description under 
the same name in @ref{Generic data container}.
 
-@code{lines} is a list of strings with each node representing one line
-(including the new-line character), see @ref{List of strings}. It will
-mostly be the output of @code{gal_txt_stdin_read}, which is used to read
-the program's input as separate lines from the standard input (see
-below). Note that @code{filename} and @code{lines} are mutually exclusive
-and one of them must be @code{NULL}.
+@code{lines} is a list of strings with each node representing one line 
(including the new-line character), see @ref{List of strings}.
+It will mostly be the output of @code{gal_txt_stdin_read}, which is used to 
read the program's input as separate lines from the standard input (see below).
+Note that @code{filename} and @code{lines} are mutually exclusive and one of 
them must be @code{NULL}.
 
-Note that this is a low-level function, so the output data list is the
-inverse of the input indexs linked list. It is recommended to use
-@code{gal_table_read} for generic reading of tables in any format, see
-@ref{Table input output}.
+Note that this is a low-level function, so the output data list is the inverse 
of the input indexs linked list.
+It is recommended to use @code{gal_table_read} for generic reading of tables 
in any format, see @ref{Table input output}.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_txt_image_read (char @code{*filename}, 
gal_list_str_t @code{*lines}, size_t @code{minmapsize}, int @code{quietmmap})
-Read the 2D plain text dataset in file (@code{filename}) or list of strings
-(@code{lines}) into a dataset and return the dataset. If the necessary
-space for the image is larger than @code{minmapsize}, don't keep it in the
-RAM, but in a file on the HDD/SSD. For more on @code{minmapsize} and
-@code{quietmmap}, see the description under the same name in @ref{Generic
-data container}.
+Read the 2D plain text dataset in file (@code{filename}) or list of strings 
(@code{lines}) into a dataset and return the dataset.
+If the necessary space for the image is larger than @code{minmapsize}, don't 
keep it in the RAM, but in a file on the HDD/SSD.
+For more on @code{minmapsize} and @code{quietmmap}, see the description under 
the same name in @ref{Generic data container}.
 
-@code{lines} is a list of strings with each node representing one line
-(including the new-line character), see @ref{List of strings}. It will
-mostly be the output of @code{gal_txt_stdin_read}, which is used to read
-the program's input as separate lines from the standard input (see
-below). Note that @code{filename} and @code{lines} are mutually exclusive
-and one of them must be @code{NULL}.
+@code{lines} is a list of strings with each node representing one line 
(including the new-line character), see @ref{List of strings}.
+It will mostly be the output of @code{gal_txt_stdin_read}, which is used to 
read the program's input as separate lines from the standard input (see below).
+Note that @code{filename} and @code{lines} are mutually exclusive and one of 
them must be @code{NULL}.
 @end deftypefun
 
 @deftypefun {gal_list_str_t *} gal_txt_stdin_read (long 
@code{timeout_microsec})
 @cindex Standard input
-Read the complete standard input and return a list of strings with each
-line (including the new-line character) as one node of that list. If the
-standard input is already filled (for example connected to another
-program's output with a pipe), then this function will parse the whole
-stream.
-
-If Standard input is not pre-configured and the @emph{first line} is
-typed/written in the terminal before @code{timeout_microsec} micro-seconds,
-it will continue parsing until reaches an end-of-file character
-(@key{CTRL-D} after a new-line on the keyboard) with no time limit. If
-nothing is entered before @code{timeout_microsec} micro-seconds, it will
-return @code{NULL}.
+Read the complete standard input and return a list of strings with each line 
(including the new-line character) as one node of that list.
+If the standard input is already filled (for example connected to another 
program's output with a pipe), then this function will parse the whole stream.
+
+If Standard input is not pre-configured and the @emph{first line} is 
typed/written in the terminal before @code{timeout_microsec} micro-seconds, it 
will continue parsing until reaches an end-of-file character (@key{CTRL-D} 
after a new-line on the keyboard) with no time limit.
+If nothing is entered before @code{timeout_microsec} micro-seconds, it will 
return @code{NULL}.
 
-All the functions that can read plain text tables will accept a filename as
-well as a list of strings (intended to be the output of this function for
-using Standard input). The reason for keeping the standard input is that
-once something is read from the standard input, it is hard to put it
-back. We often need to read a text file several times: once to count how
-many columns it has and which ones are requested, and another time to read
-the desired columns. So it easier to keep it all in allocated memory and
-pass it on from the start for each round.
+All the functions that can read plain text tables will accept a filename as 
well as a list of strings (intended to be the output of this function for using 
Standard input).
+The reason for keeping the standard input is that once something is read from 
the standard input, it is hard to put it back.
+We often need to read a text file several times: once to count how many 
columns it has and which ones are requested, and another time to read the 
desired columns.
+So it easier to keep it all in allocated memory and pass it on from the start 
for each round.
 @end deftypefun
 
-@deftypefun void gal_txt_write (gal_data_t @code{*cols}, gal_list_str_t 
@code{*comment}, char @code{*filename}, uint8_t @code{colinfoinstdout})
-Write @code{cols} in a plain text file @code{filename}. @code{cols} may
-have one or two dimensions which determines the output:
+@deftypefun void gal_txt_write (gal_data_t @code{*cols}, struct 
gal_fits_list_key_t @code{**keylist}, gal_list_str_t @code{*comment}, char 
@code{*filename}, uint8_t @code{colinfoinstdout})
+Write @code{cols} in a plain text file @code{filename}.
+@code{cols} may have one or two dimensions which determines the output:
 
 @table @asis
 @item 1D
-@code{cols} is treated as a column and a list of datasets (see @ref{List of
-gal_data_t}): every node in the list is written as one column in a
-table.
+@code{cols} is treated as a column and a list of datasets (see @ref{List of 
gal_data_t}): every node in the list is written as one column in a table.
 
 @item 2D
-@code{cols} is a two dimensional array, it cannot be treated as a list
-(only one 2D array can currently be written to a text file). So if
-@code{cols->next!=NULL} the next nodes in the list are ignored and will not
-be written.
+@code{cols} is a two dimensional array, it cannot be treated as a list (only 
one 2D array can currently be written to a text file).
+So if @code{cols->next!=NULL} the next nodes in the list are ignored and will 
not be written.
 @end table
 
-This is a low-level function for tables. It is recommended to use
-@code{gal_table_write} for generic writing of tables in a variety of
-formats, see @ref{Table input output}.
+This is a low-level function for tables.
+It is recommended to use @code{gal_table_write} for generic writing of tables 
in a variety of formats, see @ref{Table input output}.
+
+It is possible to add two types of metadata to the printed table: comments and 
keywords.
+Each string in the list given to @code{comments} will be printed into the file 
as a separate line, starting with @code{#}.
+Keywords have a more specific and computer-parsable format and are passed 
through @code{keylist}.
+Each keyword is also printed in one line, but with the format below.
+Because of the various components in a keyword, it is thus necessary to use 
the @code{gal_fits_list_key_t} data structure.
+For more, see @ref{FITS header keywords}.
 
-If @code{filename} already exists this function will abort with an error
-and will not write over the existing file. Before calling this function
-make sure if the file exists or not. If @code{comments!=NULL}, a @code{#}
-will be put at the start of each node of the list of strings and will be
-written in the file before the column meta-data in @code{filename} (see
-@ref{List of strings}).
+@example
+# [key] NAME: VALUE / [UNIT] KEYWORD COMMENT.
+@end example
 
-When @code{filename==NULL}, the column information will be printed on the
-standard output (command-line). When @code{colinfoinstdout!=0} and
-@code{filename==NULL} (columns are printed in the standard output), the
-dataset metadata will also printed in the standard output. When printing to
-the standard output, the column information can be piped into another
-program for further processing and thus the meta-data (lines starting with
-a @code{#}) must be ignored. In such cases, you only print the column
-values by passing @code{0} to @code{colinfoinstdout}.
+If @code{filename} already exists this function will abort with an error and 
will not write over the existing file.
+Before calling this function make sure if the file exists or not.
+If @code{comments!=NULL}, a @code{#} will be put at the start of each node of 
the list of strings and will be written in the file before the column meta-data 
in @code{filename} (see @ref{List of strings}).
+
+When @code{filename==NULL}, the column information will be printed on the 
standard output (command-line).
+When @code{colinfoinstdout!=0} and @code{filename==NULL} (columns are printed 
in the standard output), the dataset metadata will also printed in the standard 
output.
+When printing to the standard output, the column information can be piped into 
another program for further processing and thus the meta-data (lines starting 
with a @code{#}) must be ignored.
+In such cases, you only print the column values by passing @code{0} to 
@code{colinfoinstdout}.
 @end deftypefun
 
 
@@ -21568,47 +24829,35 @@ values by passing @code{0} to @code{colinfoinstdout}.
 @subsubsection TIFF files (@file{tiff.h})
 
 @cindex TIFF format
-Outside of astronomy, the TIFF standard is arguably the most commonly used
-format to store high-precision data/images. Unlike FITS however, the TIFF
-standard only supports images (not tables), but like FITS, it has support
-for all standard data types (see @ref{Numeric data types}) which is the
-primary reason other fields use it.
-
-Another similarity of the TIFF and FITS standards is that TIFF supports
-multiple images in one file. The TIFF standard calls each one of these
-images (and their accompanying meta-data) a `directory' (roughly equivalent
-to the FITS extensions). Unlike FITS however, the directories can only be
-identified by their number (counting from zero), recall that in FITS you
-can also use the extension name to identify it.
-
-The functions described here allow easy reading (and later writing) of TIFF
-files within Gnuastro or for users of Gnuastro's libraries. Currently only
-reading is supported, but if you are interested, please get in touch with
-us.
+Outside of astronomy, the TIFF standard is arguably the most commonly used 
format to store high-precision data/images.
+Unlike FITS however, the TIFF standard only supports images (not tables), but 
like FITS, it has support for all standard data types (see @ref{Numeric data 
types}) which is the primary reason other fields use it.
+
+Another similarity of the TIFF and FITS standards is that TIFF supports 
multiple images in one file.
+The TIFF standard calls each one of these images (and their accompanying 
meta-data) a `directory' (roughly equivalent to the FITS extensions).
+Unlike FITS however, the directories can only be identified by their number 
(counting from zero), recall that in FITS you can also use the extension name 
to identify it.
+
+The functions described here allow easy reading (and later writing) of TIFF 
files within Gnuastro or for users of Gnuastro's libraries.
+Currently only reading is supported, but if you are interested, please get in 
touch with us.
 
 @deftypefun {int} gal_tiff_name_is_tiff (char @code{*name})
-Return @code{1} if @code{name} has a TIFF suffix. This can be used to make
-sure that a given input file is TIFF. See @code{gal_tiff_suffix_is_tiff}
-for a list of recognized suffixes.
+Return @code{1} if @code{name} has a TIFF suffix.
+This can be used to make sure that a given input file is TIFF.
+See @code{gal_tiff_suffix_is_tiff} for a list of recognized suffixes.
 @end deftypefun
 
 @deftypefun {int} gal_tiff_suffix_is_tiff (char @code{*name})
-Return @code{1} if @code{suffix} is a recognized TIFF suffix. The
-recognized suffixes are @file{tif}, @file{tiff}, @file{TIFF} and
-@file{TIFF}.
+Return @code{1} if @code{suffix} is a recognized TIFF suffix.
+The recognized suffixes are @file{tif}, @file{tiff}, @file{TIFF} and 
@file{TIFF}.
 @end deftypefun
 
 @deftypefun {size_t} gal_tiff_dir_string_read (char @code{*string})
-Return the number within @code{string} as a @code{size_t} number to
-identify a TIFF directory. Note that the directories start counting from
-zero.
+Return the number within @code{string} as a @code{size_t} number to identify a 
TIFF directory.
+Note that the directories start counting from zero.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_tiff_read (char @code{*filename}, size_t 
@code{dir}, size_t @code{minmapsize}, int @code{quietmmap})
-Read the @code{dir} directory within the TIFF file @code{filename} and
-return the contents of that TIFF directory as @code{gal_data_t}. If the
-directory's image contains multiple channels, the output will be a list
-(see @ref{List of gal_data_t}).
+Read the @code{dir} directory within the TIFF file @code{filename} and return 
the contents of that TIFF directory as @code{gal_data_t}.
+If the directory's image contains multiple channels, the output will be a list 
(see @ref{List of gal_data_t}).
 @end deftypefun
 
 
@@ -21619,51 +24868,38 @@ directory's image contains multiple channels, the 
output will be a list
 @subsubsection JPEG files (@file{jpeg.h})
 
 @cindex JPEG format
-The JPEG file format is one of the most common formats for storing and
-transferring images, recognized by almost all image rendering and
-processing programs. In particular, because of its lossy compression
-algorithm, JPEG files can have low volumes, making it used heavily on the
-internet. For more on this file format, and a comparison with others,
-please see @ref{Recognized file formats}.
-
-For scientific purposes, the lossy compression and very limited dynamic
-range (8-bit integers) make JPEG very unattractive for storing of valuable
-data. However, because of its commonality, it will inevitably be needed in
-some situations. The functions here can be used to read and write JPEG
-images into Gnuastro's @ref{Generic data container}. If the JPEG file has
-more than one color channel, each channel is treated as a separate node in
-a list of datasets (see @ref{List of gal_data_t}).
+The JPEG file format is one of the most common formats for storing and 
transferring images, recognized by almost all image rendering and processing 
programs.
+In particular, because of its lossy compression algorithm, JPEG files can have 
low volumes, making it used heavily on the internet.
+For more on this file format, and a comparison with others, please see 
@ref{Recognized file formats}.
+
+For scientific purposes, the lossy compression and very limited dynamic range 
(8-bit integers) make JPEG very unattractive for storing of valuable data.
+However, because of its commonality, it will inevitably be needed in some 
situations.
+The functions here can be used to read and write JPEG images into Gnuastro's 
@ref{Generic data container}.
+If the JPEG file has more than one color channel, each channel is treated as a 
separate node in a list of datasets (see @ref{List of gal_data_t}).
 
 @deftypefun {int} gal_jpeg_name_is_jpeg (char @code{*name})
-Return @code{1} if @code{name} has a JPEG suffix. This can be used to make
-sure that a given input file is JPEG. See @code{gal_jpeg_suffix_is_jpeg}
-for a list of recognized suffixes.
+Return @code{1} if @code{name} has a JPEG suffix.
+This can be used to make sure that a given input file is JPEG.
+See @code{gal_jpeg_suffix_is_jpeg} for a list of recognized suffixes.
 @end deftypefun
 
 @deftypefun {int} gal_jpeg_suffix_is_jpeg (char @code{*name})
-Return @code{1} if @code{suffix} is a recognized JPEG suffix. The
-recognized suffixes are @code{.jpg}, @code{.JPG}, @code{.jpeg},
-@code{.JPEG}, @code{.jpe}, @code{.jif}, @code{.jfif} and @code{.jfi}.
+Return @code{1} if @code{suffix} is a recognized JPEG suffix.
+The recognized suffixes are @code{.jpg}, @code{.JPG}, @code{.jpeg}, 
@code{.JPEG}, @code{.jpe}, @code{.jif}, @code{.jfif} and @code{.jfi}.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_jpeg_read (char @code{*filename}, size_t 
@code{minmapsize}, int @code{quietmmap})
-Read the JPEG file @code{filename} and return the contents as
-@code{gal_data_t}. If the directory's image contains multiple
-colors/channels, the output will be a list with one node per color/channel
-(see @ref{List of gal_data_t}).
+Read the JPEG file @code{filename} and return the contents as 
@code{gal_data_t}.
+If the directory's image contains multiple colors/channels, the output will be 
a list with one node per color/channel (see @ref{List of gal_data_t}).
 @end deftypefun
 
 @cindex JPEG compression quality
 @deftypefun {void} gal_jpeg_write (gal_data_t @code{*in}, char 
@code{*filename}, uint8_t @code{quality}, float @code{widthincm})
-Write the given dataset (@code{in}) into @file{filename} (a JPEG file). If
-@code{in} is a list, then each node in the list will be a color channel,
-therefore there can only be 1, 3 or 4 nodes in the list. If the number of
-nodes is different, then this function will abort the program with a
-message describing the cause. The lossy JPEG compression level can be set
-through @code{quality} which is a value between 0 and 100 (inclusive, 100
-being the best quality). The display width of the JPEG file in units of
-centimeters (to suggest to viewers/users, only a meta-data) can be set
-through @code{widthincm}.
+Write the given dataset (@code{in}) into @file{filename} (a JPEG file).
+If @code{in} is a list, then each node in the list will be a color channel, 
therefore there can only be 1, 3 or 4 nodes in the list.
+If the number of nodes is different, then this function will abort the program 
with a message describing the cause.
+The lossy JPEG compression level can be set through @code{quality} which is a 
value between 0 and 100 (inclusive, 100 being the best quality).
+The display width of the JPEG file in units of centimeters (to suggest to 
viewers/users, only a meta-data) can be set through @code{widthincm}.
 @end deftypefun
 
 
@@ -21802,13 +25038,34 @@ and white.
 @node World Coordinate System, Arithmetic on datasets, File input output, 
Gnuastro library
 @subsection World Coordinate System (@file{wcs.h})
 
-The FITS standard defines the world coordinate system (WCS) as a mechanism
-to associate physical values to positions within a dataset. For example, it
-can be used to convert pixel coordinates in an image to celestial
-coordinates like the right ascension and declination. The functions in this
-section are mainly just wrappers over CFITSIO, WCSLIB and GSL library
-functions to help in common applications.
+The FITS standard defines the world coordinate system (WCS) as a mechanism to 
associate physical values to positions within a dataset.
+For example, it can be used to convert pixel coordinates in an image to 
celestial coordinates like the right ascension and declination.
+The functions in this section are mainly just wrappers over CFITSIO, WCSLIB 
and GSL library functions to help in common applications.
+
+@deffn  Macro GAL_WCS_DISTORTION_TPD
+@deffnx Macro GAL_WCS_DISTORTION_SIP
+@deffnx Macro GAL_WCS_DISTORTION_TPV
+@deffnx Macro GAL_WCS_DISTORTION_DSS
+@deffnx Macro GAL_WCS_DISTORTION_WAT
+@deffnx Macro GAL_WCS_DISTORTION_INVALID
+@cindex WCS distortion
+@cindex Distortion, WCS
+@cindex TPD WCS distortion
+@cindex SIP WCS distortion
+@cindex TPV WCS distortion
+@cindex DSS WCS distortion
+@cindex WAT WCS distortion
+@cindex Prior WCS distortion
+@cindex sequent WCS distortion
+Gnuastro identifiers of the various WCS distortion conventions, for more, see 
Calabretta et al. (2004, 
preprint)@footnote{@url{https://www.atnf.csiro.au/people/mcalabre/WCS/dcs_20040422.pdf}}.
+Among these, SIP is a prior distortion, the rest other are sequent distortions.
+TPD is a superset of all these, hence it has both prior and sequeal distortion 
coefficients.
+More information is given in the documentation of @code{dis.h}, from the 
WCSLIB 
manual@footnote{@url{https://www.atnf.csiro.au/people/mcalabre/WCS/wcslib/dis_8h.html}}.
+@end deffn
 
+@deffn Macro GAL_WCS_FLTERROR
+Limit of rounding for floating point errors.
+@end deffn
 
 @deftypefun {struct wcsprm *} gal_wcs_read_fitsptr (fitsfile @code{*fptr}, 
size_t @code{hstartwcs}, size_t @code{hendwcs}, int @code{*nwcs})
 [@strong{Not thread-safe}] Return the WCSLIB @code{wcsprm} structure that
@@ -21822,29 +25079,47 @@ the returned structure with WCSLIB's @code{wcsvfree} 
keyword:
 status = wcsvfree(&nwcs,&wcs);
 @end example
 
-If you don't want to search the full FITS header for WCS-related FITS
-keywords (for example due to conflicting keywords), but only a specific
-range of the header keywords you can use the @code{hstartwcs} and
-@code{hendwcs} arguments to specify the keyword number range (counting from
-zero). If @code{hendwcs} is larger than @code{hstartwcs}, then only
-keywords in the given range will be checked. Hence, to ignore this feature
-(and search the full FITS header), give both these arguments the same
-value.
+If you don't want to search the full FITS header for WCS-related FITS keywords 
(for example due to conflicting keywords), but only a specific range of the 
header keywords you can use the @code{hstartwcs} and @code{hendwcs} arguments 
to specify the keyword number range (counting from zero).
+If @code{hendwcs} is larger than @code{hstartwcs}, then only keywords in the 
given range will be checked.
+Hence, to ignore this feature (and search the full FITS header), give both 
these arguments the same value.
 
-If the WCS information couldn't be read from the FITS file, this function
-will return a @code{NULL} pointer and put a zero in @code{nwcs}. A WCSLIB
-error message will also be printed in @code{stderr} if there was an error.
+If the WCS information couldn't be read from the FITS file, this function will 
return a @code{NULL} pointer and put a zero in @code{nwcs}.
+A WCSLIB error message will also be printed in @code{stderr} if there was an 
error.
 
-This function is just a wrapper over WCSLIB's @code{wcspih} function which
-is not thread-safe. Therefore, be sure to not call this function
-simultaneously (over multiple threads).
+This function is just a wrapper over WCSLIB's @code{wcspih} function which is 
not thread-safe.
+Therefore, be sure to not call this function simultaneously (over multiple 
threads).
 @end deftypefun
 
 @deftypefun {struct wcsprm *} gal_wcs_read (char @code{*filename}, char 
@code{*hdu}, size_t @code{hstartwcs}, size_t @code{hendwcs}, int @code{*nwcs})
-[@strong{Not thread-safe}] Return the WCSLIB structure that is read from
-the HDU/extension @code{hdu} of the file @code{filename}. Also put the
-number of coordinate representations found into the space that @code{nwcs}
-points to. Please see @code{gal_wcs_read_fitsptr} for more.
+[@strong{Not thread-safe}] Return the WCSLIB structure that is read from the 
HDU/extension @code{hdu} of the file @code{filename}.
+Also put the number of coordinate representations found into the space that 
@code{nwcs} points to.
+Please see @code{gal_wcs_read_fitsptr} for more.
+@end deftypefun
+
+@deftypefun {struct wcsprm *} gal_wcs_create (double @code{*crpix}, double 
@code{*crval}, double @code{*cdelt}, double @code{*pc}, char @code{**cunit}, 
char @code{**ctype}, size_t @code{ndim})
+Given all the most common standard components of the WCS standard, construct a 
@code{struct wcsprm}, initialize and set it for future processing.
+See the FITS WCS standard for more on these keywords.
+All the arrays must have @code{ndim} elements with them except for @code{pc} 
which should have @code{ndim*ndim} elements (a square matrix).
+Also, @code{cunit} and @code{ctype} are arrays of strings.
+@end deftypefun
+
+@deftypefun {char *} gal_wcs_dimension_name (struct wcsprm @code{*wcs}, size_t 
@code{dimension})
+Return an allocated string array (that should be freed later) containing the 
first part of the @code{CTYPEi} FITS keyword (which contains the dimension name 
in the FITS standard).
+For example if @code{CTYPE1} is @code{RA---TAN}, the string that function 
returns will be @code{RA}.
+Recall that the second component of @code{CTYPEi} contains the type of 
projection.
+@end deftypefun
+
+@deftypefun void gal_wcs_write (struct wcsprm @code{*wcs}, char 
@code{*filename}, char @code{*extname}, gal_fits_list_key_t @code{*headers}, 
char @code{*program_string})
+Write the given WCS structure into the second extension of an empty FITS 
header.
+The first/primary extension will be empty like the default format of all 
Gnuastro outputs.
+When @code{extname!=NULL} it will be used as the FITS extension name.
+Any set of extra headers can also be written through the @code{headers} list 
and if @code{program_string!=NULL} it will be used in a commented keyword title 
just above the written version information.
+@end deftypefun
+
+@deftypefun void gal_wcs_write_in_fitsptr(fitsfile @code{*fptr}, struct wcsprm 
@code{*wcs})
+Convert the input @code{wcs} structure (keeping the WCS programmatically) into 
FITS keywords and write them into the given FITS file pointer.
+This is a relatively low-level function which assumes the FITS file has 
already been opened with CFITSIO.
+If you just want to write the WCS into an empty file, you can use 
@code{gal_wcs_write} (which internally calls this function after creating the 
FITS file and later closes it safely).
 @end deftypefun
 
 @deftypefun {struct wcsprm *} gal_wcs_copy (struct wcsprm @code{*wcs})
@@ -21876,6 +25151,13 @@ several methods to store the matrix. The output is an 
allocated square
 matrix with each side equal to the number of dimensions.
 @end deftypefun
 
+@deftypefun void gal_wcs_clean_small_errors (struct wcsprm @code{*wcs})
+Errors can make small differences between the pixel-scale elements 
(@code{CDELT}) and can also lead to extremely small values in the @code{PC} 
matrix.
+With this function, such errors will be ``cleaned'' as follows: 1) if the 
maximum difference between the @code{CDELT} elements is smaller than the 
reference error, it will be set to the mean value.
+When the FITS keyword @code{CRDER} (optional) is defined it will be used as a 
reference, if not the default value is @code{GAL_WCS_FLTERROR}.
+2) If any of the PC elements differ from 0, 1 or -1 by less than 
@code{GAL_WCS_FLTERROR}, they will be rounded to the respective value.
+@end deftypefun
+
 @deftypefun void gal_wcs_decompose_pc_cdelt (struct wcsprm @code{*wcs})
 Decompose the @code{PCi_j} and @code{CDELTi} elements of
 @code{wcs}. According to the FITS standard, in the @code{PCi_j} WCS
@@ -21894,6 +25176,54 @@ correspond to the pixel scale, and the @code{PCi_j} 
will correction show
 the rotation.
 @end deftypefun
 
+@deftypefun int gal_wcs_distortion_from_string (char @code{*distortion})
+Convert the given string (assumed to be a FITS-standard, string-based 
distortion identifier) to a Gnuastro's integer-based distortion identifier (one 
of the @code{GAL_WCS_DISTORTION_*} macros defined above).
+The sting-based distortion identifiers have three characters and are all in 
capital letters.
+@end deftypefun
+
+@deftypefun int gal_wcs_distortion_to_string (int @code{distortion})
+Convert the given Gnuastro integer-based distortion identifier (one of the 
@code{GAL_WCS_DISTORTION_*} macros defined above) to the string-based 
distortion identifier) of the FITS standard.
+The sting-based distortion identifiers have three characters and are all in 
capital letters.
+@end deftypefun
+
+@cindex WCS distortion
+@cindex Distortion, WCS
+@deftypefun {int} gal_wcs_distortion_identify (struct wcsprm @code{*wcs})
+Returns the Gnuastro identifier for the distortion of the input WCS structure.
+The returned value is one of the @code{GAL_WCS_DISTORTION_*} macros defined 
above.
+When the input pointer to a structure is @code{NULL}, or it doesn't contain a 
distortion, the returned value will be @code{GAL_WCS_DISTORTION_INVALID}.
+@end deftypefun
+
+@cindex SIP WCS distortion
+@cindex TPV WCS distortion
+@deftypefun {struct wcsprm *} gal_wcs_distortion_convert(struct wcsprm 
@code{*inwcs}, int @code{outdisptype}, size_t @code{*fitsize})
+Return a newly allocated WCS structure, where the distortion is implemented in 
a different standard, identified by the identifier @code{outdisptype}.
+The Gnuastro WCS distortion identifiers are defined in the 
@code{GAL_WCS_DISTORTION_*} macros mentioned above.
+
+The available conversions in this function will grow.
+Currently it only supports converting TPV to SIP and vice versa, following the 
recipe of Shupe et al. (2012)@footnote{Proc. of SPIE Vol. 8451  84511M-1. 
@url{https://doi.org/10.1117/12.925460}, also available at 
@url{http://web.ipac.caltech.edu/staff/shupe/reprints/SIP_to_PV_SPIE2012.pdf}.}.
+Please get in touch with us if you need other types of conversions.
+
+For some conversions, direct analytical conversions don't exist.
+It is thus necessary to model and fit the two types.
+In such cases, it is also necessary to specify the @code{fitsize} array that 
is the size of the array along each C-ordered dimension, so you can simply pass 
the @code{dsize} element of your @code{gal_data_t} dataset, see @ref{Generic 
data container}.
+Currently this is only necessary when converting TPV to SIP.
+For other conversions you may simply pass a @code{NULL} pointer.
+
+For example, if you want to convert the TPV coefficients of your input 
@file{image.fits} to SIP coefficients, you can use the following functions 
(which are also available as a command-line operation in @ref{Fits}).
+
+@example
+int nwcs;
+gal_data_t *data=gal_fits_img_read("image.fits", "1", -1, 1);
+inwcs=gal_wcs_read("image.fits", "1", 0, &nwcs);
+data->wcs=gal_wcs_distortion_convert(inwcs, GAL_WCS_DISTORTION_TPV,
+                                     NULL);
+wcsfree(inwcs);
+gal_fits_img_write(data, "tpv.fits", NULL, NULL);
+@end example
+
+@end deftypefun
+
 @deftypefun double gal_wcs_angular_distance_deg (double @code{r1}, double 
@code{d1}, double @code{r2}, double @code{d2})
 Return the angular distance (in degrees) between a point located at
 (@code{r1}, @code{d1}) to (@code{r2}, @code{d2}). All input coordinates are
@@ -21918,11 +25248,16 @@ return @code{NULL}.
 @end deftypefun
 
 @deftypefun double gal_wcs_pixel_area_arcsec2 (struct wcsprm @code{*wcs})
-Return the pixel area of @code{wcs} in arcsecond squared. If the input WCS
+Return the pixel area of @code{wcs} in arc-second squared. If the input WCS
 structure is not two dimensional and the units (@code{CUNIT} keywords) are
 not @code{deg} (for degrees), then this function will return a NaN.
 @end deftypefun
 
+@deftypefun int gal_wcs_coverage (char @code{*filename}, char @code{*hdu}, 
size_t @code{*ondim}, double @code{**ocenter}, double @code{**owidth}, double 
@code{**omin}, double @code{**omax})
+Find the sky coverage of the image HDU (@code{hdu}) within @file{filename}.
+The the number of dimensions is written into @code{ndim}, and space for the 
various output arrays is internally allocated and filled with the respective 
values.
+@end deftypefun
+
 @deftypefun {gal_data_t *} gal_wcs_world_to_img (gal_data_t @code{*coords}, 
struct wcsprm @code{*wcs}, int @code{inplace})
 Convert the linked list of world coordinates in @code{coords} to a linked
 list of image coordinates given the input WCS structure. @code{coords} must
@@ -22094,6 +25429,38 @@ different type, you can convert the input to a 
floating point type with
 @code{gal_data_copy_to_new_type_free}(see @ref{Copying datasets}).
 @end deffn
 
+@deffn  Macro GAL_ARITHMETIC_OP_SIN
+@deffnx Macro GAL_ARITHMETIC_OP_COS
+@deffnx Macro GAL_ARITHMETIC_OP_TAN
+@deffnx Macro GAL_ARITHMETIC_OP_ASIN
+@deffnx Macro GAL_ARITHMETIC_OP_ACOS
+@deffnx Macro GAL_ARITHMETIC_OP_ATAN
+@deffnx Macro GAL_ARITHMETIC_OP_ATAN2
+Trigonometric functions (and their inverse).
+All the angles, either inputs or outputs, are in units of degrees.
+@end deffn
+
+@deffn  Macro GAL_ARITHMETIC_OP_SINH
+@deffnx Macro GAL_ARITHMETIC_OP_COSH
+@deffnx Macro GAL_ARITHMETIC_OP_TANH
+@deffnx Macro GAL_ARITHMETIC_OP_ASINH
+@deffnx Macro GAL_ARITHMETIC_OP_ACOSH
+@deffnx Macro GAL_ARITHMETIC_OP_ATANH
+Hyperbolic functions (and their inverse).
+@end deffn
+
+@deffn  Macro GAL_ARITHMETIC_OP_RA_TO_DEGREE
+@deffnx Macro GAL_ARITHMETIC_OP_DEC_TO_DEGREE
+@deffnx Macro GAL_ARITHMETIC_OP_DEGREE_TO_RA
+@deffnx Macro GAL_ARITHMETIC_OP_DEGREE_TO_DEC
+@cindex Sexagesimal
+@cindex Declination
+@cindex Right Ascension
+Unary operators to convert between degrees (as a single floating point number) 
to the sexagesimal Right Ascension and Declination format (as strings, 
respectively in the format of @code{_h_m_s} and @code{_d_m_s}).
+The first two operators expect a string operand (in the sexagesimal formats 
mentioned above, but also in the @code{_:_:_}) and will return a 
double-precision floating point operand.
+The latter two are the opposite.
+@end deffn
+
 @deffn  Macro GAL_ARITHMETIC_OP_MINVAL
 @deffnx Macro GAL_ARITHMETIC_OP_MAXVAL
 @deffnx Macro GAL_ARITHMETIC_OP_NUMBERVAL
@@ -22136,6 +25503,12 @@ output will be @code{GAL_TYPE_UINT32} and for the 
rest, it will be
 @code{GAL_TYPE_FLOAT32}.
 @end deffn
 
+@deffn Macro GAL_ARITHMETIC_OP_QUANTILE
+Similar to the operands above (including @code{GAL_ARITHMETIC_MIN}), except 
that when @code{gal_arithmetic} is called with these operators, it requires two 
arguments.
+The first is the list of datasets like before, and the second is the 1-element 
dataset with the quantile value.
+The output type is the same as the inputs.
+@end deffn
+
 @deffn  Macro GAL_ARITHMETIC_OP_SIGCLIP_STD
 @deffnx Macro GAL_ARITHMETIC_OP_SIGCLIP_MEAN
 @deffnx Macro GAL_ARITHMETIC_OP_SIGCLIP_MEDIAN
@@ -22150,11 +25523,14 @@ the termination criteria (see @ref{Sigma clipping}). 
The output type of
 for the rest it will be @code{GAL_TYPE_FLOAT32}.
 @end deffn
 
+@deffn Macro GAL_ARITHMETIC_OP_SIZE
+Size operator that will return a single value for datasets of any kind. When 
@code{gal_arithmetic} is called with this operator, it requires two arguments.
+The first is the dataset, and the second is a single integer value.
+The output type is a single integer.
+@end deffn
+
 @deffn Macro GAL_ARITHMETIC_OP_POW
-Binary operator to-power operator. When @code{gal_arithmetic} is called
-with any of these operators, it will expect two operands: raising the first
-by the second. This operator only accepts floating point inputs and the
-output is also floating point.
+Binary operator to-power operator. When @code{gal_arithmetic} is called with 
any of these operators, it will expect two operands: raising the first by the 
second (returning a floating point, inputs can be integers).
 @end deffn
 
 @deffn  Macro GAL_ARITHMETIC_OP_BITAND
@@ -22205,6 +25581,12 @@ in @ref{Arithmetic}. So in your programs, it might be 
preferable to
 directly use those functions.
 @end deffn
 
+@deffn  Macro GAL_ARITHMETIC_OP_MAKENEW
+Create a new, zero-valued dataset with an unsigned 8-bit data type.
+The length along each dimension of the dataset should be given as a single 
list of @code{gal_data_t}s.
+The number of dimensions is derived from the number of nodes in the list and 
the length along each dimension is the single-valued element within that list.
+Just note that the list should be in the reverse of the desired dimensions.
+@end deffn
 
 @deftypefun {gal_data_t *} gal_arithmetic (int @code{operator}, size_t 
@code{numthreads}, int @code{flags}, ...)
 Do the arithmetic operation of @code{operator} on the given operands (the
@@ -22955,16 +26337,29 @@ pixels of the overlap will be put in @code{fpixel_o} 
and @code{lpixel_o}.
 @node Polygons, Qsort functions, Bounding box, Gnuastro library
 @subsection Polygons (@file{polygon.h})
 
-Polygons are commonly necessary in image processing. In Gnuastro, they are
-used in Crop (see @ref{Crop}) for cutting out non-rectangular regions of a
-image. Warp (see @ref{Warp}) uses them to warp the images into a new pixel
-grid. The polygon related Gnuastro library macros and functions are
-introduced here.
+Polygons are commonly necessary in image processing.
+For example in Crop they are used for cutting out non-rectangular regions of a 
image (see @ref{Crop}), and in Warp, for mapping different pixel grids over 
each other (see @ref{Warp}).
+
+@cindex Convex polygons
+@cindex Concave polygons
+@cindex Polygons, Convex
+@cindex Polygons, Concave
+Polygons come in two classes: convex and concave (or generally, non-convex!), 
see below for a demonstration.
+Convex polygons are those where all inner angles are less than 180 degrees.
+By contrast, a convex polygon is one where an inner angle may be more than 180 
degress.
+
+@example
+            Concave Polygon        Convex Polygon
+
+             D --------C          D------------- C
+              \        |        E /              |
+               \E      |          \              |
+               /       |           \             |
+              A--------B             A ----------B
+@end example
 
-In all the functions here the vertices (and points) are defined as an
-array. So a polygon with 4 vertices will be identified with an array of 8
-elements with the first two elements keeping the 2D coordinates of the
-first vertice and so on.
+In all the functions here the vertices (and points) are defined as an array.
+So a polygon with 4 vertices will be identified with an array of 8 elements 
with the first two elements keeping the 2D coordinates of the first vertice and 
so on.
 
 @deffn Macro GAL_POLYGON_MAX_CORNERS
 The largest number of vertices a polygon can have in this library.
@@ -22972,32 +26367,26 @@ The largest number of vertices a polygon can have in 
this library.
 
 @deffn Macro GAL_POLYGON_ROUND_ERR
 @cindex Round-off error
-We have to consider floating point round-off errors when dealing with
-polygons. For example we will take @code{A} as the maximum of @code{A} and
-@code{B} when @code{A>B-GAL_POLYGON_ROUND_ERR}.
+We have to consider floating point round-off errors when dealing with polygons.
+For example we will take @code{A} as the maximum of @code{A} and @code{B} when 
@code{A>B-GAL_POLYGON_ROUND_ERR}.
 @end deffn
 
-@deftypefun void gal_polygon_ordered_corners (double @code{*in}, size_t 
@code{n}, size_t @code{*ordinds})
-We have a simple polygon (that can result from projection, so its edges
-don't collide or it doesn't have holes) and we want to order its corners in
-an anticlockwise fashion. This is necessary for clipping it and finding its
-area later. The input vertices can have practically any order.
-
-The input (@code{in}) is an array containing the coordinates (two values)
-of each vertice. @code{n} is the number of corners. So @code{in} should
-have @code{2*n} elements. The output (@code{ordinds}) is an array with
-@code{n} elements specifying the indexs in order. This array must have been
-allocated before calling this function. The indexes are output for more
-generic usage, for example in a homographic transform (necessary in warping
-an image, see @ref{Warping basics}), the necessary order of vertices is the
-same for all the pixels. In other words, only the positions of the vertices
-change, not the way they need to be ordered. Therefore, this function would
-only be necessary once.
-
-As a summary, the input is unchanged, only @code{n} values will be put in
-the @code{ordinds} array. Such that calling the input coordinates in the
-following fashion will give an anti-clockwise order when there are 4
-vertices:
+@deftypefun void gal_polygon_vertices_sort_convex (double @code{*in}, size_t 
@code{n}, size_t @code{*ordinds})
+We have a simple polygon (that can result from projection, so its edges don't 
collide or it doesn't have holes) and we want to order its corners in an 
anticlockwise fashion.
+This is necessary for clipping it and finding its area later.
+The input vertices can have practically any order.
+
+The input (@code{in}) is an array containing the coordinates (two values) of 
each vertice.
+@code{n} is the number of corners.
+So @code{in} should have @code{2*n} elements.
+The output (@code{ordinds}) is an array with @code{n} elements specifying the 
indexs in order.
+This array must have been allocated before calling this function.
+The indexes are output for more generic usage, for example in a homographic 
transform (necessary in warping an image, see @ref{Warping basics}), the 
necessary order of vertices is the same for all the pixels.
+In other words, only the positions of the vertices change, not the way they 
need to be ordered.
+Therefore, this function would only be necessary once.
+
+As a summary, the input is unchanged, only @code{n} values will be put in the 
@code{ordinds} array.
+Such that calling the input coordinates in the following fashion will give an 
anti-clockwise order when there are 4 vertices:
 
 @example
 1st vertice: in[ordinds[0]*2], in[ordinds[0]*2+1]
@@ -23008,61 +26397,91 @@ vertices:
 
 @cindex Convex Hull
 @noindent
-The implementation of this is very similar to the Graham scan in finding
-the Convex Hull. However, in projection we will never have a concave
-polygon (the left condition below, where this algorithm will get to E
-before D), we will always have a convex polygon (right case) or E won't
-exist!
-
-@example
-            Concave Polygon        Convex Polygon
-
-             D --------C          D------------- C
-              \        |        E /              |
-               \E      |          \              |
-               /       |           \             |
-              A--------B             A ----------B
-@end example
-
-This is because we are always going to be calculating the area of the
-overlap between a quadrilateral and the pixel grid or the quadrilateral
-its self.
+The implementation of this is very similar to the Graham scan in finding the 
Convex Hull.
+However, in projection we will never have a concave polygon (the left 
condition below, where this algorithm will get to E before D), we will always 
have a convex polygon (right case) or E won't exist!
+This is because we are always going to be calculating the area of the overlap 
between a quadrilateral and the pixel grid or the quadrilateral its self.
 
-The @code{GAL_POLYGON_MAX_CORNERS} macro is defined so there will be no
-need to allocate these temporary arrays separately. Since we are dealing
-with pixels, the polygon can't really have too many vertices.
+The @code{GAL_POLYGON_MAX_CORNERS} macro is defined so there will be no need 
to allocate these temporary arrays separately.
+Since we are dealing with pixels, the polygon can't really have too many 
vertices.
+@end deftypefun
 
+@deftypefun int gal_polygon_is_convex (double @code{*v}, size_t @code{n})
+Returns @code{1} if the polygon is convex with vertices defined by @code{v} 
and @code{0} if it is a concave polygon.
+Note that the vertices of the polygon should be sorted in an anti-clockwise 
manner.
 @end deftypefun
 
 @deftypefun double gal_polygon_area (double @code{*v}, size_t @code{n})
-Find the area of a polygon with vertices defined in @code{v}. @code{v}
-points to an array of doubles which keep the positions of the vertices such
-that @code{v[0]} and @code{v[1]} are the positions of the first vertice to
-be considered.
+Find the area of a polygon with vertices defined in @code{v}.
+@code{v} points to an array of doubles which keep the positions of the 
vertices such that @code{v[0]} and @code{v[1]} are the positions of the first 
vertice to be considered.
+@end deftypefun
+
+@deftypefun int gal_polygon_is_inside (double @code{*v}, double @code{*p}, 
size_t @code{n})
+Returns @code{0} if point @code{p} in inside a polygon, either convex or 
concave.
+The vertices of the polygon are defined by @code{v} and @code{0} otherwise, 
they have to be ordered in an anti-clockwise manner.
+This function uses the 
@url{https://en.wikipedia.org/wiki/Point_in_polygon#Winding_number_algorithm, 
winding number algorithm}, to check the points.
+Note that this is a generic function (working on both concave and convex 
polygons, so if you know before-hand that your polygon is convex, it is much 
more efficient to use @code{gal_polygon_is_inside_convex}.
 @end deftypefun
 
-@deftypefun int gal_polygon_pin (double @code{*v}, double @code{*p}, size_t 
@code{n})
-Return @code{1} if the point @code{p} is within the polygon whose vertices
-are defined by @code{v} and @code{0} otherwise. Note that the vertices of
-the polygon have to be sorted in an anti-clock-wise manner.
+@deftypefun int gal_polygon_is_inside_convex (double @code{*v}, double 
@code{*p}, size_t @code{n})
+Return @code{1} if the point @code{p} is within the polygon whose vertices are 
defined by @code{v}.
+The polygon is assumed to be convex, for a more generic function that deals 
with concave and convex polygons, see @code{gal_polygon_is_inside}.
+Note that the vertices of the polygon have to be sorted in an anti-clock-wise 
manner.
 @end deftypefun
 
 @deftypefun int gal_polygon_ppropin (double @code{*v}, double @code{*p}, 
size_t @code{n})
-Similar to @code{gal_polygon_pin}, except that if the point @code{p} is on
+Similar to @code{gal_polygon_is_inside_convex}, except that if the point 
@code{p} is on
 one of the edges of a polygon, this will return @code{0}.
 @end deftypefun
 
+@deftypefun int gal_polygon_is_counterclockwise (double @code{*v}, size_t 
@code{n})
+Returns  @code{1} if the sorted polygon has a counter-clockwise orientation 
and @code{0} otherwise.
+This function uses the concept of ``winding'', which defines the relative 
order in which the vertices of a polygon are listed to determine the 
orientation of vertices.
+For complex polygons (where edges, or sides, intersect), the most significant 
orientation is returned.
+In a complex polygon, when the alternative windings are equal (for example an 
@code{8}-shape) it will return @code{1} (as if it was counter-clockwise).
+Note that the polygon vertices have to be sorted before calling this function.
+@end deftypefun
+
+@deftypefun int gal_polygon_to_counterclockwise (double @code{*v}, size_t 
@code{n})
+Arrange the vertices of the sorted polygon in place, to be in a 
counter-clockwise direction.
+If the input polygon already has a counter-clockwise direction it won't touch 
the input.
+The return value is @code{1} on successful execution.
+This function is just a wrapper over @code{gal_polygon_is_counterclockwise}, 
and will reverse the order of the vertices when necessary necessary.
+@end deftypefun
+
 @deftypefun void gal_polygon_clip (double @code{*s}, size_t @code{n}, double 
@code{*c}, size_t @code{m}, double @code{*o}, size_t @code{*numcrn})
-Clip (find the overlap of) two polygons. This function uses the
-@url{https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm,
-Sutherland-Hodgman} polygon clipping algorithm. Note that the vertices of
-both polygons have to be sorted in an anti-clock-wise manner.
+Clip (find the overlap of) two polygons.
+This function uses the 
@url{https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm, 
Sutherland-Hodgman} polygon clipping algorithm.
+Note that the vertices of both polygons have to be sorted in an 
anti-clock-wise manner.
+@end deftypefun
+
+@deftypefun void gal_polygon_vertices_sort (double @code{*vertices}, size_t 
@code{n}, size_t @code{*ordinds})
+Sort the indexs of the un-ordered @code{vertices} array to a counter-clockwise 
polygon in the already allocated space of @code{ordinds}.
+It is assumed that there are @code{n} vertices, and thus that @code{vertices} 
contains @code{2*n} elements where the two coordinates of the first vertice 
occupy the first two elements of the array and so on.
+
+The polygon can be both concave and convex (see the start of this section).
+However, note that for concave polygons there is no unique sort from an 
un-ordered set of vertices.
+So after this function you may want to use @code{gal_polygon_is_convex} and 
print a warning to check the output if the polygon was concave.
+
+Note that the contents of the @code{vertices} array are left untouched by this 
function.
+If you want to write the ordered vertice coordinates in another array with the 
same size, you can use a loop like this:
+
+@example
+for(i=0;i<n;++i)
+@{
+  ordered[i*2  ] = vertices[ ordinds[i]*2    ];
+  ordered[i*2+1] = vertices[ ordinds[i]*2 + 1];
+@}
+@end example
+
+In this algorithm, we find the rightmost and leftmost points (based on their 
x-coordinate) and use the diagonal vector between those points to group the 
points in arrays based on their position with respect to this vector.
+For anticlockwise sorting, all the points below the vector are sorted by their 
ascending x-coordinates and points above the vector are sorted in decreasing 
order using @code{qsort}.
+Finally, both these arrays are merged together to get the final sorted array 
of points, from which the points are indexed into the @code{ordinds} using 
linear search.
 @end deftypefun
 
 
 
 
-@node Qsort functions, Permutations, Polygons, Gnuastro library
+@node Qsort functions, K-d tree, Polygons, Gnuastro library
 @subsection Qsort functions (@file{qsort.h})
 
 @cindex @code{qsort}
@@ -23191,7 +26610,207 @@ increasing order (first element will have the 
smallest value).
 
 
 
-@node Permutations, Matching, Qsort functions, Gnuastro library
+
+@node K-d tree, Permutations, Qsort functions, Gnuastro library
+@subsection K-d tree (@file{kdtree.h})
+@cindex K-d tree
+K-d tree is a space-partitioning binary search tree for organizing points in a 
k-dimensional space.
+They are a very useful data structure for multidimensional searches like range 
searches and nearest neighbor searches.
+For a more formal and complete introduction see 
@url{https://en.wikipedia.org/wiki/K-d_tree, the Wikipedia page}.
+
+Each non-leaf node in a k-d tree divides the space into two parts, known as 
half-spaces.
+To select the top/root node for partitioning, we find the median of the points 
and make a hyperplane normal to the first dimension.
+The points to the left of this space are represented by the left subtree of 
that node and points to the right of the space are represented by the right 
subtree.
+This is then repeated for all the points in the input, thus associating a 
``left'' and ``right'' branch for each input point.
+
+Gnuastro uses the standard algorithms of the k-d tree with one small 
difference that makes it much more memory and CPU optimized.
+The set of input points that define the tree nodes are given as a list of 
Gnuastro's data container type, see @ref{List of gal_data_t}.
+Each @code{gal_data_t} in the list represents the point's coordinate in one 
dimension, and the first element in the list is the first dimension.
+Hence the number of data values in each @code{gal_data_t} (which must be equal 
in all of them) represents the number of points.
+This is the same format that Gnuastro's Table reading/writing functions 
read/write columns in tables, see @ref{Table input output}.
+
+The output k-d tree is a list of two @code{gal_data_t}s, representing the 
input's row-number (or index, counting from 0) of the left and right subtrees 
of each row.
+Each @code{gal_data_t} thus has the same number of rows (or points) as the 
input, but only containing integers with a type of @code{uint32_t} (unsigned 
32-bit integer).
+If a node has no left, or right subtree, then @code{GAL_BLANK_UINT32} will be 
used.
+Below you can see the simple tree for 2D points from Wikipedia.
+The input point coordinates are represented as two input @code{gal_data_t}s 
(@code{X} and @code{Y}, where @code{X->next=Y} and @code{Y->next=NULL}).
+If you had three dimensional points, you could define an extra 
@code{gal_data_t} such that @code{Y->next=Z} and @code{Z->next=NULL}.
+The output is always a list of two @code{gal_data_t}s, where the first one 
contains the index of the left sub-tree in the input, and the second one, the 
index of the right subtree.
+The index of the root node (@code{0} in the case below@footnote{This example 
input table is the same as the example in Wikipedia (as of December 2020).
+However, on the Wikipedia output, the root node is (7,2), not (5,4).
+The difference is primarily because there are 6 rows and the median element of 
an even number of elements can vary by integer calculation strategies.
+Here we use 0-based indexes for finding median and round to the smaller 
integer.}) is also returned as a single number.
+
+@example
+INDEX         INPUT              OUTPUT              K-D Tree
+(as guide)    X --> Y        LEFT --> RIGHT        (visualized)
+----------    -------        --------------     ------------------
+0             5     4        1        2               (5,4)
+1             2     3        BLANK    4               /   \
+2             7     2        5        3           (2,3)    \
+3             9     6        BLANK    BLANK           \    (7,2)
+4             4     7        BLANK    BLANK         (4,7)  /   \
+5             8     1        BLANK    BLANK               (8,1) (9,6)
+@end example
+
+This format is therefore scalable to any number of dimensions: the number of 
dimensions are determined from the number of nodes in the input list of 
@code{gal_data_t}s (for example, using @code{gal_list_data_number}).
+In Gnuastro's k-d tree implementation, there are thus no special structures to 
keep every tree node (which would take extra memory and would need to be moved 
around as the tree is being created).
+Everything is done internally on the index of each point in the input dataset: 
the only thing that is flipped/sorted during tree creation is the index to the 
input row for any number of dimensions.
+As a result, Gnuastro's k-d tree implementation is very memory and CPU 
efficient and its two output columns can directly be written into a standard 
table (without having to define any special binary format).
+
+@deftypefun {gal_data_t *} gal_kdtree_create (gal_data_t @code{*coords_raw}, 
size_t @code{*root})
+Create a k-d tree in a bottom-up manner (from leaves to the root).
+This function returns two @code{gal_data_t}s connected as a list, see 
description above.
+The first dataset contains the indexes of left and right nodes of the subtrees 
for each input node.
+The index of the root node is written into the memory that @code{root} points 
to.
+@code{coords_raw} is the list of the input points (one @code{gal_data_t} per 
dimension, see above).
+
+For example, assume you have the simple set of points below (from the 
visualized example at the start of this section) in a plain-text file called 
@file{coordinates.txt}:
+
+@example
+$ cat coordinates.txt
+5     4
+2     3
+7     2
+9     6
+4     7
+8     1
+@end example
+
+With the program below, you can calculate the kd-tree, and write it in a FITS 
file (while keeping the root index as a FITS keyword inside of it).
+
+@example
+#include <stdio.h>
+#include <gnuastro/table.h>
+#include <gnuastro/kdtree.h>
+
+int
+main (void)
+@{
+  gal_data_t *input, *kdtree;
+  char kdtreefile[]="kd-tree.fits";
+  char inputfile[]="coordinates.txt";
+
+  /* To write the root within the saved file. */
+  size_t root;
+  char *unit="index";
+  char *keyname="KDTROOT";
+  gal_fits_list_key_t *keylist=NULL;
+  char *comment="k-d tree root index (counting from 0).";
+
+  /* Read the input table. Note: this assumes the table only
+   * contains your input point coordinates (one column for each
+   * dimension). If it contains more columns with other properties
+   * for each point, you can specify which columns to read by
+   * name or number, see the documentation of 'gal_table_read'. */
+  input=gal_table_read(inputfile, "1", NULL, NULL,
+                      GAL_TABLE_SEARCH_NAME, 0, -1, 0, NULL);
+
+  /* Construct a k-d tree. The index of root is stored in `root` */
+  kdtree=gal_kdtree_create(input, &root);
+
+  /* Write the k-d tree to a file and write root index and input
+   * name as FITS keywords ('gal_table_write' frees 'keylist').*/
+  gal_fits_key_list_title_add(&keylist, "k-d tree parameters", 0);
+  gal_fits_key_write_filename("KDTIN", inputfile, &keylist, 0);
+  gal_fits_key_list_add_end(&keylist, GAL_TYPE_SIZE_T, keyname, 0,
+                           &root, 0, comment, 0, unit, 0);
+  gal_table_write(kdtree, &keylist, NULL, GAL_TABLE_FORMAT_BFITS,
+                 kdtreefile, "kdtree", 0);
+
+  /* Clean up and return. */
+  gal_list_data_free(input);
+  gal_list_data_free(kdtree);
+  return EXIT_SUCCESS;
+@}
+@end example
+
+You can inspect the saved k-d tree FITS table with Gnuastro's @ref{Table} 
(first command below), and you can see the keywords containing the root index 
with @ref{Fits} (second command below):
+
+@example
+asttable kd-tree.fits
+astfits kd-tree.fits -h1
+@end example
+
+@end deftypefun
+
+@deftypefun size_t gal_kdtree_nearest_neighbour (gal_data_t 
@code{*coords_raw}, gal_data_t @code{*kdtree}, size_t @code{root}, double 
@code{*point}, double @code{*least_dist})
+Returns the index of the nearest input point to the query point (@code{point}, 
assumed to be an array with same number of elements as @code{gal_data_t}s in 
@code{coords_raw}).
+The distance between the query point and its nearest neighbor is stored in the 
space that @code{least_dist} points to.
+This search is efficient due to the constant checking for the presence of 
possible best points in other branches.
+If it isn't possible for the other branch to have a better nearest neighbor, 
that branch is not searched.
+
+As an example, let's use the k-d tree that was created in the example of 
@code{gal_kdtree_create} (above) and find the nearest row to a given coordinate 
(@code{point}).
+This will be a very common scenario, especially in large and multi-dimensional 
datasets where the k-d tree creation can take long and you don't want to 
re-create the k-d tree every time.
+In the @code{gal_kdtree_create} example output, we also wrote the k-d tree 
root index as a FITS keyword (@code{KDTROOT}), so after loading the two table 
data (input coordinates and k-d tree), we'll read the root from the FITS 
keyword.
+This is a very simple example, but the scalability is clear: for example it is 
trivial to parallelize (see @ref{Library demo - multi-threaded operation}).
+
+@example
+#include <stdio.h>
+#include <gnuastro/table.h>
+#include <gnuastro/kdtree.h>
+
+int
+main (void)
+@{
+  /* INPUT: desired point. */
+  double point[2]=@{8.9,5.9@};
+
+  /* Same as example in description of 'gal_kdtree_create'. */
+  gal_data_t *input, *kdtree;
+  char kdtreefile[]="kd-tree.fits";
+  char inputfile[]="coordinates.txt";
+
+  /* Processing variables of this function. */
+  char kdtreehdu[]="1";
+  double *in_x, *in_y, least_dist;
+  size_t root, nkeys=1, nearest_index;
+  gal_data_t *rkey, *keysll=gal_data_array_calloc(nkeys);
+
+  /* Read the input coordinates, see comments in example of
+   * 'gal_kdtree_create' for more. */
+  input=gal_table_read(inputfile, "1", NULL, NULL,
+                       GAL_TABLE_SEARCH_NAME, 0, -1, 0, NULL);
+
+  /* Read the k-d tree contents (created before). */
+  kdtree=gal_table_read(kdtreefile, "1", NULL, NULL,
+                        GAL_TABLE_SEARCH_NAME, 0, -1, 0, NULL);
+
+  /* Read the k-d tree root index from the header keyword.
+   * See example in description of 'gal_fits_key_read_from_ptr'.*/
+  keysll[0].name="KDTROOT";
+  keysll[0].type=GAL_TYPE_SIZE_T;
+  gal_fits_key_read(kdtreefile, kdtreehdu, keysll, 0, 0);
+  keysll[0].name=NULL; /* Since we didn't allocate it. */
+  rkey=gal_data_copy_to_new_type(&keysll[0], GAL_TYPE_SIZE_T);
+  root=((size_t *)(rkey->array))[0];
+
+  /* Find the nearest neighbour of the point. */
+  nearest_index=gal_kdtree_nearest_neighbour(input, kdtree, root,
+                                             point, &least_dist);
+
+  /* Print the results. */
+  in_x=input->array;
+  in_y=input->next->array;
+  printf("(%g, %g): nearest is (%g, %g), with a distance of %g\n",
+         point[0], point[1], in_x[nearest_index],
+         in_y[nearest_index], least_dist);
+
+  /* Clean up and return. */
+  gal_data_free(rkey);
+  gal_list_data_free(input);
+  gal_list_data_free(kdtree);
+  gal_data_array_free(keysll, nkeys, 1);
+  return EXIT_SUCCESS;
+@}
+@end example
+@end deftypefun
+
+
+
+
+
+@node Permutations, Matching, K-d tree, Gnuastro library
 @subsection Permutations (@file{permutation.h})
 @cindex permutation
 Permutation is the technical name for re-ordering of values. The need for
@@ -23247,87 +26866,78 @@ Apply the inverse of @code{permutation} on the 
@code{input} dataset (can
 have any type), see above for the definition of permutation.
 @end deftypefun
 
+
+
+
+
 @node Matching, Statistical operations, Permutations, Gnuastro library
 @subsection Matching (@file{match.h})
 
-Matching is often necessary when the measurements have been done using
-different instruments, different software or different configurations of
-the same software. The functions in this part of Gnuastro's library will be
-growing to allow matching of images and finding a match between different
-catalogs (register them). Currently it only provides the  The high-level
-measurements are stored in tables with positions (commonly in RA and Dec
-with units of degrees).
+@cindex Matching
+@cindex Coordinate matching
+Matching is often necessary when two measurements of the same points have been 
done using different instruments (or hardware), different software or different 
configurations of the same software.
+In other words, you have two catalogs or tables and each has N columns 
containing the N-dimensional ``positional'' values of each point.
+Each can have other columns too, for example one can have brightness 
measurements in one filter, and another can have brightness measurements in 
another filter as well as morphology measurements or etc.
+
+The matching functions here will use the positional columns to find the 
permutation necessary to apply to both tables.
+This will enable you to match by the positions, then apply the permutation to 
the brightness or morphology columns in the example above.
+The input and output data formats of the functions below are the some and 
described below before the actual functions.
+Each function also has extra arguments due to the particular algorithm it uses 
for the matching.
+
+The two inputs of the functions (@code{coord1} and @code{coord2}) must be 
@ref{List of gal_data_t}.
+Each @code{gal_data_t} node in @code{coord1} or @code{coord2} should be a 
single dimensional dataset (column in a table) and all the nodes must have the 
same number of elements (rows).
+In other words, each column can be visualized as having the coordinates of 
each point in its respective dimension.
+The dimensions of the coordinates is determined by the number of 
@code{gal_data_t} nodes in the two input lists (which must be equal).
+The number of rows (or the number of elements in each @code{gal_data_t}) in 
the columns of @code{coord1} and @code{coord2} can be different.
+All these functions will all be satisfied if you use @code{gal_table_read} to 
read the two coordinate columns, see @ref{Table input output}.
+
+@cindex Permutation
+The functions below return a simply-linked list of three 1D datasets (see 
@ref{List of gal_data_t}), let's call the returned dataset @code{ret}.
+The first two (@code{ret} and @code{ret->next}) are permutations.
+In other words, the @code{array} elements of both have a type of 
@code{size_t}, see @ref{Permutations}.
+The third node (@code{ret->next->next}) is the calculated distance for that 
match and its array has a type of @code{double}.
+The number of matches will be put in the space pointed by the 
@code{nummatched} argument.
+If there wasn't any match, this function will return @code{NULL}.
+
+The two permutations can be applied to the rows of the two inputs: the first 
one (@code{ret}) should be applied to the rows of the table containing 
@code{coord1} and the second one (@code{ret->next}) to the table containing 
@code{coord2}.
+After applying the returned permutations to the inputs, the top 
@code{nummatched} elements of both will match with each other.
+The ordering of the rest of the elements is undefined (depends on the matching 
funciton used).
+The third node is the distances between the respective match (which may be 
elliptical distance, see discussion of ``aperture'' below).
+
+The functions will not simply return the nearest neighbor as a match.
+The nearest neighbor may be too far to be a meaningful.
+They will check the distance between the distance of the nearest neighbor of 
each point and only return a match for it it is within an acceptable 
N-dimensional distance (or ``aperture'').
+The matching aperture is defined by the @code{aperture} array that is an input 
argument to the functions.
+If several points of one catalog lie within this aperture of a point in the 
other, the  nearest is defined as the match.
+In a 2D situation (where the input lists have two nodes), for the most generic 
case, it must have three elements: the major axis length, axis ratio and 
position angle (see @ref{Defining an ellipse and ellipsoid}).
+If @code{aperture[1]==1}, the aperture will be a circle of radius 
@code{aperture[0]} and the third value won't be used.
+When the aperture is an ellipse, distances between the points are also 
calculated in the respective elliptical distances (@mymath{r_{el}} in 
@ref{Defining an ellipse and ellipsoid}).
+
+
+
 
 @deftypefun {gal_data_t *} gal_match_coordinates (gal_data_t @code{*coord1}, 
gal_data_t @code{*coord2}, double @code{*aperture}, int @code{sorted_by_first}, 
int @code{inplace}, size_t @code{minmapsize}, int @code{quietmmap}, size_t 
@code{*nummatched})
 
-Return the permutations that when applied, the first @code{nummatched} rows
-of both inputs match with each other (are the nearest within the given
-aperture). The two inputs (@code{coord1} and @code{coord2}) must be
-@ref{List of gal_data_t}. Each @code{gal_data_t} node in the list should be
-a single dimensional dataset (column in a table). The dimensions of the
-coordinates is determined by the number of @code{gal_data_t} nodes in the
-two input lists (which must be equal). Note that the number of rows (or the
-number of elements in each @code{gal_data_t}) in the columns of
-@code{coord1} and @code{coord2} can be different.
-
-The matching aperture is defined by the @code{aperture} array. If several
-points of one catalog lie within this aperture of a point in the other, the
-nearest is defined as the match. In a 2D situation (where the input lists
-have two nodes), for the most generic case, it must have three elements:
-the major axis length, axis ratio and position angle (see @ref{Defining an
-ellipse and ellipsoid}). If @code{aperture[1]==1}, the aperture will be a
-circle of radius @code{aperture[0]} and the third value won't be used. When
-the aperture is an ellipse, distances between the points are also
-calculated in the respective elliptical distances (@mymath{r_{el}} in
-@ref{Defining an ellipse and ellipsoid}).
-
-To speed up the search, this function will sort the input coordinates by
-their first column (first axis). If @emph{both} are already sorted by their
-first column, you can avoid the sorting step by giving a non-zero value to
-@code{sorted_by_first}.
-
-When sorting is necessary and @code{inplace} is non-zero, the actual input
-columns will be sorted. Otherwise, an internal copy of the inputs will be
-made, used (sorted) and later freed before returning. Therefore, when
-@code{inplace==0}, inputs will remain untouched, but this function will
-take more time and memory.
-
-If internal allocation is necessary and the space is larger than
-@code{minmapsize}, the space will be not allocated in the RAM, but in a
-file, see description of @option{--minmapsize} and @code{--quietmmap} in
-@ref{Processing options}.
-
-The number of matches will be put in the space pointed by
-@code{nummatched}. If there wasn't any match, this function will return
-@code{NULL}. If match(s) were found, a list with three @code{gal_data_t}
-nodes will be returned. The top two nodes in the list are the permutations
-that must be applied to the first and second inputs respectively. After
-applying the permutations, the top @code{nummatched} elements will match
-with each other. The third node is the distances between the respective
-match. Note that the three nodes of the list are all one-dimensional (a
-column) and can have different lengths.
+Use a basic sort-based match to find the matching points of two input 
coordinates.
+See the descriptions above on the format of the inputs and outputs.
+To speed up the search, this function will sort the input coordinates by their 
first column (first axis).
+If @emph{both} are already sorted by their first column, you can avoid the 
sorting step by giving a non-zero value to @code{sorted_by_first}.
+
+When sorting is necessary and @code{inplace} is non-zero, the actual input 
columns will be sorted.
+Otherwise, an internal copy of the inputs will be made, used (sorted) and 
later freed before returning.
+Therefore, when @code{inplace==0}, inputs will remain untouched, but this 
function will take more time and memory.
+If internal allocation is necessary and the space is larger than 
@code{minmapsize}, the space will be not allocated in the RAM, but in a file, 
see description of @option{--minmapsize} and @code{--quietmmap} in 
@ref{Processing options}.
 
 @cartouche
 @noindent
-@strong{Output permutations ignore internal sorting}: the output
-permutations will correspond to the initial inputs. Therefore, even when
-@code{inplace!=0} (and this function re-arranges the inputs), the output
-permutation will correspond to original (possibly non-sorted) inputs.
-
-The reason for this is that you rarely want the actual positional columns
-after the match. Usually, you also have other columns (measurements, for
-example magnitudes) for higher-level processing after the match (that
-correspond to the input order before sorting). Once you have the
-permutations, they can be applied to those other columns (see
-@ref{Permutations}) and the higher-level processing can continue.
-@end cartouche
-
-When you read the coordinates from a table using @code{gal_table_read} (see
-@ref{Table input output}), and only ask for the coordinate columns, the
-inputs to this function are the returned @code{gal_data_t *} from two
-different tables.
-
+@strong{Output permutations ignore internal sorting}: the output permutations 
will correspond to the initial inputs.
+Therefore, even when @code{inplace!=0} (and this function re-arranges the 
inputs in place), the output permutation will correspond to original (possibly 
non-sorted) inputs.
 
+The reason for this is that you rarely want to permute the actual positional 
columns after the match.
+Usually, you also have other columns (for example the brightness, morphology 
and etc) and you want to find how they differ between the objects that match.
+Once you have the permutations, they can be applied to those other columns 
(see @ref{Permutations}) and the higher-level processing can continue.
+So if you don't need the coordinate columns for the rest of your analysis, it 
is better to set @code{inplace=1}.
+@end cartouche
 @end deftypefun
 
 @node Statistical operations, Binary datasets, Matching, Gnuastro library
@@ -23431,7 +27041,7 @@ Return the index of the element that has a quantile of 
@code{quantile}
 assuming the dataset has @code{size} elements.
 @end deftypefun
 
-@deftypefun size_t gal_statistics_quantile (gal_data_t @code{*input}, double 
@code{quantile}, int @code{inplace})
+@deftypefun {gal_data_t *} gal_statistics_quantile (gal_data_t @code{*input}, 
double @code{quantile}, int @code{inplace})
 Return a single-element dataset containing the value with in a quantile
 @code{quantile} of the non-blank values in @code{input}. The numerical
 datatype of the output is the same as @code{input}. See
@@ -23447,13 +27057,22 @@ function will return @code{GAL_BLANK_SIZE_T}.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_statistics_quantile_function (gal_data_t 
@code{*input}, gal_data_t @code{*value}, int @code{inplace})
-Return a single-element (@code{double} or @code{float64}) dataset
-containing the quantile function of the non-blank values in @code{input} at
-@code{value}. In other words, this function will return the quantile of
-@code{value} in @code{input}. If the value is smaller than the input's
-smallest element, the returned value will be zero. If the value is larger
-than the input's largest element, then the returned value is 1. See
-@code{gal_statistics_median} for a description of @code{inplace}.
+
+Return a single-element dataset containing the quantile function of the 
non-blank values in @code{input} at @code{value} (a single-element dataset).
+The numerical data type is of the returned dataset is @code{float64} (or 
@code{double}).
+In other words, this function will return the quantile of @code{value} in 
@code{input}.
+@code{value} has to have the same type as @code{input}.
+See @code{gal_statistics_median} for a description of @code{inplace}.
+
+When all elements are blank, the returned value will be NaN.
+If the value is smaller than the input's smallest element, the returned value 
will be negative infinity.
+If the value is larger than the input's largest element, then the returned 
value will be positive infinity
+@end deftypefun
+
+@deftypefun {gal_data_t *} gal_statistics_unique (gal_data_t @code{*input}, 
int @code{inplace})
+Return a 1D dataset with the same numeric data type as the input, but only 
containing its unique elements and without any (possible) blank/NaN elements.
+Note that the input's number of dimensions is irrelevant for this function.
+If @code{inplace} is not zero, then the unique values will over-write the 
allocated space of the input, otherwise a new space will be allocated and the 
input will not be touched.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_statistics_mode (gal_data_t @code{*input}, 
float @code{mirrordist}, int @code{inplace})
@@ -23513,10 +27132,10 @@ If you want to re-check a dataset with the 
blank-value-check flag already
 set (for example if you have made changes to it), then explicitly set the
 @code{GAL_DATA_FLAG_SORT_CH} bit to zero before calling this function. When
 there are no other flags, you can simply set the flags to zero (with
-@code{input->flags=0}), otherwise you can use this expression:
+@code{input->flag=0}), otherwise you can use this expression:
 
 @example
-input->flags &= ~GAL_DATA_FLAG_SORT_CH;
+input->flag &= ~GAL_DATA_FLAG_SORT_CH;
 @end example
 @end deftypefun
 
@@ -23538,7 +27157,7 @@ allocate a new copy of the dataset and work on that. 
Therefore if
 @code{inplace==0}, the input dataset will be modified.
 
 This function uses the bit flags of the input, so if you have modified the
-dataset, set @code{input->flags=0} before calling this function. Also note
+dataset, set @code{input->flag=0} before calling this function. Also note
 that @code{inplace} is only for the dataset elements. Therefore even when
 @code{inplace==0}, if the input is already sorted @emph{and} has no blank
 values, then the flags will be updated to show this.
@@ -23599,39 +27218,43 @@ shifted to accommodate this request.
 
 
 @deftypefun {gal_data_t *} gal_statistics_histogram (gal_data_t @code{*input}, 
gal_data_t @code{*bins}, int @code{normalize}, int @code{maxone})
-Make a histogram of all the elements in the given dataset with bin values
-that are defined in the @code{inbins} structure (see
-@code{gal_statistics_regular_bins}, they currently have to be equally
-spaced). @code{inbins} is not mandatory, if you pass a @code{NULL} pointer,
-the bins structure will be built within this function based on the
-@code{numbins} input. As a result, when you have already defined the bins,
-@code{numbins} is not used.
+@cindex Histogram
+Make a histogram of all the elements in the given dataset with bin values that 
are defined in the @code{inbins} structure (see 
@code{gal_statistics_regular_bins}, they currently have to be equally spaced).
+@code{inbins} is not mandatory, if you pass a @code{NULL} pointer, the bins 
structure will be built within this function based on the @code{numbins} input.
+As a result, when you have already defined the bins, @code{numbins} is not 
used.
 
-Let's write the center of the @mymath{i}th element of the bin array as
-@mymath{b_i}, and the fixed half-bin width as @mymath{h}. Then element
-@mymath{j} of the input array (@mymath{in_j}) will be counted in
-@mymath{b_i} if @mymath{(b_i-h) \le in_j < (b_i+h)}. However, if
-@mymath{in_j} is somewhere in the last bin, the condition changes to
-@mymath{(b_i-h) \le in_j \le (b_i+h)}.
+Let's write the center of the @mymath{i}th element of the bin array as 
@mymath{b_i}, and the fixed half-bin width as @mymath{h}.
+Then element @mymath{j} of the input array (@mymath{in_j}) will be counted in 
@mymath{b_i} if @mymath{(b_i-h) \le in_j < (b_i+h)}.
+However, if @mymath{in_j} is somewhere in the last bin, the condition changes 
to @mymath{(b_i-h) \le in_j \le (b_i+h)}.
+
+If @code{normalize!=0}, the histogram will be ``normalized'' such that the sum 
of the counts column will be one. In other words, all the counts in every bin 
will be divided by the total number of counts. If @code{maxone!=0}, the 
histogram's maximum count will be 1. In other words, the counts in every bin 
will be divided by the value of the maximum.
 @end deftypefun
 
+@deftypefun {gal_data_t *} gal_statistics_histogram2d (gal_data_t 
@code{*input}, gal_data_t @code{*bins})
+@cindex Histogram, 2D
+@cindex 2D histogram
+This function is very similar to @code{gal_statistics_histogram}, but will 
build a 2D histogram (count how many of the elements of @code{input} have a 
within a 2D box.
+The bins comprising the first dimension of the 2D box are defined by 
@code{bins}.
+The bins of the second dimension are defined by @code{bins->next} (@code{bins} 
is a @ref{List of gal_data_t}).
+Both the @code{bin} and @code{bin->next} can be created with 
@code{gal_statistics_regular_bins}.
+
+This function returns a list of @code{gal_data_t} with three nodes/columns, so 
you can directly write them into a table (see @ref{Table input output}).
+Assuming @code{bins} has @mymath{N1} bins and @code{bins->next} has 
@mymath{N2} bins, each node/column of the returned output is a 1D array with 
@mymath{N1\times N2} elements.
+The first and second columns are the center of the 2D bin along the first and 
second dimensions and have a @code{double} data type.
+The third column is the 2D histogram (the number of input elements that have a 
value within that 2D bin) and has a @code{uint32} data type (see @ref{Numeric 
data types}).
+@end deftypefun
 
 @deftypefun {gal_data_t *} gal_statistics_cfp (gal_data_t @code{*input}, 
gal_data_t @code{*bins}, int @code{normalize})
 Make a cumulative frequency plot (CFP) of all the elements in @code{input}
 with bin values that are defined in the @code{bins} structure (see
 @code{gal_statistics_regular_bins}).
 
-The CFP is built from the histogram: in each bin, the value is the sum of
-all previous bins in the histogram. Thus, if you have already calculated
-the histogram before calling this function, you can pass it onto this
-function as the data structure in @code{bins->next} (see @code{List of
-gal_data_t}). If @code{bin->next!=NULL}, then it is assumed to be the
-histogram. If it is @code{NULL}, then the histogram will be calculated
-internally and freed after the job is finished.
+The CFP is built from the histogram: in each bin, the value is the sum of all 
previous bins in the histogram.
+Thus, if you have already calculated the histogram before calling this 
function, you can pass it onto this function as the data structure in 
@code{bins->next} (see @code{List of gal_data_t}).
+If @code{bin->next!=NULL}, then it is assumed to be the histogram.
+If it is @code{NULL}, then the histogram will be calculated internally and 
freed after the job is finished.
 
-When a histogram is given and it is normalized, the CFP will also be
-normalized (even if the normalized flag is not set here): note that a
-normalized CFP's maximum value is 1.
+When a histogram is given and it is normalized, the CFP will also be 
normalized (even if the normalized flag is not set here): note that a 
normalized CFP's maximum value is 1.
 @end deftypefun
 
 
@@ -23664,52 +27287,38 @@ above.
 @end deftypefun
 
 
-@deftypefun {gal_data_t *} gal_statistics_outlier_positive (gal_data_t 
@code{*input}, size_t @code{window_size}, float @code{sigma}, float 
@code{sigclip_multip}, float @code{sigclip_param}, int @code{inplace}, int 
@code{quiet})
-Find the first positive outlier in the @code{input} distribution. The
-returned dataset contains a single element: the first positive outlier. It
-is one of the dataset's elements, in the same type as the input. If the
-process fails for any reason (for example no outlier was found), a
-@code{NULL} pointer will be returned.
+@deftypefun {gal_data_t *} gal_statistics_outlier_bydistance (int 
@code{pos1_neg0}, gal_data_t @code{*input}, size_t @code{window_size}, float 
@code{sigma}, float @code{sigclip_multip}, float @code{sigclip_param}, int 
@code{inplace}, int @code{quiet})
 
-All (possibly existing) blank elements are first removed from the input
-dataset, then it is sorted. A sliding window of @code{window_size} elements
-is parsed over the dataset. Starting from the @code{window_size}-th element
-of the dataset, in the direction of increasing values. This window is used
-as a reference. The first element where the distance to the previous
-(sorted) element is @code{sigma} units away from the distribution of
-distances in its window is considered an outlier and returned by this
-function.
+Find the first positive outlier (if @code{pos1_neg0!=0}) in the @code{input} 
distribution.
+When @code{pos1_neg0==0}, the same algorithm goes to the start of the dataset.
+The returned dataset contains a single element: the first positive outlier.
+It is one of the dataset's elements, in the same type as the input.
+If the process fails for any reason (for example no outlier was found), a 
@code{NULL} pointer will be returned.
 
-Formally, if we assume there are @mymath{N} non-blank elements. They are
-first sorted. Searching for the outlier starts on element @mymath{W}. Let's
-take @mymath{v_i} to be the @mymath{i}-th element of the sorted input (with
-no blank values) and @mymath{m} and @mymath{\sigma} as the
-@mymath{\sigma}-clipped median and standard deviation from the distances of
-the previous @mymath{W} elements (not including @mymath{v_i}). If the value
-given to @code{sigma} is displayed with @mymath{s}, the @mymath{i}-th
-element is considered as an outlier when the condition below is true.
+All (possibly existing) blank elements are first removed from the input 
dataset, then it is sorted.
+A sliding window of @code{window_size} elements is parsed over the dataset.
+Starting from the @code{window_size}-th element of the dataset, in the 
direction of increasing values.
+This window is used as a reference.
+The first element where the distance to the previous (sorted) element is 
@code{sigma} units away from the distribution of distances in its window is 
considered an outlier and returned by this function.
+
+Formally, if we assume there are @mymath{N} non-blank elements.
+They are first sorted.
+Searching for the outlier starts on element @mymath{W}.
+Let's take @mymath{v_i} to be the @mymath{i}-th element of the sorted input 
(with no blank values) and @mymath{m} and @mymath{\sigma} as the 
@mymath{\sigma}-clipped median and standard deviation from the distances of the 
previous @mymath{W} elements (not including @mymath{v_i}).
+If the value given to @code{sigma} is displayed with @mymath{s}, the 
@mymath{i}-th element is considered as an outlier when the condition below is 
true.
 
 @dispmath{{(v_i-v_{i-1})-m\over \sigma}>s}
 
-The @code{sigclip_multip} and @code{sigclip_param} arguments specify the
-properties of the @mymath{\sigma}-clipping (see @ref{Sigma clipping} for
-more). You see that by this definition, the outlier cannot be any of the
-lower half elements. The advantage of this algorithm compared to
-@mymath{\sigma}-clippign is that it only looks backwards (in the sorted
-array) and parses it in one direction.
+The @code{sigclip_multip} and @code{sigclip_param} arguments specify the 
properties of the @mymath{\sigma}-clipping (see @ref{Sigma clipping} for more).
+You see that by this definition, the outlier cannot be any of the lower half 
elements.
+The advantage of this algorithm compared to @mymath{\sigma}-clippign is that 
it only looks backwards (in the sorted array) and parses it in one direction.
 
-If @code{inplace!=0}, the removing of blank elements and sorting will be
-done within the input dataset's allocated space. Otherwise, this function
-will internally allocate (and later free) the necessary space to keep the
-intermediate space that this process requires.
+If @code{inplace!=0}, the removing of blank elements and sorting will be done 
within the input dataset's allocated space.
+Otherwise, this function will internally allocate (and later free) the 
necessary space to keep the intermediate space that this process requires.
 
-If @code{quiet!=0}, this function will report the parameters every time it
-moves the window as a separate line with several columns. The first column
-is the value, the second (in square brackets) is the sorted index, the
-third is the distance of this element from the previous one. The Fourth and
-fifth (in parenthesis) are the median and standard deviation of the
-@mymath{\sigma}-clipped distribution within the window and the last column
-is the difference between the third and fourth, divided by the fifth.
+If @code{quiet!=0}, this function will report the parameters every time it 
moves the window as a separate line with several columns.
+The first column is the value, the second (in square brackets) is the sorted 
index, the third is the distance of this element from the previous one.
+The Fourth and fifth (in parenthesis) are the median and standard deviation of 
the @mymath{\sigma}-clipped distribution within the window and the last column 
is the difference between the third and fourth, divided by the fifth.
 
 @end deftypefun
 
@@ -23906,22 +27515,16 @@ Note that the indexs will only be calculated on the 
pixels with a value of 1 and
 @deftypefun {gal_data_t *} gal_binary_connected_adjacency_matrix (gal_data_t 
@code{*adjacency}, size_t @code{*numconnected})
 @cindex Adjacency matrix
 @cindex Matrix, adjacency
-Find the number of connected labels and new labels based on an adjacency
-matrix, which must be a square binary array (type
-@code{GAL_TYPE_UINT8}). The returned dataset is a list of new labels for
-each old label. In other words, this function will find the objects that
-are connected (possibly through a third object) and in the output array,
-the respective elements for all input labels is going to have the same
-value. The total number of connected labels is put into the space that
-@code{numconnected} points to.
-
-An adjacency matrix defines connection between two labels. For example,
-let's assume we have 5 labels and we know that labels 1 and 5 are connected
-to label 3, but are not connected with each other. Also, labels 2 and 4 are
-not touching any other label. So in total we have 3 final labels: one
-combined object (merged from labels 1, 3, and 5) and the initial labels 2
-and 4. The input adjacency matrix would look like this (note the extra row
-and column for a label 0 which is ignored):
+Find the number of connected labels and new labels based on an adjacency 
matrix, which must be a square binary array (type @code{GAL_TYPE_UINT8}).
+The returned dataset is a list of new labels for each old label.
+In other words, this function will find the objects that are connected 
(possibly through a third object) and in the output array, the respective 
elements for all input labels is going to have the same value.
+The total number of connected labels is put into the space that 
@code{numconnected} points to.
+
+An adjacency matrix defines connection between two labels.
+For example, let's assume we have 5 labels and we know that labels 1 and 5 are 
connected to label 3, but are not connected with each other.
+Also, labels 2 and 4 are not touching any other label.
+So in total we have 3 final labels: one combined object (merged from labels 1, 
3, and 5) and the initial labels 2 and 4.
+The input adjacency matrix would look like this (note the extra row and column 
for a label 0 which is ignored):
 
 @example
             INPUT                             OUTPUT
@@ -23936,8 +27539,36 @@ in_lab 4 -->   0  0  0  0  0  0   |   | 0 | 1 | 2 | 1 
| 3 | 1 |
 in_lab 5 -->   0  0  0  1  0  0   |
 @end example
 
-Although the adjacency matrix as used here is symmetric, currently this
-function assumes that it is filled on both sides of the diagonal.
+Although the adjacency matrix as used here is symmetric, currently this 
function assumes that it is filled on both sides of the diagonal.
+@end deftypefun
+
+@deftypefun {gal_data_t *} gal_binary_connected_adjacency_list 
(gal_list_sizet_t @code{**listarr}, size_t @code{number}, size_t 
@code{minmapsize}, int @code{quietmmap}, size_t @code{*numconnected})
+@cindex RAM
+Find the number of connected labels and new labels based on an adjacency list.
+The output of this function is identical to that of 
@code{gal_binary_connected_adjacency_matrix}.
+But the major difference is that it uses a list of connected labels to each 
label instead of a square adjacency matrix.
+This is done because when the number of labels becomes very large (for example 
on the scale of 100,000), the adjacency matrix can consume more than 10GB of 
RAM!
+
+The input list has the following format: it is an array of pointers to 
@code{gal_list_sizet_t *} (or @code{gal_list_sizet_t **}).
+The array has @code{number} elements and each @code{listarr[i]} is a linked 
list of @code{gal_list_sizet_t *}.
+As a demonstration, the input of the same example in 
@code{gal_binary_connected_adjacency_matrix} would look like below and the 
output of this function will be identical to there.
+
+@example
+listarr[0] = NULL
+listarr[1] = 3
+listarr[2] = NULL
+listarr[3] = 1 -> 5
+listarr[4] = NULL
+listarr[5] = 3
+@end example
+
+From this example, it is already clear that this method will consume far less 
memory.
+But because it needs to parse lists (and not easily jump between array 
elements), it can be slower.
+But in scenarios where there are too many objects (that may exceed the whole 
system's RAM+SWAP), this option is a good alternative and the drop in 
processing speed is worth getting the job done.
+
+Similar to @code{gal_binary_connected_adjacency_matrix}, this function will 
write the final number of connected labels in @code{numconnected}.
+But since it takes no @code{gal_data_t *} argument (where it can inherit the 
@code{minmapsize} and @code{quietmmap} parameters), it also needs these as 
input.
+For more on @code{minmapsize} and @code{quietmmap}, see @ref{Memory 
management}.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_binary_holes_label (gal_data_t @code{*input}, 
int @code{connectivity}, size_t @code{*numholes})
@@ -24103,8 +27734,8 @@ your software), to avoid an extra parsing of the 
dataset and improve
 performance, you can set the two bits manually (see the description of
 @code{flags} in @ref{Generic data container}):
 @example
-input->flags |=  GAL_DATA_FLAG_BLANK_CH; /* Set bit to 1. */
-input->flags &= ~GAL_DATA_FLAG_HASBLANK; /* Set bit to 0. */
+input->flag |=  GAL_DATA_FLAG_BLANK_CH; /* Set bit to 1. */
+input->flag &= ~GAL_DATA_FLAG_HASBLANK; /* Set bit to 0. */
 @end example
 @end deftypefun
 
@@ -24158,77 +27789,46 @@ labels of each measurement in @code{sig} will be 
written in
 @code{sigind->array}. If @code{keepsmall} zero, small clumps (where no
 measurement is made) will not be included in the output table.
 
-This function is initially intended for a multi-threaded environment. In
-such cases, you will be writing arrays of clump measures from different
-regions in parallel into an array of @code{gal_data_t}s. You can simply
-allocate (and initialize), such an array with the
-@code{gal_data_array_calloc} function in @ref{Arrays of datasets}. For
-example if the @code{gal_data_t} array is called @code{array}, you can pass
-@code{&array[i]} as @code{sig}.
+This function is initially intended for a multi-threaded environment.
+In such cases, you will be writing arrays of clump measures from different 
regions in parallel into an array of @code{gal_data_t}s.
+You can simply allocate (and initialize), such an array with the 
@code{gal_data_array_calloc} function in @ref{Arrays of datasets}.
+For example if the @code{gal_data_t} array is called @code{array}, you can 
pass @code{&array[i]} as @code{sig}.
 
-Along with some other functions in @code{label.h}, this function was
-initially written for @ref{Segment}. The description of the parameter used
-to measure a clump's significance is fully given in @ref{Segment changes
-after publication}.
+Along with some other functions in @code{label.h}, this function was initially 
written for @ref{Segment}.
+The description of the parameter used to measure a clump's significance is 
fully given in @url{https://arxiv.org/abs/1909.11230, Akhlaghi [2019]}.
 
 @end deftypefun
 
 @deftypefun void gal_label_grow_indexs (gal_data_t @code{*labels}, gal_data_t 
@code{*indexs}, int @code{withrivers}, int @code{connectivity})
-Grow the (positive) labels of @code{labels} over the pixels in
-@code{indexs} (see description of @code{gal_label_indexs}). The pixels
-(position in @code{indexs}, values in @code{labels}) that must be ``grown''
-must have a value of @code{GAL_LABEL_INIT} in @code{labels} before calling
-this function. For a demonstration see Columns 2 and 3 of Figure 10 in
-@url{http://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]}.
-
-In many aspects, this function is very similar to over-segmentation
-(watershed algorithm, @code{gal_label_watershed}). The big difference is
-that in over-segmentation local maximums (that aren't touching any already
-labeled pixel) get a separate label. However, here the final number of
-labels will not change. All pixels that aren't directly touching a labeled
-pixel just get pushed back to the start of the loop, and the loop iterates
-until its size doesn't change any more. This is because in a generic
-scenario some of the indexed pixels might not be reachable through other
-indexed pixels.
-
-The next major difference with over-segmentation is that when there is only
-one label in growth region(s), it is not mandatory for @code{indexs} to be
-sorted by values. If there are multiple labeled regions in growth
-region(s), then values are important and you can use @code{qsort} with
-@code{gal_qsort_index_single_d} to sort the indexs by values in a separate
-array (see @ref{Qsort functions}).
-
-This function looks for positive-valued neighbors of each pixel in
-@code{indexs} and will label a pixel if it touches one. Therefore, it is
-very important that only pixels/labels that are intended for growth have
-positive values in @code{labels} before calling this function. Any
-non-positive (zero or negative) value will be ignored as a label by this
-function. Thus, it is recommended that while filling in the `indexs' array
-values, you initialize all the pixels that are in @code{indexs} with
-@code{GAL_LABEL_INIT}, and set non-labeled pixels that you don't want to
-grow to @code{0}.
-
-This function will write into both the input datasets. After this function,
-some of the non-positive @code{labels} pixels will have a new positive
-label and the number of useful elements in @code{indexs} will have
-decreased. The index of those pixels that couldn't be labeled will remain
-inside @code{indexs}. If @code{withrivers} is non-zero, then pixels that
-are immediately touching more than one positive value will be given a
-@code{GAL_LABEL_RIVER} label.
+Grow the (positive) labels of @code{labels} over the pixels in @code{indexs} 
(see description of @code{gal_label_indexs}).
+The pixels (position in @code{indexs}, values in @code{labels}) that must be 
``grown'' must have a value of @code{GAL_LABEL_INIT} in @code{labels} before 
calling this function.
+For a demonstration see Columns 2 and 3 of Figure 10 in 
@url{http://arxiv.org/abs/1505.01664, Akhlaghi and Ichikawa [2015]}.
+
+In many aspects, this function is very similar to over-segmentation (watershed 
algorithm, @code{gal_label_watershed}).
+The big difference is that in over-segmentation local maximums (that aren't 
touching any alreadylabeled pixel) get a separate label.
+However, here the final number of labels will not change.
+All pixels that aren't directly touching a labeled pixel just get pushed back 
to the start of the loop, and the loop iterates until its size doesn't change 
any more.
+This is because in a generic scenario some of the indexed pixels might not be 
reachable through other indexed pixels.
+
+The next major difference with over-segmentation is that when there is only 
one label in growth region(s), it is not mandatory for @code{indexs} to be 
sorted by values.
+If there are multiple labeled regions in growth region(s), then values are 
important and you can use @code{qsort} with @code{gal_qsort_index_single_d} to 
sort the indexs by values in a separate array (see @ref{Qsort functions}).
+
+This function looks for positive-valued neighbors of each pixel in 
@code{indexs} and will label a pixel if it touches one.
+Therefore, it is very important that only pixels/labels that are intended for 
growth have positive values in @code{labels} before calling this function.
+Any non-positive (zero or negative) value will be ignored as a label by this 
function.
+Thus, it is recommended that while filling in the `indexs' array values, you 
initialize all the pixels that are in @code{indexs} with @code{GAL_LABEL_INIT}, 
and set non-labeled pixels that you don't want to grow to @code{0}.
+
+This function will write into both the input datasets.
+After this function, some of the non-positive @code{labels} pixels will have a 
new positivelabel and the number of useful elements in @code{indexs} will have 
decreased.
+The index of those pixels that couldn't be labeled will remain inside 
@code{indexs}.
+If @code{withrivers} is non-zero, then pixels that are immediately touching 
more than one positive value will be given a @code{GAL_LABEL_RIVER} label.
 
 @cindex GNU C library
-Note that the @code{indexs->array} is not re-allocated to its new size at
-the end@footnote{Note that according to the GNU C Library, even a
-@code{realloc} to a smaller size can also cause a re-write of the whole
-array, which is not a cheap operation.}. But since @code{indexs->dsize[0]}
-and @code{indexs->size} have new values after this function is returned,
-the extra elements just won't be used until they are ultimately freed by
-@code{gal_data_free}.
+Note that the @code{indexs->array} is not re-allocated to its new size at the 
end@footnote{Note that according to the GNU C Library, even a @code{realloc} to 
a smaller size can also cause a re-write of the whole array, which is not a 
cheap operation.}.
+But since @code{indexs->dsize[0]} and @code{indexs->size} have new values 
after this function is returned, the extra elements just won't be used until 
they are ultimately freed by @code{gal_data_free}.
 
-Connectivity is a value between @code{1} (fewest number of neighbors) and
-the number of dimensions in the input (most number of neighbors). For
-example in a 2D dataset, a connectivity of @code{1} and @code{2}
-corresponds to 4-connected and 8-connected neighbors.
+Connectivity is a value between @code{1} (fewest number of neighbors) and the 
number of dimensions in the input (most number of neighbors).
+For example in a 2D dataset, a connectivity of @code{1} and @code{2} 
corresponds to 4-connected and 8-connected neighbors.
 @end deftypefun
 
 
@@ -24291,54 +27891,59 @@ is much faster.
 
 @cindex Sky line
 @cindex Interpolation
-During data analysis, it happens that parts of the data cannot be given a
-value, but one is necessary for the higher-level analysis. For example a
-very bright star saturated part of your image and you need to fill in the
-saturated pixels with some values. Another common usage case are masked
-sky-lines in 1D spectra that similarly need to be assigned a value for
-higher-level analysis. In other situations, you might want a value in an
-arbitrary point: between the elements/pixels where you have data. The
-functions described in this section are for such operations.
+During data analysis, it happens that parts of the data cannot be given a 
value, but one is necessary for the higher-level analysis.
+For example a very bright star saturated part of your image and you need to 
fill in the saturated pixels with some values.
+Another common usage case are masked sky-lines in 1D spectra that similarly 
need to be assigned a value for higher-level analysis.
+In other situations, you might want a value in an arbitrary point: between the 
elements/pixels where you have data.
+The functions described in this section are for such operations.
 
 @cindex GNU Scientific Library
-The parametric interpolations discussed below are wrappers around the
-interpolation functions of the GNU Scientific Library (or GSL, see @ref{GNU
-Scientific Library}). To identify the different GSL interpolation types,
-Gnuastro's @file{gnuastro/interpolate.h} header file contains macros that
-are discussed below. The GSL wrappers provided here are not yet complete
-because we are too busy. If you need them, please consider helping us in
-adding them to Gnuastro's library. Your would be very welcome and
-appreciated.
-
-@deftypefun {gal_data_t *} gal_interpolate_close_neighbors (gal_data_t 
@code{*input}, struct gal_tile_two_layer_params @code{*tl}, size_t 
@code{numneighbors}, size_t @code{numthreads}, int @code{onlyblank}, int 
@code{aslinkedlist})
-Interpolate the values in the image using the median value of their
-@code{numneighbors} closest neighbors. This function is non-parametric and
-thus agnostic to the input's number of dimension. If @code{onlyblank} is
-non-zero, then only blank elements will be interpolated and pixels that
-already have a value will be left untouched. This function is
-multi-threaded and will run on @code{numthreads} threads (see
-@code{gal_threads_number} in @ref{Multithreaded programming}).
-
-@code{tl} is Gnuastro's two later tessellation structure used to define
-tiles over an image and is fully described in @ref{Tile grid}. When
-@code{tl!=NULL}, then it is assumed that the @code{input->array} contains
-one value per tile and interpolation will respect certain tessellation
-properties, for example to not interpolate over channel borders.
-
-If several datasets have the same set of blank values, you don't need to
-call this function multiple times. When @code{aslinkedlist} is non-zero,
-then @code{input} will be seen as a @ref{List of gal_data_t}. In this case,
-the same neighbors will be used for all the datasets in the list. Of
-course, the values for each dataset will be different, so a different value
-will be written in the each dataset, but the neighbor checking that is the
-most CPU intensive part will only be done once.
-
-This is a non-parametric and robust function for interpolation. The
-interpolated values are also always within the range of the non-blank
-values and strong outliers do not get created. However, this type of
-interpolation must be used with care when there are gradients. This is
-because it is non-parametric and if there aren't enough neighbors,
-step-like features can be created.
+The parametric interpolations discussed below are wrappers around the 
interpolation functions of the GNU Scientific Library (or GSL, see @ref{GNU 
Scientific Library}).
+To identify the different GSL interpolation types, Gnuastro's 
@file{gnuastro/interpolate.h} header file contains macros that are discussed 
below.
+The GSL wrappers provided here are not yet complete because we are too busy.
+If you need them, please consider helping us in adding them to Gnuastro's 
library.
+Your would be very welcome and appreciated.
+
+@deffn Macro GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL
+@deffnx Macro GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN
+@deffnx Macro GAL_INTERPOLATE_NEIGHBORS_METRIC_INVALID
+The metric used to find distance for nearest neighbor interpolation.
+A radial metric uses the simple Euclidean function to find the distance 
between two pixels.
+A manhattan metric will always be an integer and is like steps (but is also 
much faster to calculate than radial metric because it doesn't need a square 
root calculation).
+@end deffn
+
+@deffn Macro GAL_INTERPOLATE_NEIGHBORS_FUNC_MIN
+@deffnx Macro GAL_INTERPOLATE_NEIGHBORS_FUNC_MAX
+@deffnx Macro GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN
+@deffnx Macro GAL_INTERPOLATE_NEIGHBORS_FUNC_INVALID
+@cindex Saturated stars
+The various types of nearest-neighbor interpolation functions for 
@code{gal_interpolate_neighbors}.
+The names are descriptive for the operation they do, so we won't go into much 
more detail here.
+The median operator will be one of the most used, but operators like the 
maximum are good to fill the center of saturated stars.
+@end deffn
+
+@deftypefun {gal_data_t *} gal_interpolate_neighbors (gal_data_t 
@code{*input}, struct gal_tile_two_layer_params @code{*tl}, uint8_t 
@code{metric}, size_t @code{numneighbors}, size_t @code{numthreads}, int 
@code{onlyblank}, int @code{aslinkedlist}, int @code{function})
+
+Interpolate the values in the input dataset using a calculated statistics from 
the distribution of their @code{numneighbors} closest neighbors.
+The desired statistics is determined from the @code{func} argument, which 
takes any of the @code{GAL_INTERPOLATE_NEIGHBORS_FUNC_} macros (see above).
+This function is non-parametric and thus agnostic to the input's number of 
dimension or shape of the distribution.
+
+Distance can be defined on different metrics that are identified through 
@code{metric} (taking values determined by the 
@code{GAL_INTERPOLATE_NEIGHBORS_METRIC_} macros described above).
+If @code{onlyblank} is non-zero, then only blank elements will be interpolated 
and pixels that already have a value will be left untouched.
+This function is multi-threaded and will run on @code{numthreads} threads (see 
@code{gal_threads_number} in @ref{Multithreaded programming}).
+
+@code{tl} is Gnuastro's tessellation structure used to define tiles over an 
image and is fully described in @ref{Tile grid}.
+When @code{tl!=NULL}, then it is assumed that the @code{input->array} contains 
one value per tile and interpolation will respect certain tessellation 
properties, for example to not interpolate over channel borders.
+
+If several datasets have the same set of blank values, you don't need to call 
this function multiple times.
+When @code{aslinkedlist} is non-zero, then @code{input} will be seen as a 
@ref{List of gal_data_t}.
+In this case, the same neighbors will be used for all the datasets in the list.
+Of course, the values for each dataset will be different, so a different value 
will be written in the each dataset, but the neighbor checking that is the most 
CPU intensive part will only be done once.
+
+This is a non-parametric and robust function for interpolation.
+The interpolated values are also always within the range of the non-blank 
values and strong outliers do not get created.
+However, this type of interpolation must be used with care when there are 
gradients.
+This is because it is non-parametric and if there aren't enough neighbors, 
step-like features can be created.
 @end deftypefun
 
 @deffn Macro GAL_INTERPOLATE_1D_INVALID
@@ -24501,7 +28106,7 @@ blank. To see if any blank (non-interpolated) elements 
remain, you can use
 @end deftypefun
 
 
-@node Git wrappers, Spectral lines library, Interpolation, Gnuastro library
+@node Git wrappers, Unit conversion library (@file{units.h}), Interpolation, 
Gnuastro library
 @subsection Git wrappers (@file{git.h})
 
 @cindex Git
@@ -24537,12 +28142,48 @@ not installed or the program calling this function is 
not within a version
 controlled directory, then the output will be the @code{NULL} pointer.
 @end deftypefun
 
-@node Spectral lines library, Cosmology library, Git wrappers, Gnuastro library
+@node Unit conversion library (@file{units.h}), Spectral lines library, Git 
wrappers, Gnuastro library
+@subsection Unit conversion library (@file{units.h})
+
+Datasets can contain values in various formats or units.
+The functions in this section are defined to facilitate the easy conversion 
between them and are declared in @file{units.h}.
+If there are certain conversions that are useful for your work, please get in 
touch.
+
+@deftypefun int gal_units_extract_decimal (char @code{*convert}, const char 
@code{*delimiter}, double @code{*args}, size_t @code{n})
+Parse the input @code{convert} string with a certain delimiter (for example 
@code{01:23:45}, where the delimiter is @code{":"}) as multiple numbers (for 
example 1,23,45) and write them as an array in the space that @code{args} is 
pointing to.
+The expected number of values in the string is specified by the @code{n} 
argument (3 in the example above).
+
+If the function succeeds, it will return 1, otherwise it will return 0 and the 
values may not be fully written into @code{args}.
+If the number of values parsed in the string is different from @code{n}, this 
function will fail.
+@end deftypefun
+
+@deftypefun double gal_units_ra_to_degree (char @code{*convert})
+@cindex Right Ascension
+Convert the input Right Ascension (RA) string (in the format of hours, minutes 
and seconds either as @code{_h_m_s} or @code{_:_:_}) to degrees (a single 
floating point number).
+@end deftypefun
+
+@deftypefun double gal_units_dec_to_degree (char @code{*convert})
+@cindex Declination
+Convert the input Declination (Dec) string (in the format of degrees, 
arc-minutes and arc-seconds either as @code{_d_m_s} or @code{_:_:_}) to degrees 
(a single floating point number).
+@end deftypefun
+
+@deftypefun {char *} gal_units_degree_to_ra (double @code{decimal}, int 
@code{usecolon})
+@cindex Right Ascension
+Convert the input Right Ascension (RA) degree (a single floating point number) 
to old/standard notation (in the format of hours, minutes and seconds of 
@code{_h_m_s}).
+If @code{usecolon!=0}, then the delimiters between the components will be 
colons: @code{_:_:_}.
+@end deftypefun
+
+@deftypefun {char *} gal_units_degree_to_dec (double @code{decimal}, int 
@code{usecolon})
+@cindex Declination
+Convert the input Declination (Dec) degree (a single floating point number) to 
old/standard notation (in the format of degrees, arc-minutes and arc-seconds of 
@code{_d_m_s}).
+If @code{usecolon!=0}, then the delimiters between the components will be 
colons: @code{_:_:_}.
+@end deftypefun
+
+@node Spectral lines library, Cosmology library, Unit conversion library 
(@file{units.h}), Gnuastro library
 @subsection Spectral lines library (@file{speclines.h})
 
-Gnuastro's library has the following macros and functions for dealing with
-spectral lines. All these functions are declared in
-@file{gnuastro/spectra.h}.
+Gnuastro's library has the following macros and functions for dealing with 
spectral lines.
+All these functions are declared in @file{gnuastro/spectra.h}.
 
 @cindex H-alpha
 @cindex H-beta
@@ -24594,8 +28235,17 @@ spectral lines. All these functions are declared in
 @deffnx Macro GAL_SPECLINES_HEIIBLUE
 @deffnx Macro GAL_SPECLINES_LYALPHA
 @deffnx Macro GAL_SPECLINES_LYLIMIT
+@deffnx Macro GAL_SPECLINES_INVALID_MAX
 Internal values/identifiers for specific spectral lines as is clear from
 their names.
+Note the first and last one, they can be used when parsing the lines 
automatically: both don't correspond to any line, but their integer values 
correspond to the two integers just before and after the first and last line 
identifier.
+
+@code{GAL_SPECLINES_INVALID} has a value of zero, and allows you to have a 
fixed integer which never corresponds to a line.
+@code{GAL_SPECLINES_INVALID_MAX} is the total number of pre-defined lines, 
plus one.
+So you can parse all the known lines with a @code{for} loop like this:
+@example
+for(i=1;i<GAL_SPECLINES_INVALID_MAX;++i)
+@end example
 @end deffn
 
 @deffn  Macro GAL_SPECLINES_ANGSTROM_SIIRED
@@ -24749,6 +28399,13 @@ redshift @code{z}. This value has to be added to the 
apparent magnitude to
 give the absolute magnitude of an object at redshift @code{z}.
 @end deftypefun
 
+@deftypefun double gal_cosmology_velocity_from_z (double @code{z})
+Return the velocity (in km/s) corresponding to the given redshift (@code{z}).
+@end deftypefun
+
+@deftypefun double gal_cosmology_z_from_velocity (double @code{v})
+Return the redshift corresponding to the given velocity (@code{v} in km/s).
+@end deftypefun
 
 @node Library demo programs,  , Gnuastro library, Library
 @section Library demo programs
@@ -24882,47 +28539,29 @@ main(void)
 @node Library demo - multi-threaded operation, Library demo - reading and 
writing table columns, Library demo - inspecting neighbors, Library demo 
programs
 @subsection Library demo - multi-threaded operation
 
-The following simple program shows how to use Gnuastro to simplify spinning
-off threads and distributing different jobs between the threads. The
-relevant thread-related functions are defined in @ref{Gnuastro's thread
-related functions}. For easy linking/compilation of this program, along
-with a first run, see Gnuastro's @ref{BuildProgram}. Before running, also
-change the @code{filename} and @code{hdu} variable values to specify an
-existing FITS file and/or extension/HDU.
-
-This is a very simple program to open a FITS image, distribute its pixels
-between different threads and print the value of each pixel and the thread
-it was assigned to. The actual operation is very simple (and would not
-usually be done with threads in a real-life program). It is intentionally
-chosen to put more focus on the important steps in spinning of threads and
-how the worker function (which is called by each thread) can identify the
-job-IDs it should work on.
-
-For example, instead of an array of pixels, you can define an array of
-tiles or any other context-specific structures as separate targets. The
-important thing is that each action should have its own unique ID (counting
-from zero, as is done in an array in C). You can then follow the process
-below and use each thread to work on all the targets that are assigned to
-it. Recall that spinning-off threads is its self an expensive process and
-we don't want to spin-off one thread for each target (see the description
-of @code{gal_threads_dist_in_threads} in @ref{Gnuastro's thread related
-functions}.
-
-There are many (more complicated, real-world) examples of using
-@code{gal_threads_spin_off} in Gnuastro's actual source code, you can see
-them by searching for the @code{gal_threads_spin_off} function from the top
-source (after unpacking the tarball) directory (for example with this
-command):
+The following simple program shows how to use Gnuastro to simplify spinning 
off threads and distributing different jobs between the threads.
+The relevant thread-related functions are defined in @ref{Gnuastro's thread 
related functions}.
+For easy linking/compilation of this program, along with a first run, see 
Gnuastro's @ref{BuildProgram}.
+Before running, also change the @code{filename} and @code{hdu} variable values 
to specify an existing FITS file and/or extension/HDU.
+
+This is a very simple program to open a FITS image, distribute its pixels 
between different threads and print the value of each pixel and the thread it 
was assigned to.
+The actual operation is very simple (and would not usually be done with 
threads in a real-life program).
+It is intentionally chosen to put more focus on the important steps in 
spinning of threads and how the worker function (which is called by each 
thread) can identify the job-IDs it should work on.
+
+For example, instead of an array of pixels, you can define an array of tiles 
or any other context-specific structures as separate targets.
+The important thing is that each action should have its own unique ID 
(counting from zero, as is done in an array in C).
+You can then follow the process below and use each thread to work on all the 
targets that are assigned to it.
+Recall that spinning-off threads is its self an expensive process and we don't 
want to spin-off one thread for each target (see the description of 
@code{gal_threads_dist_in_threads} in @ref{Gnuastro's thread related functions}.
+
+There are many (more complicated, real-world) examples of using 
@code{gal_threads_spin_off} in Gnuastro's actual source code, you can see them 
by searching for the @code{gal_threads_spin_off} function from the top source 
(after unpacking the tarball) directory (for example with this command):
 
 @example
 $ grep -r gal_threads_spin_off ./
 @end example
 
 @noindent
-The code of this demonstration program is shown below. This program was
-also built and run when you ran @code{make check} during the building of
-Gnuastro (@code{tests/lib/multithread.c}, so it is already tested for your
-system and you can safely use it as a guide.
+The code of this demonstration program is shown below.
+This program was also built and run when you ran @code{make check} during the 
building of Gnuastro (@code{tests/lib/multithread.c}), so it is already tested 
for your system and you can safely use it as a guide.
 
 @example
 #include <stdio.h>
@@ -24990,12 +28629,16 @@ main(void)
   char *filename="input.fits", *hdu="1";
   size_t numthreads=gal_threads_number();
 
+  /* We are using * `-1' for `minmapsize' to ensure that the image is
+     read into * memory and `1' for `quietmmap' (which can also be
+     zero), see the "Memory management" section in the book. */
+  int quietmmap=1;
+  size_t minmapsize=-1;
+
 
-  /* Read the image into memory as a float32 data type. We are using
-   * `-1' for `minmapsize' to ensure that the image is read into
-   * memory and `1' for `quietmmap' (which can also be zero). */
+  /* Read the image into memory as a float32 data type. */
   p.image=gal_fits_img_read_to_type(filename, hdu, GAL_TYPE_FLOAT32,
-                                    -1, 1);
+                                    minmapsize, quietmmap);
 
 
   /* Print some basic information before the actual contents: */
@@ -25015,7 +28658,8 @@ main(void)
 
 
   /* Spin-off the threads and do the processing on each thread. */
-  gal_threads_spin_off(worker_on_thread, &p, p.image->size, numthreads);
+  gal_threads_spin_off(worker_on_thread, &p, p.image->size, numthreads,
+                       minmapsize, quietmmap);
 
 
   /* Clean up and return. */
@@ -25144,7 +28788,8 @@ main(void)
   /* Set names for the columns and write them out. */
   c1->name = "COUNTER";
   c2->name = "VALUE";
-  gal_table_write(c1, NULL, GAL_TABLE_FORMAT_BFITS, outname);
+  gal_table_write(c1, NULL, NULL, GAL_TABLE_FORMAT_BFITS, outname,
+                  "MY-COLUMNS", 0);
 
   /* The names weren't allocated, so to avoid cleaning-up problems,
    * we'll set them to NULL. */
@@ -25213,17 +28858,19 @@ development and get involved in @ref{Gnuastro project 
webpage},
 @section Why C programming language?
 @cindex C programming language
 @cindex C++ programming language
+@cindex Java programming language
 @cindex Python programming language
-Currently the programming language that is most commonly used in scientific
-applications is C++@footnote{@url{https://isocpp.org/}},
+Currently the programming languages that are commonly used in scientific
+applications are C++@footnote{@url{https://isocpp.org/}},
+Java@footnote{@url{https://en.wikipedia.org/wiki/Java_(programming_language)}};
 Python@footnote{@url{https://www.python.org/}}, and
 Julia@footnote{@url{https://julialang.org/}} (which is a newcomer but
-swiftly gaining ground). One of the main reasons behind this choice is
+swiftly gaining ground). One of the main reasons behind choosing these is
 their high-level abstractions. However, GNU Astronomy Utilities is fully
 written in the C programming
 
language@footnote{@url{https://en.wikipedia.org/wiki/C_(programming_language)}}.
 The
 reasons can be summarized with simplicity, portability and
-efficiency/speed. All three are very important in a scientific software and
+efficiency/speed. All four are very important in a scientific software and
 we will discuss them below.
 
 @cindex ANSI C
@@ -25248,7 +28895,7 @@ be able to readily read the code of a program at their 
will with minimum
 requirements.
 
 @cindex Object oriented programming
-In C++, inheriting objects in the object oriented programming paradigm and
+In C++ or Java, inheritance in the object oriented programming paradigm and
 their internal functions make the code very easy to write for a programmer
 who is deeply invested in those objects and understands all their relations
 well. But it simultaneously makes reading the program for a first time
@@ -25289,20 +28936,38 @@ future versions any more. Some converters are 
available, but since they are
 automatic, lots of complications might arise in the conversion@footnote{For
 example see @url{https://arxiv.org/abs/1712.00461, Jenness (2017)} which
 describes how LSST is managing the transition.}.
-
 If a research project begins using Python 3.x today, there is no telling
 how compatible their investments will be when Python 4.x or 5.x will come
-out. This stems from the core principles of Python, which are very useful
-when you look in the `on the go' basis as described before and not future
-usage. Reproducibility (ability to run the code in the future) is a core
-principal of any scientific result, or the software that produced that
-result. Rebuilding all the dependencies of a software in an obsolete
-language is not easy. Future-proof code (as long as current operating
-systems will be used) is written in C.
-
-The portability of C is best demonstrated by the fact that both C++ and
+out.
+
+@cindex JVM: Java virtual machine
+@cindex Java Virtual Machine (JVM)
+Java is also fully object-oriented, but uses a different paradigm: its
+compilation generates a hardware-independent @emph{bytecode}, and a
+@emph{Java Virtual Machine} (JVM) is required for the actual execution of
+this bytecode on a computer. Java also evolved with time, and tried to
+remain backward compatible, but inevitably some evolutions required
+discontinuities and replacements of a few Java components which were first
+declared as becoming @emph{deprecated}, and removed from later versions.
+
+@cindex Reproducibility
+This stems from the core principles of high-level languages like Python of
+Java: that they evolve significantly on the scale of roughly 5 to 10
+years. They are therefore useful when you want to solve a short-term
+problem and you are ready to pay the high cost of keeping your software up
+to date with all the changes in the language. This is fine for private
+companies, but usually too expensive for scientific projects that have
+limited funding for a fixed period. As a result, the reproducibility of the
+result (ability to regenerate the result in the future, which is a core
+principal of any scientific result) and re-usability of all the investments
+that went into the science software will be lost to future generations!
+Rebuilding all the dependencies of a software in an obsolete language is
+not easy, or even not possible. Future-proof code (as long as current
+operating systems will be used) is therefore written in C.
+
+The portability of C is best demonstrated by the fact that C++, Java and
 Python are part of the C-family of programming languages which also include
-Julia, Java, Perl, and many other languages. C libraries can be immediately
+Julia, Perl, and many other languages. C libraries can be immediately
 included in C++, and it is easy to write wrappers for them in all C-family
 programming languages. This will allow other scientists to benefit from C
 libraries using any C-family language that they prefer. As a result,
@@ -25315,16 +28980,19 @@ higher-level languages like Python, Julia and Java.
 The final reason was speed. This is another very important aspect of C
 which is not independent of simplicity (first reason discussed above). The
 abstractions provided by the higher-level languages (which also makes
-learning them harder for a newcomer) comes at the cost of speed. Since C is
+learning them harder for a newcomer) come at the cost of speed. Since C is
 a low-level language@footnote{Low-level languages are those that directly
 operate the hardware like assembly languages. So C is actually a high-level
 language, but it can be considered one of the lowest-level languages among
-all high-level languages.} (closer to the hardware), it is much less
-complex for both the human reader @emph{and} the computer. The benefits of
-simplicity for a human were discussed above. Simplicity for the computer
-translates into more efficient (faster) programs. This creates a much
-closer relation between the scientist/programmer (or their program) and the
-actual data and processing. The GNU coding
+all high-level languages.} (closer to the hardware), it has a direct access
+to the CPU@footnote{for instance the @emph{long double} numbers with at
+least 64-bit mantissa are not accessible in Python or Java.}, is generally
+considered as being faster in its execution, and is much less complex for
+both the human reader @emph{and} the computer. The benefits of simplicity
+for a human were discussed above. Simplicity for the computer translates
+into more efficient (faster) programs. This creates a much closer relation
+between the scientist/programmer (or their program) and the actual data and
+processing. The GNU coding
 standards@footnote{@url{http://www.gnu.org/prep/standards/}} also encourage
 the use of C over all other languages when generality of usage and ``high
 speed'' is desired.
@@ -25627,7 +29295,7 @@ new to coding (and text editors) and only has a 
scientific curiosity.
 
 Newcomers to coding and development, who are curious enough to venture into
 the code, will probably not be using (or have any knowledge of) advanced
-text editors. They will see the raw code in the webpage or on a simple text
+text editors. They will see the raw code in the web page or on a simple text
 editor (like Gedit) as plain text. Trying to learn and understand a file
 with dense functions that are all spaced with one or two blank lines can be
 very taunting for a newcomer. But when they scroll through the file and see
@@ -26323,7 +29991,7 @@ in the documentation (also a bug) or a feature request 
or an enhancement.
 The set of horizontal links on the top of the page (Starting with
 `Main' and `Homepage' and finishing with `News') are the easiest way
 to access these trackers (and other major aspects of the project) from
-any part of the project webpage. Hovering your mouse over them will
+any part of the project web page. Hovering your mouse over them will
 open a drop down menu that will link you to the different things you
 can do on each tracker (for example, `Submit new' or `Browse').  When
 you browse each tracker, you can use the ``Display Criteria'' link
@@ -26492,10 +30160,10 @@ people who have assigned their copyright to the FSF 
and have thus helped to
 guarantee the freedom and reliability of Gnuastro. The Free Software
 Foundation will also acknowledge your copyright contributions in the Free
 Software Supporter: @url{https://www.fsf.org/free-software-supporter} which
-will circulate to a very large community (104,444 people in April
-2016). See the archives for some examples and subscribe to receive
+will circulate to a very large community (222,882 people in January 2021).
+See the archives for some examples and subscribe to receive
 interesting updates. The very active code contributors (or developers) will
-also be recognized as project members on the Gnuastro project webpage (see
+also be recognized as project members on the Gnuastro project web page (see
 @ref{Gnuastro project webpage}) and can be given a @code{gnu.org} email
 address. So your very valuable contribution and copyright assignment will
 not be forgotten and is highly appreciated by a very large community. If
@@ -26678,13 +30346,13 @@ This is a tutorial on the second suggested method 
(commonly known as
 forking) that you can submit your modifications in Gnuastro (see
 @ref{Production workflow}).
 
-To start, please create an empty repository on your hosting service webpage
+To start, please create an empty repository on your hosting service web page
 (we recommend GitLab@footnote{See
 @url{https://www.gnu.org/software/repo-criteria-evaluation.html} for an
 evaluation of the major existing repositories. Gnuastro uses GNU Savannah
 (which also has the highest ranking in the evaluation), but for starters,
 GitLab may be easier.}). If this is your first hosted repository on the
-webpage, you also have to upload your public SSH key@footnote{For example
+web page, you also have to upload your public SSH key@footnote{For example
 see this explanation provided by GitLab:
 @url{http://docs.gitlab.com/ce/ssh/README.html}.} for the @command{git
 push} command below to work. Here we'll assume you use the name
@@ -26692,7 +30360,7 @@ push} command below to work. Here we'll assume you use 
the name
 @file{gnuastro-janedoe} as the name of your Gnuastro fork. Any online
 hosting service will give you an address (similar to the
 `@file{git@@gitlab.com:...}' below) of the empty repository you have created
-using their webpage, use that address in the third line below.
+using their web page, use that address in the third line below.
 
 @example
 $ git clone git://git.sv.gnu.org/gnuastro.git
@@ -26869,6 +30537,11 @@ uses a technique to detect very faint and diffuse, 
irregularly shaped
 signal in noise (galaxies in the sky), using thresholds that are below the
 Sky value, see @url{http://arxiv.org/abs/1505.01664, arXiv:1505.01664}.
 
+@item Query
+(@file{astquery}, see @ref{Query}) High-level interface to query
+pre-defined remote, or external databases, and directly download the
+required sub-tables on the command-line.
+
 @item Segment
 (@file{astsegment}, see @ref{Segment}) Segment detected regions based on
 the structure of signal and the input dataset's noise properties.
@@ -26930,7 +30603,7 @@ SAO ds9@footnote{@url{http://ds9.si.edu/}} is not a 
requirement of
 Gnuastro, it is a FITS image viewer. So to check your inputs and
 outputs, it is one of the best options. Like the other packages, it
 might already be available in your distribution's repositories. It is
-already pre-compiled in the download section of its webpage. Once you
+already pre-compiled in the download section of its web page. Once you
 download it you can unpack and install (move it to a system recognized
 directory) with the following commands (@code{x.x.x} is the version
 number):
@@ -27173,7 +30846,7 @@ WCSLIB. Installing it is a little tricky (mainly 
because it is so
 old!).
 
 You can download the most recent version from the FTP link in its
-webpage@footnote{@url{http://www.astro.caltech.edu/~tjp/pgplot/}}. You can
+web page@footnote{@url{http://www.astro.caltech.edu/~tjp/pgplot/}}. You can
 unpack it with the @command{tar xf} command. Let's assume the directory you
 have unpacked it to is @file{PGPLOT}, most probably it is:
 @file{/home/username/Downloads/pgplot/}.  open the @file{drivers.list}
diff --git a/doc/gnuastro.translist b/doc/gnuastro.translist
index b32d101..0a61f9d 100644
--- a/doc/gnuastro.translist
+++ b/doc/gnuastro.translist
@@ -1,5 +1,5 @@
 <!-- begin translist file -->
-<!-- Copyright (C) 2015-2019, Free Software Foundation, Inc.
+<!-- Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
  This file is part of GNU Astronomy Utilities (Gnuastro) webpage.
 
diff --git a/doc/javascript.html b/doc/javascript.html
index b078dda..60a3468 100644
--- a/doc/javascript.html
+++ b/doc/javascript.html
@@ -4,7 +4,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2016-2019, Free Software Foundation, Inc.
+# Copyright (C) 2016-2021, 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
@@ -50,6 +50,41 @@
         <td><a 
href="../MathJax/extensions/MathZoom.js?rev=2.5.1">MathZoom.js</a></td>
       </tr>
 
+      <tr>
+        <td><a href="../MathJax/jax/output/HTML-CSS/jax.js">jax.js</a></td>
+        <td><a 
href="http://www.apache.org/licenses/LICENSE-2.0";>Apache-2.0-only</a></td>
+        <td><a href="../MathJax/jax/output/HTML-CSS/jax.js">jax.js</a></td>
+      </tr>
+
+      <tr>
+        <td><a 
href="../MathJax/jax/element/mml/optable/BasicLatin.js">BasicLatin.js</a></td>
+        <td><a 
href="http://www.apache.org/licenses/LICENSE-2.0";>Apache-2.0-only</a></td>
+        <td><a 
href="../MathJax/jax/element/mml/optable/BasicLatin.js">BasicLatin.js</a></td>
+      </tr>
+
+      <tr>
+        <td><a 
href="../MathJax/jax/output/HTML-CSS/fonts/TeX/fontdata.js">fontdata.js</a></td>
+        <td><a 
href="http://www.apache.org/licenses/LICENSE-2.0";>Apache-2.0-only</a></td>
+        <td><a 
href="../MathJax/jax/output/HTML-CSS/fonts/TeX/fontdata.js">fontdata.js</a></td>
+      </tr>
+
+      <tr>
+        <td><a 
href="../MathJax/jax/output/HTML-CSS/autoload/mtable.js">mtable.js</a></td>
+        <td><a 
href="http://www.apache.org/licenses/LICENSE-2.0";>Apache-2.0-only</a></td>
+        <td><a 
href="../MathJax/jax/output/HTML-CSS/autoload/mtable.js">mtable.js</a></td>
+      </tr>
+
+      <tr>
+        <td><a 
href="../MathJax/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/Main.js">Main.js</a></td>
+        <td><a 
href="http://www.apache.org/licenses/LICENSE-2.0";>Apache-2.0-only</a></td>
+        <td><a 
href="../MathJax/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/Main.js">Main.js</a></td>
+      </tr>
+
+      <tr>
+        <td><a 
href="../MathJax/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/GeneralPunctuation.js">GeneralPunctuation.js</a></td>
+        <td><a 
href="http://www.apache.org/licenses/LICENSE-2.0";>Apache-2.0-only</a></td>
+        <td><a 
href="../MathJax/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/GeneralPunctuation.js">GeneralPunctuation.js</a></td>
+      </tr>
     </table>
   </body>
 </html>
diff --git a/doc/plotsrc/Makefile b/doc/plotsrc/Makefile
index e7a6788..482dfc0 100644
--- a/doc/plotsrc/Makefile
+++ b/doc/plotsrc/Makefile
@@ -3,7 +3,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019, Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # GNU Astronomy Utilities is free software: you can redistribute it
 # and/or modify it under the terms of the GNU General Public License
@@ -20,32 +20,43 @@
 # <http://www.gnu.org/licenses/>.
 
 # Definitions of which rules to apply:
-all: all.pdf
+finaltarget=../gnuastro-figures/done.txt
+all: $(finaltarget)
 .PHONY: clean
 
 
 
 
 
-# Make all the plots and convert all the figures
-all.pdf: all.tex ./tex/*.tex ./conversions.sh
+# Directory to keep TiKZ products.
+tikz:
+       mkdir $@
 
-#      Make the tikz directory if it doesn't exist:
-       mkdir -p tikz
 
-#      Run the LaTeX script:
-       latex -shell-escape all.tex
-       dvipdf all.dvi
-       rm -f *.bcf *.xml *.aux *.auxlock *.log *.out *.dvi
 
-#      Put all the EPS files in their appropriate place:
-       cp tikz/all-figure0.eps ../gnuastro-figures/iandtime.eps
-       cp tikz/all-figure1.eps ../gnuastro-figures/samplingfreq.eps
-       cp tikz/all-figure2.eps ../gnuastro-figures/flatplane.eps
-       cp tikz/all-figure3.eps ../gnuastro-figures/sphereandplane.eps
 
-#      Make all the conversions:
-       ./conversions.sh ../gnuastro-figures/
+
+# Make all the plots with LaTeX, then convert all the figures to the
+# necessary formats using a separate script and finally write the final
+# target.
+#
+# Explanation for putting all the commands in one line (by ending them with
+# a '\' and starting the next one with '&&'). We do this because we can't
+# assume GNU Make, or GNU Bash here (to use the '.SHELLFLAGS = -ec' and
+# '.ONESHELL:' features in the Makefile). The user's Make or shell can be
+# any implementation. If any of these commands fails, the final target
+# should not be built and the Makefile should crash.
+$(finaltarget): all.tex ./tex/*.tex ./conversions.sh | tikz
+
+       latex -shell-escape all.tex \
+       && dvipdf all.dvi \
+       && rm -f *.bcf *.xml *.aux *.auxlock *.log *.out *.dvi \
+       && cp tikz/all-figure0.eps ../gnuastro-figures/iandtime.eps \
+       && cp tikz/all-figure1.eps ../gnuastro-figures/samplingfreq.eps \
+       && cp tikz/all-figure2.eps ../gnuastro-figures/flatplane.eps \
+       && cp tikz/all-figure3.eps ../gnuastro-figures/sphereandplane.eps \
+       && ./conversions.sh ../gnuastro-figures/ \
+       && echo "All necessary images created." > $(finaltarget)
 
 
 
@@ -58,7 +69,7 @@ all.pdf: all.tex ./tex/*.tex ./conversions.sh
 #   2. Remove everything in the gnuastro-figures directory
 #   3. Put the actual images back in that directory.
 clean:
-       rm -rf all.pdf tikz
-       cp ../gnuastro-figures/epicycles.png ./
-       rm ../gnuastro-figures/*
-       mv epicycles.png ../gnuastro-figures/
+       rm -rf all.pdf tikz \
+       && cp ../gnuastro-figures/epicycles.png ./ \
+       && rm ../gnuastro-figures/* \
+       && mv epicycles.png ../gnuastro-figures/
diff --git a/doc/plotsrc/README b/doc/plotsrc/README
index 6511edf..cc60baa 100644
--- a/doc/plotsrc/README
+++ b/doc/plotsrc/README
@@ -1,7 +1,7 @@
 Make the plots/figures of the book
 ----------------------------------
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
 This directory has all the necessary information to make the plots
@@ -21,9 +21,9 @@ PRACTICAL NOTES
 1. When creating the final PDF files here (usually at the end of the
 boostrapping step), you might get this error:
 
-convert: attempt to perform an operation not allowed by the security policy 
`EPS' @ error/constitute.c/IsCoderAuthorized/408.
+convert: attempt to perform an operation not allowed by the security policy 
'EPS' @ error/constitute.c/IsCoderAuthorized/408.
 
-If you do, go to `/etc/ImageMagick-7/policy.xml', and uncomment the line
+If you do, go to '/etc/ImageMagick-7/policy.xml', and uncomment the line
 with the EPS,PDF,etc.... policy limitations. It was apparently due to a bug
 in Ghostscript that has been fixed since version 9.24
 (https://www.kb.cert.org/vuls/id/332928/).
@@ -32,9 +32,9 @@ in Ghostscript that has been fixed since version 9.24
 
 Copyright
 ---------
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
 version published by the Free Software Foundation; with no Invariant
-Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
\ No newline at end of file
+Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
diff --git a/doc/plotsrc/all.tex b/doc/plotsrc/all.tex
index c9c4888..0f1c8d4 100644
--- a/doc/plotsrc/all.tex
+++ b/doc/plotsrc/all.tex
@@ -5,7 +5,7 @@
 % Original author:
 %     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 % Contributing author(s):
-% Copyright (C) 2015-2019, Free Software Foundation, Inc.
+% Copyright (C) 2015-2021, 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
diff --git a/doc/plotsrc/conversions.sh b/doc/plotsrc/conversions.sh
index df48883..8dd4667 100755
--- a/doc/plotsrc/conversions.sh
+++ b/doc/plotsrc/conversions.sh
@@ -13,7 +13,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019, Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, 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
diff --git a/doc/plotsrc/tex/flatplane.tex b/doc/plotsrc/tex/flatplane.tex
index d68d9f7..c11cea0 100644
--- a/doc/plotsrc/tex/flatplane.tex
+++ b/doc/plotsrc/tex/flatplane.tex
@@ -5,7 +5,7 @@
 % Original author:
 %     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 % Contributing author(s):
-% Copyright (C) 2016-2019, Free Software Foundation, Inc.
+% Copyright (C) 2016-2021, 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
diff --git a/doc/plotsrc/tex/iandtime.tex b/doc/plotsrc/tex/iandtime.tex
index 1ccea2f..44c87e2 100644
--- a/doc/plotsrc/tex/iandtime.tex
+++ b/doc/plotsrc/tex/iandtime.tex
@@ -5,7 +5,7 @@
 % Original author:
 %     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 % Contributing author(s):
-% Copyright (C) 2015-2019, Free Software Foundation, Inc.
+% Copyright (C) 2015-2021, 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
diff --git a/doc/plotsrc/tex/samplingfreq.tex b/doc/plotsrc/tex/samplingfreq.tex
index 5e34e5d..677cd8b 100644
--- a/doc/plotsrc/tex/samplingfreq.tex
+++ b/doc/plotsrc/tex/samplingfreq.tex
@@ -5,7 +5,7 @@
 % Original author:
 %     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 % Contributing author(s):
-% Copyright (C) 2015-2019, Free Software Foundation, Inc.
+% Copyright (C) 2015-2021, 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
diff --git a/doc/plotsrc/tex/sphereandplane.tex 
b/doc/plotsrc/tex/sphereandplane.tex
index ee27ede..03e4410 100644
--- a/doc/plotsrc/tex/sphereandplane.tex
+++ b/doc/plotsrc/tex/sphereandplane.tex
@@ -5,7 +5,7 @@
 % Original author:
 %     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 % Contributing author(s):
-% Copyright (C) 2016-2019, Free Software Foundation, Inc.
+% Copyright (C) 2016-2021, 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
diff --git a/doc/release-checklist.txt b/doc/release-checklist.txt
index 042ad7b..8ed5f5e 100644
--- a/doc/release-checklist.txt
+++ b/doc/release-checklist.txt
@@ -1,14 +1,60 @@
 Gnuastro release checklist
 ==========================
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
+
+
+Creating animated SVG for demo
+==============================
+
+To help in visualizing the new features of every release, we show the
+features in a recorded terminal.
+
+ - Prerequisites;
+     - termtosvg (BSD: https://github.com/nbedos/termtosvg)
+     - NOT WORKING (as of February 9th, 2021, 'svg-term' has problems with
+       converting 'asciinema', complaining about the format)
+       - asciinema (GPL v3: https://asciinema.org/docs/installation).
+       - svg-term (MIT license: https://github.com/marionebl/svg-term-cli).
+
+ - Change your terminal prompt to be general by putting the line below
+   inside your user's '.bashrc' (just change 'XX' to the version of
+   Gnuastro that you want to display). Asciinema creates a new shell, so
+   you can't temporarily set it before calling asciinema.
+
+      export PS1="[\[\033[01;35m\]Gnuastro XX\[\033[32m\]\[\033[00m\]]$ "
+
+ - Make a clean empty directory (to avoid mixing existing files), and copy
+   any necessary dataset into it.
+
+      $ mkdir feature-demo
+      $ cd feature-demo
+      $ cp XXXXXXXX ./
+
+ - Start "recording". To help in writing, its good to reset the
+   screen.
+
+      $ rm ./*
+      $ reset
+      $ termtosvg ../feature.svg --screen-geometry 100x20
+
+ - When the recording is finished, put an entry in the "Comming soon" page
+   with a small description, remove the extra line and your
+
+
+
+
+
+Tarball release on gnu.org
+==========================
+
 This file is primarily intended for the Gnuastro maintainer and lists the
 set of operations to do for making each release. This should be done after
 all the commits needed for this release have been completed.
 
- - [STABLE] Run a spell-check (in emacs, with `M-x ispell') on the new
+ - [STABLE] Run a spell-check (in emacs, with 'M-x ispell') on the new
    parts of the book. You can put them in a test file with this command,
    just replace X.X with the previous version:
 
@@ -17,11 +63,11 @@ all the commits needed for this release have been completed.
 
 
  - [STABLE] Check if THANKS, and the book's Acknowledgments section have
-   everyone in `doc/announce-acknowledge.txt' in them. To see who has been
-   added in the `THANKS' file since the last stable release (to add in the
+   everyone in 'doc/announce-acknowledge.txt' in them. To see who has been
+   added in the 'THANKS' file since the last stable release (to add in the
    book), you can use this command:
 
-       $ git diff gnuastro_vP.P..HEAD THANKS
+       $ git diff gnuastro_vX.X..HEAD THANKS
 
 
  - Build the Debian distribution (just for a test) and correct any build or
@@ -30,12 +76,12 @@ all the commits needed for this release have been completed.
    are very useful in making the package build cleanly on other systems.
 
    If you don't actually want to make a Debian release, in the end, instead
-   of running `git push', just delete the top commits that you made in the
+   of running 'git push', just delete the top commits that you made in the
    three branchs with the following command
 
        $ git checkout master
        $ git tag -d debian/$ver-1
-       $ git reset --hard HEAD~1        # To go to the merge with upstream.
+       $ git reset --hard HEAD~1            # To go to the merge with upstream.
        $ git reset --hard HEAD~1            # To go to last Debian release.
        $ git checkout pristine-tar
        $ git reset --hard HEAD~1
@@ -53,17 +99,17 @@ all the commits needed for this release have been completed.
 
 
  - 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
+   code and those that must be acknowledged for the announcement ('P.P' is
    the previous version).
 
-     $ git shortlog gnuastro_vP.P...HEAD --numbered --summary > ~/people.txt
+     $ git shortlog gnuastro_vX.X...HEAD --numbered --summary > ~/people.txt
      $ cat doc/announce-acknowledge.txt >> ~/people.txt
 
-     [STABLE] Remove the names in `doc/announce-acknowledge.txt'.
+     [STABLE] Remove the names in 'doc/announce-acknowledge.txt'.
 
 
- - [STABLE] Correct the version links in the webpage
-   (`doc/gnuastro.en.html' and `doc/gnuastro.fr.html'). Note that this is
+ - [STABLE] Correct the version links, and date, in the webpage
+   ('doc/gnuastro.en.html' and 'doc/gnuastro.fr.html'). Note that this is
    not for upload at this stage, it is only for the version control.
 
 
@@ -80,10 +126,9 @@ all the commits needed for this release have been completed.
      $ ./bootstrap --copy --gnulib-srcdir=/path/to/updated/gnulib
 
 
- - Update the version and build Gnuastro, all done with the `-p' option of
-   `./developer-build'. But we'll give it a `pure-junk' directory so it
-   doesn't actually upload the build. Then go into the build directory and
-   run `make distcheck -j8'.
+ - Update the version and build Gnuastro, all done with the '-p' option of
+   './developer-build'. But we'll give it a 'pure-junk' directory so it
+   doesn't actually upload the build.
 
      $ ./developer-build -p pure-junk
 
@@ -94,7 +139,7 @@ all the commits needed for this release have been completed.
      $ make distcheck -j8
 
 
- - [STABLE]: After the `make distcheck' is safely finished, tag the release:
+ - [STABLE]: After the 'make distcheck' is safely finished, tag the release:
 
      $ cd ..
      $ git tag -a gnuastro_vX.X
@@ -105,11 +150,11 @@ all the commits needed for this release have been 
completed.
 
      $ ./developer-build -a -c -C -d
      $ cd build
-     $ make dist dist-lzip       # to build `tar.gz' and `tar.lz'.
+     $ make dist dist-lzip       # to build 'tar.gz' and 'tar.lz'.
 
 
- - Upload the tarball with the command below: Note that `gnupload'
-   uses `ncftpput' which comes with the `ncftp' package. The replaces
+ - Upload the tarball with the command below: Note that 'gnupload'
+   uses 'ncftpput' which comes with the 'ncftp' package. The replaces
    are necessary for the symbolic links.
 
    - Set the key-ID as a variable for easy steps later:
@@ -134,12 +179,15 @@ all the commits needed for this release have been 
completed.
    directory to build the full webpage with this script. We will build with
    debug flags so the build completes fast.
 
+   IMPORTANT NOTE: don't end the directory with a '/'. The directory should
+   be the one that has 'CVSROOT' in it.
+
        $ ./configure --enable-debug
        $ make -j8
        $ cd doc
        $ ./forwebpage /path/to/local/copy/of/webpage
 
-   If any of the files have a `?' in front 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
@@ -158,8 +206,8 @@ all the commits needed for this release have been completed.
 
  - Prepare the announcement, this command will calculate the checksums and
    also make the links ready. You just have to add a starting and ending
-   similar to previous announcements in a text editor. In the `XXXX', put
-   `stable' or `alpha' and in YYYY, put `ftp' for a stable, and `alpha' for
+   similar to previous announcements in a text editor. In the 'XXXX', put
+   'stable' or 'alpha' and in YYYY, put 'ftp' for a stable, and 'alpha' for
    an alpha release.
 
      $ cd build
@@ -167,27 +215,28 @@ all the commits needed for this release have been 
completed.
               --package-name=gnuastro --previous-version=0.1               \
               --current-version=0.2 --gpg-key-id=$mykeyid                  \
               --url-directory=https://YYYY.gnu.org/gnu/gnuastro            \
-              --archive-suffix=tar.lz > ~/announcement.txt
+              > ~/announcement.txt
 
 
  - Based on previous announcements, add an intro, the NEWS file and the
-   contents of `~/people.txt' to the announcement.
+   contents of '~/people.txt' to the announcement.
 
 
- - Add the size of the files (in mega-bytes) to the announcement.
+ - Add the size of the detached signatures (833B) to the announcement. By
+   default it only prints the size of the actual tarball(s).
 
 
- - Run a spell-check on the announcement and remove `~/people.txt'.
+ - Run a spell-check on the announcement and remove '~/people.txt'.
 
      $ rm ~/people.txt
 
 
- - Announce the release on `info-gnuastro@gnu.org', `info-gnu@gnu.org'
+ - Announce the release on 'info-gnuastro@gnu.org', 'info-gnu@gnu.org'
    (only for STABLE) and Savannah news (only for STABLE).
 
 
- - [STABLE] Open `configure.ac' and increment `GAL_CURRENT' for the next
-   release. See the `Updating library version information' section of the
+ - [STABLE] Open 'configure.ac' and increment 'GAL_CURRENT' for the next
+   release. See the 'Updating library version information' section of the
    GNU Libtool manual as a guide. Note that we are assuming that until the
    next release some change will be made in the library.
 
@@ -216,51 +265,71 @@ Steps necessary to Package Gnuastro for Debian.
       $ ./bootstrap --copy --gnulib-srcdir=/path/to/gnulib
       $ ./developer-build -p upload-server:folder
 
+ - Some tips on basic Debian operating system to do the packaging:
 
- - It would help to use the most recent versions of packages in Debian. To
-   do that, you can update the `/etc/apt/sources.list' file. You can get
-   the current releases from this webpage:
-   http://ftp.debian.org/debian/. See which release is currently the
-   `testing' release and replace its name with the one that is currently
-   present in that file. Afterwards, run these commands:
-
-     $ sudo apt-get update
-     $ sudo apt-get upgrade
+   After installing Debian from DVD, you need to tell apt to use an
+   internet connection and not the DVD for installing the packages. To do
+   that, comment the line(s) with a `cdrom' in this file:
 
+      nano /etc/apt/sources.list
 
- - If this is the first time you are packaging on this system, you will
-   need to install the following programs. The first group of packages are
-   general for package building, and the second are only for Gnuastro.
+   Install the following programs. The first group of packages are general
+   for package building, and the second are only for Gnuastro.
 
      $ sudo apt-get install ssh devscripts pbuilder pristine-tar git quilt \
-                            lintian lzip emacs
-     $ sudo apt-get install debhelpler ghostscript libcfitsio-dev \
-                            libtool-bin libgsl0-dev libjpeg-dev   \
-                            libtiff-dev libgit2-dev wcslib-dev
+                            lintian lzip emacs dh-make
+     $ sudo apt-get install ghostscript libcfitsio-dev libtool-bin wcslib-dev \
+                            libgsl-dev libjpeg-dev libtiff-dev libgit2-dev
      $ sudo pbuilder create
-     $ emacs ~/.devscripts
+     $ su
+     # usermod -aG sudo YOURUSERID   # Add your user to the 'sudo' group.
 
-   Add these two lines to the opened file:
+   Add these two lines to '~/.devscripts':
 
      DEBFULLNAME="Your name"
      DEBEMAIL=your@email.address
 
-   Then add these lines to `~/.quiltrc':
+   Add these lines to '~/.quiltrc':
 
      QUILT_PATCHES=debian/patches
      QUILT_NO_DIFF_INDEX=1
      QUILT_NO_DIFF_TIMESTAMPS=1
      QUILT_REFRESH_ARGS="-p ab"
 
+   Add this line to `~/.pbuilderrc':
+
+    DEBMAIL="Your Name <your@email.address>"
+
    A restart should help in making sure everything that has been updated is
    being used.
 
+ - It would help to use the most recent versions of packages in Debian. To
+   do that, you can update the '/etc/apt/sources.list' file. You can get
+   the current releases from this webpage:
+   http://ftp.debian.org/debian/. See which release is currently the
+   'testing' release and replace its name with the one that is currently
+   present in that file. Afterwards, run these commands:
+
+     $ sudo apt-get update
+     $ sudo apt-get upgrade
 
  - If you don't already have the Git repository, clone it with the
-   following command. A `gnuastro' directory will be built, for the moment
-   don't go in it.
+   following command. A 'gnuastro' directory will be built, but to setup
+   the other to main branches, you'll need to go into the cloned directory
+   and checkout to them manually.
 
      $ git clone https://salsa.debian.org/debian-astro-team/gnuastro.git
+     $ cd gnuastro
+     $ git checkout -b upstream --track origin/upstream
+     $ git checkout -b pristine-tar --track origin/pristine-tar
+     $ git checkout master
+
+   Note that if you have just setup the operating system, add your basic
+   Git information:
+
+     $ git config --global user.name "John Doe"
+     $ git config --global user.email johndoe@example.com
+     $ git config --global core.editor emacs
 
 
  - If a Git directory exists, then pull any possible changes that already
@@ -278,8 +347,8 @@ Steps necessary to Package Gnuastro for Debian.
 
 
  - [ALPHA] Build an ASCII-armored, detached signature for the tarball with
-   this command (it will make a `.asc' file by default, so use that instead
-   of `.sig' in the two following steps).
+   this command (it will make a '.asc' file by default, so use that instead
+   of '.sig' in the two following steps).
 
      $ gpg -b --armor gnuastro-X.Y.ZZZZ-ZZZZ.tar.gz
 
@@ -298,8 +367,8 @@ Steps necessary to Package Gnuastro for Debian.
      $ export ver=A.B.CCC
 
 
- - Make a standard symbolic link to the tarball (IMPORTANT: the `dash' is
-   changed to an `underscore' and an `orig' is added), then go into the
+ - Make a standard symbolic link to the tarball (IMPORTANT: the 'dash' is
+   changed to an 'underscore' and an 'orig' is added), then go into the
    cloned directory.
 
      $ mv gnuastro-$ver-XXXX.tar.gz      gnuastro_$ver.orig.tar.gz
@@ -307,19 +376,19 @@ Steps necessary to Package Gnuastro for Debian.
      $ cd gnuastro
 
 
- - You need to checkout to the `upstream' branch, clean everything that was
+ - You need to checkout to the 'upstream' branch, clean everything that was
    in it and unpack this release's raw package source files into it as
    shown below.
 
      $ git checkout upstream
-     $ mv .git ../gnuastro-tmp-git         # We want to keep `.git'.
+     $ mv .git ../gnuastro-tmp-git         # We want to keep '.git'.
      $ rm -rf ./* ./.*                     # Delete everything.
-     $ mv ../gnuastro-tmp-git .git         # Bring back the `.git' directory.
+     $ mv ../gnuastro-tmp-git .git         # Bring back the '.git' directory.
      $ tar xf ../gnuastro_$ver.orig.tar.gz --strip-components=1
 
 
- - We now need to commit these into the `upstream' branch of the Git
-   history, tag it and run `pristine-tar' on it.
+ - We now need to commit these into the 'upstream' branch of the Git
+   history, tag it and run 'pristine-tar' on it.
 
      $ git add --all
      $ git commit -m "Upstream Gnuastro $ver"
@@ -328,68 +397,67 @@ Steps necessary to Package Gnuastro for Debian.
                     -s ../gnuastro_$ver.orig.tar.gz.asc
 
 
- - We are done with the `upstream' and `pristine-tar' branches and can
-   checkout `master':
+ - We are done with the 'upstream' and 'pristine-tar' branches and can
+   checkout 'master' and merge with the upstream branch:
 
      $ git checkout master
-
-
- - Merge the upstream branch into the master branch to update the Gnuastro
-   files in master also:
-
      $ git merge upstream
 
 
  - Check the current Debian policy version and update it in
-   `debian/control'. The policy can be found here:
+   'debian/control'. NOTE that you only need to put the top three digits
+   and ignore the fourth digit. The policy can be found here:
    https://www.debian.org/doc/debian-policy/
 
-     $ emacs debian/control
+     $ emacs debian/control   # Only three digits "X.Y.Z"
 
 
  - If the soname of the shared libraries has changed:
 
-     - Rename the file `debian/libgnuastroX.install' (set `X' to the new
+     - Rename the file 'debian/libgnuastroX.install' (set 'X' to the new
        soname).
 
-     - In `debian/control', change all the old sonames to the new value.
+     - In 'debian/control', change all the old sonames to the new value.
 
 
- - Update `debian/changelog' with all the Debian-related changes (since
+ - Update 'debian/changelog' with all the Debian-related changes (since
    merging with the upstream branch). Gnuastro's changes don't need to be
    mentioned here. If there was no major changes, just say "New upstream
    version".
 
-   IMPORTANT: An official release should have `unstable' after the
+   IMPORTANT: An official release should have 'unstable' after the
    version. But if you just want to make sure Gnuastro builds on all
-   systems for testing, it should be `experimental'.
+   systems for testing, it should be 'experimental'.
 
    When changing the state (from experimental to unstable or vice versa)
-   add the following line in `debian/changelog' (this is necessary to avoid
+   add the following line in 'debian/changelog' (this is necessary to avoid
    Lintian warnings):
 
    Experimental -> Unstable: "Switch to unstable for upstream stable release"
    Unstable -> Experimental: "Switch to experimental to prepare transition".
 
 
- - Update your version of `pbuilder':
+ - Update your version of 'pbuilder':
 
      $ sudo pbuilder update
 
 
- - Run `pdebuild' to build the package (needs sudo).
+ - Run 'pdebuild' to build the package (needs sudo).
 
      $ sudo pdebuild
 
 
- - Run Lintian to check the build.
+ - Run Lintian to check the build. You can ignore the warning about the
+   non-existant email address (hosted on 'alioth.debian.org'). As Ole
+   Streicher (olebole@debian.org) mentioned privately: "this links all
+   Debian Astro packages together".
 
-     $ lintian -E -I --pedantic                                         \
+     $ lintian -E -I --pedantic \
                /var/cache/pbuilder/result/gnuastro_$ver-1_amd64.changes
 
 
  - Commit the contents of the new release (should be just the contents of
-   the `debian' directory).
+   the 'debian' directory).
 
      $ git add --all
      $ git status                         # For a visual check
@@ -416,7 +484,7 @@ Steps necessary to Package Gnuastro for Debian.
 
 Copyright
 =========
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
diff --git a/doc/style.css b/doc/style.css
index 2658ea0..02d41ee 100644
--- a/doc/style.css
+++ b/doc/style.css
@@ -3,7 +3,7 @@
 Original author:
     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019 Free Software Foundation, Inc.
+Copyright (C) 2016-2021, Free Software Foundation, Inc.
 
 This stylesheet is free software: you can redistribute it and/or
 modify it under the terms of the GNU General Public Licence as
@@ -140,11 +140,11 @@ p{
 
 
 /* Select all paragraphs that must not be indented. Some of them don't need
-   any `:first-of-type' because:
+   any ':first-of-type' because:
 
-    - The `header's have only one <p> in the HTMLs.
+    - The 'header's have only one <p> in the HTMLs.
 
-    - All `<p>'s in the `.footnote's and `.bottom-links's must have no
+    - All '<p>'s in the '.footnote's and '.bottom-links's must have no
       indentation, not just the first ones. */
 .header p,
 .footnote p,
diff --git a/genauthors b/genauthors
index 3a53fc7..0349fc3 100755
--- a/genauthors
+++ b/genauthors
@@ -16,7 +16,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019, Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, 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
@@ -78,13 +78,13 @@ fi
 
 
 # Set the version number. Note that this script is also run at the start of
-# the bootstrapping process. At that point we don't have the `.version'
-# file, so we will just rely on `.git describe'. Later during `make', this
-# scripot will be run again to set it using `git-version-gen'.
+# the bootstrapping process. At that point we don't have the '.version'
+# file, so we will just rely on '.git describe'. Later during 'make', this
+# scripot will be run again to set it using 'git-version-gen'.
 if [ -f "$1/.version" ]; then
     gnuastroversion="Gnuastro "$(cat "$1/.version")
 else
-    gnuastroversion=$(git --git-dir=$1/.git describe)
+    gnuastroversion=$(git --git-dir=$1/.git describe --always)
 fi
 
 
diff --git a/lib/Makefile.am b/lib/Makefile.am
index d71ccdf..5b427c6 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -24,13 +24,16 @@
 
 ## Necessary flags.
 ##
-##   $(top_srcdir)/bootstrapped/lib: only necessary for the libraries since
-##       the Gnulib functions will be statically linked to the Gnuastro
-##       library so linking to Gnuastro is enough to access them also.
+##   $(top_builddir)/bootstrapped/lib: Gnulib headers that are customized
+##   for the running system.
 ##
-##   SYSCONFIG_DIR: only necessary in `options.c' to get the system
+##   $(top_srcdir)/bootstrapped/lib: Gnulib headers that are generic (don't
+##   need any customization).
+##
+##   SYSCONFIG_DIR: only necessary in 'options.c' to get the system
 ##       installation directory.
-AM_CPPFLAGS = -I\$(top_srcdir)/bootstrapped/lib            \
+AM_CPPFLAGS = -I\$(top_builddir)/bootstrapped/lib \
+              -I\$(top_srcdir)/bootstrapped/lib \
               -DSYSCONFIG_DIR=\"$(sysconfdir)\"
 
 
@@ -50,40 +53,49 @@ libgnuastro_la_LIBADD = 
$(top_builddir)/bootstrapped/lib/libgnu.la
 
 
 
+# Conditional compilation
+if COND_HASWCSDIS_H
+  MAYBE_WCSDISTORTION = wcsdistortion.c
+endif
+
+
+
+
 
 # Specify the library .c files
-libgnuastro_la_SOURCES = arithmetic.c arithmetic-and.c arithmetic-bitand.c \
-  arithmetic-bitlsh.c arithmetic-bitor.c arithmetic-bitrsh.c               \
-  arithmetic-bitxor.c arithmetic-divide.c arithmetic-eq.c arithmetic-ge.c  \
-  arithmetic-gt.c arithmetic-le.c arithmetic-lt.c arithmetic-minus.c       \
-  arithmetic-modulo.c arithmetic-multiply.c arithmetic-ne.c                \
-  arithmetic-or.c arithmetic-plus.c array.c binary.c blank.c box.c         \
-  checkset.c convolve.c cosmology.c data.c eps.c fits.c git.c              \
-  interpolate.c jpeg.c label.c list.c match.c options.c pdf.c              \
-  permutation.c pointer.c polygon.c qsort.c dimension.c speclines.c        \
-  statistics.c table.c tableintern.c threads.c tiff.c tile.c               \
-  tile-internal.c timing.c txt.c type.c wcs.c
+libgnuastro_la_SOURCES = $(MAYBE_WCSDISTORTION) arithmetic.c \
+  arithmetic-and.c arithmetic-bitand.c arithmetic-bitlsh.c \
+  arithmetic-bitor.c arithmetic-bitrsh.c arithmetic-bitxor.c \
+  arithmetic-divide.c arithmetic-eq.c arithmetic-ge.c arithmetic-gt.c \
+  arithmetic-le.c arithmetic-lt.c arithmetic-minus.c arithmetic-modulo.c \
+  arithmetic-multiply.c arithmetic-ne.c arithmetic-or.c \
+  arithmetic-plus.c arithmetic-set.c array.c binary.c blank.c box.c \
+  checkset.c convolve.c cosmology.c data.c eps.c fits.c git.c \
+  interpolate.c jpeg.c kdtree.c label.c list.c match.c options.c pdf.c \
+  permutation.c pointer.c polygon.c qsort.c dimension.c speclines.c \
+  statistics.c table.c tableintern.c threads.c tiff.c tile.c \
+  tile-internal.c timing.c txt.c type.c units.c wcs.c
 
 
 
 
 
-# Installed headers, note that we are not blindly including all `.h' files
+# Installed headers, note that we are not blindly including all '.h' files
 # in the $(headersdir) directory. Some of the header files don't need to be
 # installed.
 headersdir=$(top_srcdir)/lib/gnuastro
-pkginclude_HEADERS = gnuastro/config.h $(headersdir)/arithmetic.h          \
-  $(headersdir)/array.h $(headersdir)/binary.h $(headersdir)/blank.h       \
-  $(headersdir)/box.h $(headersdir)/convolve.h $(headersdir)/cosmology.h   \
-  $(headersdir)/data.h $(headersdir)/dimension.h $(headersdir)/eps.h       \
-  $(headersdir)/fits.h $(headersdir)/git.h $(headersdir)/interpolate.h     \
-  $(headersdir)/jpeg.h $(headersdir)/label.h $(headersdir)/list.h          \
-  $(headersdir)/match.h $(headersdir)/pdf.h $(headersdir)/permutation.h    \
-  $(headersdir)/pointer.h $(headersdir)/polygon.h $(headersdir)/qsort.h    \
-  $(headersdir)/speclines.h $(headersdir)/statistics.h                     \
-  $(headersdir)/table.h $(headersdir)/threads.h $(headersdir)/tiff.h       \
-  $(headersdir)/tile.h $(headersdir)/txt.h $(headersdir)/type.h            \
-  $(headersdir)/wcs.h
+pkginclude_HEADERS = gnuastro/config.h $(headersdir)/arithmetic.h \
+  $(headersdir)/array.h $(headersdir)/binary.h $(headersdir)/blank.h \
+  $(headersdir)/box.h $(headersdir)/convolve.h $(headersdir)/cosmology.h \
+  $(headersdir)/data.h $(headersdir)/dimension.h $(headersdir)/eps.h \
+  $(headersdir)/fits.h $(headersdir)/git.h $(headersdir)/interpolate.h \
+  $(headersdir)/jpeg.h $(headersdir)/kdtree.h $(headersdir)/label.h \
+  $(headersdir)/list.h $(headersdir)/match.h $(headersdir)/pdf.h \
+  $(headersdir)/permutation.h $(headersdir)/pointer.h $(headersdir)/polygon.h \
+  $(headersdir)/qsort.h $(headersdir)/speclines.h $(headersdir)/statistics.h \
+  $(headersdir)/table.h $(headersdir)/threads.h $(headersdir)/tiff.h \
+  $(headersdir)/tile.h $(headersdir)/txt.h $(headersdir)/type.h \
+  $(headersdir)/units.h $(headersdir)/wcs.h
 
 
 
@@ -95,21 +107,22 @@ pkginclude_HEADERS = gnuastro/config.h 
$(headersdir)/arithmetic.h          \
 # will not distributed, so we need to explicitly tell Automake to
 # distribute them here.
 internaldir=$(top_srcdir)/lib/gnuastro-internal
-EXTRA_DIST = gnuastro.pc.in $(headersdir)/README $(internaldir)/README  \
-  $(internaldir)/arithmetic-and.h $(internaldir)/arithmetic-binary.h    \
+EXTRA_DIST = gnuastro.pc.in $(headersdir)/README $(internaldir)/README \
+  $(internaldir)/arithmetic-and.h $(internaldir)/arithmetic-binary.h \
   $(internaldir)/arithmetic-bitand.h $(internaldir)/arithmetic-bitlsh.h \
-  $(internaldir)/arithmetic-bitor.h $(internaldir)/arithmetic-bitrsh.h  \
+  $(internaldir)/arithmetic-bitor.h $(internaldir)/arithmetic-bitrsh.h \
   $(internaldir)/arithmetic-bitxor.h $(internaldir)/arithmetic-divide.h \
-  $(internaldir)/arithmetic-eq.h $(internaldir)/arithmetic-ge.h         \
-  $(internaldir)/arithmetic-gt.h $(internaldir)/arithmetic-internal.h   \
-  $(internaldir)/arithmetic-le.h $(internaldir)/arithmetic-lt.h         \
-  $(internaldir)/arithmetic-minus.h $(internaldir)/arithmetic-modulo.h  \
-  $(internaldir)/arithmetic-multiply.h $(internaldir)/arithmetic-ne.h   \
-  $(internaldir)/arithmetic-or.h $(internaldir)/arithmetic-plus.h       \
-  $(internaldir)/checkset.h $(internaldir)/commonopts.h                 \
-  $(internaldir)/config.h.in $(internaldir)/fixedstringmacros.h         \
-  $(internaldir)/options.h $(internaldir)/tableintern.h                 \
-  $(internaldir)/tile-internal.h $(internaldir)/timing.h
+  $(internaldir)/arithmetic-eq.h $(internaldir)/arithmetic-ge.h \
+  $(internaldir)/arithmetic-gt.h $(internaldir)/arithmetic-internal.h \
+  $(internaldir)/arithmetic-le.h $(internaldir)/arithmetic-lt.h \
+  $(internaldir)/arithmetic-minus.h $(internaldir)/arithmetic-modulo.h \
+  $(internaldir)/arithmetic-multiply.h $(internaldir)/arithmetic-ne.h \
+  $(internaldir)/arithmetic-or.h $(internaldir)/arithmetic-plus.h \
+  $(internaldir)/arithmetic-set.h $(internaldir)/checkset.h \
+  $(internaldir)/commonopts.h $(internaldir)/config.h.in \
+  $(internaldir)/fixedstringmacros.h $(internaldir)/options.h \
+  $(internaldir)/tableintern.h $(internaldir)/tile-internal.h \
+  $(internaldir)/timing.h $(internaldir)/wcsdistortion.h
 
 
 
@@ -125,18 +138,22 @@ CLEANFILES = gnuastro.pc gnuastro/config.h
 
 
 
-# Build `gnuastro/config.h' based on the information in the Makefile after
+# Build 'gnuastro/config.h' based on the information in the Makefile after
 # the Makefile has been built.
 gnuastro/config.h: Makefile $(internaldir)/config.h.in
        rm -f $@ $@.tmp
        $(MKDIR_P) gnuastro
-       $(SED) -e 's|@VERSION[@]|$(VERSION)|g'                            \
-              -e 's|@HAVE_LIBGIT2[@]|$(HAVE_LIBGIT2)|g'                  \
-              -e 's|@HAVE_WCSLIB_VERSION[@]|$(HAVE_WCSLIB_VERSION)|g'    \
-              -e 's|@HAVE_PTHREAD_BARRIER[@]|$(HAVE_PTHREAD_BARRIER)|g'  \
-              -e 's|@SIZEOF_LONG[@]|$(SIZEOF_LONG)|g'                    \
-              -e 's|@SIZEOF_SIZE_T[@]|$(SIZEOF_SIZE_T)|g'                \
-              -e 's|@RESTRICT_REPLACEMENT[@]|$(RESTRICT_REPLACEMENT)|g'  \
+       $(SED) -e 's|@VERSION[@]|$(VERSION)|g' \
+              -e 's|@SIZEOF_LONG[@]|$(SIZEOF_LONG)|g' \
+              -e 's|@HAVE_LIBGIT2[@]|$(HAVE_LIBGIT2)|g' \
+              -e 's|@SIZEOF_SIZE_T[@]|$(SIZEOF_SIZE_T)|g' \
+              -e 's|@HAVE_WCSLIB_DIS_H[@]|$(HAVE_WCSLIB_DIS_H)|g' \
+              -e 's|@HAVE_WCSLIB_MJDREF[@]|$(HAVE_WCSLIB_MJDREF)|g' \
+              -e 's|@HAVE_WCSLIB_OBSFIX[@]|$(HAVE_WCSLIB_OBSFIX)|g' \
+              -e 's|@HAVE_WCSLIB_VERSION[@]|$(HAVE_WCSLIB_VERSION)|g' \
+              -e 's|@HAVE_PTHREAD_BARRIER[@]|$(HAVE_PTHREAD_BARRIER)|g' \
+              -e 's|@RESTRICT_REPLACEMENT[@]|$(RESTRICT_REPLACEMENT)|g' \
+              -e 's|@HAVE_FITS_IS_REENTRANT[@]|$(HAVE_FITS_IS_REENTRANT)|g' \
                $(internaldir)/config.h.in >> $@.tmp
        chmod a-w $@.tmp
        mv $@.tmp $@
@@ -145,16 +162,21 @@ gnuastro/config.h: Makefile $(internaldir)/config.h.in
 
 
 
-# Build Gnuastro's pkg-config file similar to `gnuastro/config.h'.
+# Build Gnuastro's pkg-config file similar to 'gnuastro/config.h'.
 gnuastro.pc: Makefile $(srcdir)/gnuastro.pc.in
        rm -f $@ $@.tmp
-       $(SED)                                      \
-       -e 's|@prefix[@]|$(prefix)|g'               \
-       -e 's|@exec_prefix[@]|$(exec_prefix)|g'     \
-       -e 's|@libdir[@]|$(libdir)|g'               \
-       -e 's|@includedir[@]|$(includedir)|g'       \
-       -e 's|@LIBS[@]|$(LIBS)|g'                   \
-       -e 's|@VERSION[@]|$(VERSION)|g'             \
-       '$(srcdir)/$@.in' >> $@.tmp
+       ol=""; \
+       if [ x"$(HAVE_LIBJPEG)" = xyes ]; then ol="$$ol libjpeg"; fi; \
+       if [ x"$(HAVE_LIBLZMA)" = xyes ]; then ol="$$ol liblzma"; fi; \
+       if [ x"$(HAVE_LIBGIT2)" = xyes ]; then ol="$$ol libgit2"; fi; \
+       if [ x"$(HAVE_LIBTIFF)" = xyes ]; then ol="$$ol libtiff-4"; fi; \
+       $(SED) -e's|@prefix[@]|$(prefix)|g' \
+              -e"s|@optional_libs[@]|$$ol|g" \
+              -e's|@exec_prefix[@]|$(exec_prefix)|g' \
+              -e's|@libdir[@]|$(libdir)|g' \
+              -e's|@includedir[@]|$(includedir)|g' \
+              -e's|@LIBS[@]|$(LIBS)|g' \
+              -e's|@VERSION[@]|$(VERSION)|g' \
+              '$(srcdir)/$@.in' >> $@.tmp
        chmod a-w $@.tmp
        mv $@.tmp $@
diff --git a/lib/arithmetic-and.c b/lib/arithmetic-and.c
index 2eb6999..d87f888 100644
--- a/lib/arithmetic-and.c
+++ b/lib/arithmetic-and.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_and(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-bitand.c b/lib/arithmetic-bitand.c
index 5aa5845..ace79c5 100644
--- a/lib/arithmetic-bitand.c
+++ b/lib/arithmetic-bitand.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT_INT' is defined in `arithmetic-binary.h'. As you see
+/* 'BINARY_SET_LT_INT' is defined in 'arithmetic-binary.h'. As you see
    there, this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_bitand(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-bitlsh.c b/lib/arithmetic-bitlsh.c
index 14ab5c8..0184ca2 100644
--- a/lib/arithmetic-bitlsh.c
+++ b/lib/arithmetic-bitlsh.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT_INT' is defined in `arithmetic-binary.h'. As you see
+/* 'BINARY_SET_LT_INT' is defined in 'arithmetic-binary.h'. As you see
    there, this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_bitlsh(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-bitor.c b/lib/arithmetic-bitor.c
index 4c9625c..eccb0a2 100644
--- a/lib/arithmetic-bitor.c
+++ b/lib/arithmetic-bitor.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT_INT' is defined in `arithmetic-binary.h'. As you see
+/* 'BINARY_SET_LT_INT' is defined in 'arithmetic-binary.h'. As you see
    there, this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_bitor(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-bitrsh.c b/lib/arithmetic-bitrsh.c
index c74c666..8b6d7ff 100644
--- a/lib/arithmetic-bitrsh.c
+++ b/lib/arithmetic-bitrsh.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT_INT' is defined in `arithmetic-binary.h'. As you see
+/* 'BINARY_SET_LT_INT' is defined in 'arithmetic-binary.h'. As you see
    there, this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_bitrsh(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-bitxor.c b/lib/arithmetic-bitxor.c
index e2fd3ce..ea6ba8d 100644
--- a/lib/arithmetic-bitxor.c
+++ b/lib/arithmetic-bitxor.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT_INT' is defined in `arithmetic-binary.h'. As you see
+/* 'BINARY_SET_LT_INT' is defined in 'arithmetic-binary.h'. As you see
    there, this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_bitxor(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-divide.c b/lib/arithmetic-divide.c
index 7838392..9981cdf 100644
--- a/lib/arithmetic-divide.c
+++ b/lib/arithmetic-divide.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_divide(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-eq.c b/lib/arithmetic-eq.c
index 7003924..592cbc9 100644
--- a/lib/arithmetic-eq.c
+++ b/lib/arithmetic-eq.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_eq(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-ge.c b/lib/arithmetic-ge.c
index 66f47ee..db824ea 100644
--- a/lib/arithmetic-ge.c
+++ b/lib/arithmetic-ge.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_ge(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-gt.c b/lib/arithmetic-gt.c
index 353e2b1..7d91d76 100644
--- a/lib/arithmetic-gt.c
+++ b/lib/arithmetic-gt.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_gt(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-le.c b/lib/arithmetic-le.c
index 0457eca..6190d2e 100644
--- a/lib/arithmetic-le.c
+++ b/lib/arithmetic-le.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_le(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-lt.c b/lib/arithmetic-lt.c
index 3c0f1ae..fa7601a 100644
--- a/lib/arithmetic-lt.c
+++ b/lib/arithmetic-lt.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_lt(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-minus.c b/lib/arithmetic-minus.c
index bbd083d..25ca24f 100644
--- a/lib/arithmetic-minus.c
+++ b/lib/arithmetic-minus.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_minus(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-modulo.c b/lib/arithmetic-modulo.c
index 93fa0e4..428da70 100644
--- a/lib/arithmetic-modulo.c
+++ b/lib/arithmetic-modulo.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT_INT' is defined in `arithmetic-binary.h'. As you see
+/* 'BINARY_SET_LT_INT' is defined in 'arithmetic-binary.h'. As you see
    there, this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_modulo(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-multiply.c b/lib/arithmetic-multiply.c
index 28e784b..a6ad4b6 100644
--- a/lib/arithmetic-multiply.c
+++ b/lib/arithmetic-multiply.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_multiply(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-ne.c b/lib/arithmetic-ne.c
index c695f8f..3030a3d 100644
--- a/lib/arithmetic-ne.c
+++ b/lib/arithmetic-ne.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_ne(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-or.c b/lib/arithmetic-or.c
index 6117006..5c9a884 100644
--- a/lib/arithmetic-or.c
+++ b/lib/arithmetic-or.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_or(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-plus.c b/lib/arithmetic-plus.c
index abe84cb..e24b003 100644
--- a/lib/arithmetic-plus.c
+++ b/lib/arithmetic-plus.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -36,11 +36,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* `BINARY_SET_LT' is defined in `arithmetic-binary.h'. As you see there,
+/* 'BINARY_SET_LT' is defined in 'arithmetic-binary.h'. As you see there,
    this is a deep macro (calls other macros) to deal with different
    types. This allows efficiency in processing (after compilation), but
    compilation will be very slow. Therefore, for each operator we have
-   defined a separate `.c' file so they are built separately and when built
+   defined a separate '.c' file so they are built separately and when built
    in parallel can be much faster than having them all in a single file. */
 void
 arithmetic_plus(gal_data_t *l, gal_data_t *r, gal_data_t *o)
diff --git a/lib/arithmetic-set.c b/lib/arithmetic-set.c
new file mode 100644
index 0000000..24da3cb
--- /dev/null
+++ b/lib/arithmetic-set.c
@@ -0,0 +1,193 @@
+/*********************************************************************
+Arithmetic operations on data structures.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2021, 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 <string.h>
+
+#include <gnuastro/list.h>
+
+#include <gnuastro-internal/checkset.h>
+#include <gnuastro-internal/arithmetic-set.h>
+
+
+
+
+
+/* Remove a name from the list of names and return the dataset it points
+   to. */
+static gal_data_t *
+arithmetic_set_remove_name(struct gal_arithmetic_set_params *p,
+                           char *name)
+{
+  gal_data_t *tmp, *removed=NULL, *prev=NULL;
+
+  /* Go over all the given names. */
+  for(tmp=p->named;tmp!=NULL;tmp=tmp->next)
+    {
+      if( !strcmp(tmp->name, name) )
+        {
+          removed=tmp;
+          if(prev) prev->next = tmp->next;
+          else     p->named   = tmp->next;
+        }
+
+      /* Set this node as the 'prev' pointer. */
+      prev=tmp;
+    }
+
+  /* A small sanity check. */
+  if(removed==NULL)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+          "fix the problem. 'removed' must not be NULL at this point",
+          __func__, PACKAGE_BUGREPORT);
+
+  /* Nothing in the list points to it now. So we can safely modify and
+     return it. */
+  free(removed->name);
+  removed->next=NULL;
+  removed->name=NULL;
+  return removed;
+}
+
+
+
+
+
+/* Pop a dataset and keep it in the 'named' list for later use. */
+void
+gal_arithmetic_set_name(struct gal_arithmetic_set_params *p, char *token)
+{
+  gal_data_t *tmp, *tofree;
+  char *varname=&token[ GAL_ARITHMETIC_SET_PREFIX_LENGTH ];
+
+  /* If a dataset with this name already exists, it will be removed/deleted
+     so we can use the name for the newly designated dataset. */
+  for(tmp=p->named; tmp!=NULL; tmp=tmp->next)
+    if( !strcmp(varname, tmp->name) )
+      {
+        tofree=arithmetic_set_remove_name(p, varname);
+        gal_data_free(tofree);
+
+        /* IMPORTANT: we MUST break here! 'tmp' does't point to the right
+           place any more. We can define a 'prev' node and modify it on
+           every attempt, but since there is only one dataset with a given
+           name, that is redundant and will just make the program slow. */
+        break;
+      }
+
+  /* Pop the top operand, then add it to the list of named datasets, but
+     only if it is used in later tokens. If it isn't, free the popped
+     dataset. The latter case (to define a name, but not use it), is
+     obviously a redundant operation, but that is upto the user, we
+     shouldn't worry about it here. We should just have everything in
+     place, so no crashes occur or no extra memory is consumed. */
+  if( p->used_later(p, varname) )
+    {
+      /* Add the top popped operand to the list of names. */
+      gal_list_data_add(&p->named, p->pop(p));
+
+      /* Write the requested name into this dataset. But note that 'name'
+         MUST be already empty. So to be safe, we'll do a sanity check. */
+      if(p->named->name )
+        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+              "fix the problem. The 'name' element should be NULL at "
+              "this point, but it isn't", __func__, PACKAGE_BUGREPORT);
+      if(p->named->unit)    { free(p->named->unit);    p->named->unit=NULL;    
}
+      if(p->named->comment) { free(p->named->comment); p->named->comment=NULL; 
}
+      gal_checkset_allocate_copy(varname, &p->named->name);
+    }
+  else
+    {
+      /* Pop the top operand, then free it: for example the user has ran
+         'set-i', but forgot to actually use it (happens a lot due to human
+         error!). */
+      tmp=p->pop(p);
+      gal_data_free(tmp);
+    }
+}
+
+
+
+
+
+/* See if a given token is the name of a variable. */
+int
+gal_arithmetic_set_is_name(gal_data_t *named, char *token)
+{
+  gal_data_t *tmp;
+
+  /* Make sure the variable name hasn't been set before. */
+  for(tmp=named; tmp!=NULL; tmp=tmp->next)
+    if( !strcmp(token, tmp->name) )
+      return 1;
+
+  /* If control reaches here, then there was no match*/
+  return 0;
+}
+
+
+
+
+
+/* Return a copy of the named dataset. */
+gal_data_t *
+gal_arithmetic_set_copy_named(struct gal_arithmetic_set_params *p,
+                              char *name)
+{
+  gal_data_t *out=NULL, *tmp;
+
+  /* Find the proper named element to use. */
+  for(tmp=p->named;tmp!=NULL;tmp=tmp->next)
+    {
+    if( !strcmp(tmp->name, name) )
+      {
+        /* If the named operand is used later, then copy it into the
+           output. */
+        if( p->used_later(p, name) )
+          {
+            out=gal_data_copy(tmp);
+            out->next=NULL;
+            if(out->name)    { free(out->name);    out->name=NULL;    }
+            if(out->unit)    { free(out->unit);    out->unit=NULL;    }
+            if(out->comment) { free(out->comment); out->comment=NULL; }
+          }
+
+        /* The named operand is not used any more. Remove it from the list
+           of named datasets and continue. */
+        else out=arithmetic_set_remove_name(p, name);
+      }
+    }
+
+  /* A small sanity check. */
+  if(out==NULL)
+    error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to fix the "
+          "problem. The requested name '%s' couldn't be found in the list",
+          __func__, PACKAGE_BUGREPORT, name);
+
+  /* Return. */
+  return out;
+}
diff --git a/lib/arithmetic.c b/lib/arithmetic.c
index d6d1378..e7f68d9 100644
--- a/lib/arithmetic.c
+++ b/lib/arithmetic.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -31,6 +31,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #include <gnuastro/list.h>
 #include <gnuastro/blank.h>
+#include <gnuastro/units.h>
 #include <gnuastro/qsort.h>
 #include <gnuastro/pointer.h>
 #include <gnuastro/threads.h>
@@ -78,7 +79,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Some functions are only for a floating point operand, so if the input
    isn't floating point, inform the user to change the type explicitly,
    doing it implicitly/internally puts too much responsability on the
-   program. */
+   program.
 static void
 arithmetic_check_float_input(gal_data_t *in, int operator, char *numstr)
 {
@@ -90,17 +91,17 @@ arithmetic_check_float_input(gal_data_t *in, int operator, 
char *numstr)
     default:
       error(EXIT_FAILURE, 0, "the %s operator can only accept single or "
             "double precision floating point numbers as its operand. The "
-            "%s operand has type %s. You can use the `float' or `double' "
+            "%s operand has type %s. You can use the 'float' or 'double' "
             "operators before this operator to explicitly convert to the "
             "desired precision floating point type. If the operand was "
-            "originally a typed number (string of characters), add an `f' "
+            "originally a typed number (string of characters), add an 'f' "
             "after it so it is directly read into the proper precision "
             "floating point number (based on the number of non-zero "
             "decimals it has)", gal_arithmetic_operator_string(operator),
             numstr, gal_type_name(in->type, 1));
     }
 }
-
+*/
 
 
 
@@ -352,103 +353,163 @@ arithmetic_abs(int flags, gal_data_t *in)
 
 
 
-#define UNIFUNC_RUN_FUNCTION_ON_ELEMENT(OT, IT, OP){                    \
+/* Wrapper functions for RA/Dec strings. */
+static char *
+arithmetic_units_degree_to_ra(double decimal)
+{ return gal_units_degree_to_ra(decimal, 0); }
+
+static char *
+arithmetic_units_degree_to_dec(double decimal)
+{ return gal_units_degree_to_dec(decimal, 0); }
+
+#define UNIFUNC_RUN_FUNCTION_ON_ELEMENT(OT, IT, OP, BEFORE, AFTER){     \
     OT *oa=o->array;                                                    \
     IT *ia=in->array, *iaf=ia + in->size;                               \
-    do *oa++ = OP(*ia++); while(ia<iaf);                                \
+    do *oa++ = OP( *ia++ BEFORE ) AFTER; while(ia<iaf);                 \
   }
 
-#define UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(IT, OP)                   \
+#define UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(IT, OP, BEFORE, AFTER)    \
   switch(o->type)                                                       \
     {                                                                   \
     case GAL_TYPE_UINT8:                                                \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(uint8_t,  IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(uint8_t,  IT, OP, BEFORE, AFTER)  \
         break;                                                          \
     case GAL_TYPE_INT8:                                                 \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(int8_t,   IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(int8_t,   IT, OP, BEFORE, AFTER)  \
         break;                                                          \
     case GAL_TYPE_UINT16:                                               \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(uint16_t, IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(uint16_t, IT, OP, BEFORE, AFTER)  \
         break;                                                          \
     case GAL_TYPE_INT16:                                                \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(int16_t,  IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(int16_t,  IT, OP, BEFORE, AFTER)  \
         break;                                                          \
     case GAL_TYPE_UINT32:                                               \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(uint32_t, IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(uint32_t, IT, OP, BEFORE, AFTER)  \
         break;                                                          \
     case GAL_TYPE_INT32:                                                \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(int32_t,  IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(int32_t,  IT, OP, BEFORE, AFTER)  \
         break;                                                          \
     case GAL_TYPE_UINT64:                                               \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(uint64_t, IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(uint64_t, IT, OP, BEFORE, AFTER)  \
         break;                                                          \
     case GAL_TYPE_INT64:                                                \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(int64_t,  IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(int64_t,  IT, OP, BEFORE, AFTER)  \
         break;                                                          \
     case GAL_TYPE_FLOAT32:                                              \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(float,    IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(float,    IT, OP, BEFORE, AFTER)  \
       break;                                                            \
     case GAL_TYPE_FLOAT64:                                              \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(double,   IT, OP)                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(double,   IT, OP, BEFORE, AFTER)  \
       break;                                                            \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: type code %d not recognized",         \
             "UNIARY_FUNCTION_ON_ELEMENT", in->type);                    \
     }
 
-#define UNIARY_FUNCTION_ON_ELEMENT(OP)                                  \
+#define UNIARY_FUNCTION_ON_ELEMENT_OUTPUT_STRING(OP)     \
   switch(in->type)                                                      \
     {                                                                   \
     case GAL_TYPE_UINT8:                                                \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(uint8_t,  OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, uint8_t,  OP, +0, +0)     \
         break;                                                          \
     case GAL_TYPE_INT8:                                                 \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(int8_t,   OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, int8_t,   OP, +0, +0)     \
         break;                                                          \
     case GAL_TYPE_UINT16:                                               \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(uint16_t, OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, uint16_t, OP, +0, +0)     \
         break;                                                          \
     case GAL_TYPE_INT16:                                                \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(int16_t,  OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, int16_t,  OP, +0, +0)     \
         break;                                                          \
     case GAL_TYPE_UINT32:                                               \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(uint32_t, OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, uint32_t, OP, +0, +0)     \
         break;                                                          \
     case GAL_TYPE_INT32:                                                \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(int32_t,  OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, int32_t,  OP, +0, +0)     \
         break;                                                          \
     case GAL_TYPE_UINT64:                                               \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(uint64_t, OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, uint64_t, OP, +0, +0)     \
         break;                                                          \
     case GAL_TYPE_INT64:                                                \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(int64_t,  OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, int64_t,  OP, +0, +0)     \
         break;                                                          \
     case GAL_TYPE_FLOAT32:                                              \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(float,    OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, float,    OP, +0, +0)     \
+        break;                                                          \
+    case GAL_TYPE_FLOAT64:                                              \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT(char *, double,   OP, +0, +0)     \
+        break;                                                          \
+    default:                                                            \
+      error(EXIT_FAILURE, 0, "%s: type code %d not recognized",         \
+            "UNIARY_FUNCTION_ON_ELEMENT_OUTPUT_STRING", in->type);      \
+    }
+
+#define UNIARY_FUNCTION_ON_ELEMENT(OP, BEFORE, AFTER)                   \
+  switch(in->type)                                                      \
+    {                                                                   \
+    case GAL_TYPE_UINT8:                                                \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(uint8_t,  OP, BEFORE, AFTER) \
+        break;                                                          \
+    case GAL_TYPE_INT8:                                                 \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(int8_t,   OP, BEFORE, AFTER) \
+        break;                                                          \
+    case GAL_TYPE_UINT16:                                               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(uint16_t, OP, BEFORE, AFTER) \
+        break;                                                          \
+    case GAL_TYPE_INT16:                                                \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(int16_t,  OP, BEFORE, AFTER) \
+        break;                                                          \
+    case GAL_TYPE_UINT32:                                               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(uint32_t, OP, BEFORE, AFTER) \
+        break;                                                          \
+    case GAL_TYPE_INT32:                                                \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(int32_t,  OP, BEFORE, AFTER) \
+        break;                                                          \
+    case GAL_TYPE_UINT64:                                               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(uint64_t, OP, BEFORE, AFTER) \
+        break;                                                          \
+    case GAL_TYPE_INT64:                                                \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(int64_t,  OP, BEFORE, AFTER) \
+        break;                                                          \
+    case GAL_TYPE_FLOAT32:                                              \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(float,    OP, BEFORE, AFTER) \
       break;                                                            \
     case GAL_TYPE_FLOAT64:                                              \
-      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(double,   OP)               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(double,   OP, BEFORE, AFTER) \
       break;                                                            \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: type code %d not recognized",         \
             "UNIARY_FUNCTION_ON_ELEMENT", in->type);                    \
     }
 
+
+#define UNIFUNC_RUN_FUNCTION_ON_ELEMENT_STRING(OT, OP){                 \
+    OT *oa=o->array;                                                    \
+    char **ia=in->array, **iaf=ia + in->size;                           \
+    do *oa++ = OP(*ia++); while(ia<iaf);                                \
+}
+
 static gal_data_t *
-arithmetic_unary_function(int operator, int flags, gal_data_t *in)
+arithmetic_function_unary(int operator, int flags, gal_data_t *in)
 {
   uint8_t otype;
   int inplace=0;
   gal_data_t *o;
+  double pi=3.14159265358979323846264338327;
 
-  /* See if the operation should be done in place. Note that so far, the
-     output of these operators is defined in the real space (floating
-     point). So even if the user requested inplace opereation, if its not a
-     floating point type, its not useful.*/
+  /* See if the operation should be done in place. The output of these
+     operators is defined in the floating point space. So even if the input
+     is integer type and user requested inplace opereation, if its not a
+     floating point type, it will not be in-place. */
   if( (flags & GAL_ARITHMETIC_INPLACE)
-      && (in->type==GAL_TYPE_FLOAT32 || in->type==GAL_TYPE_FLOAT64) )
+      && ( in->type==GAL_TYPE_FLOAT32 || in->type==GAL_TYPE_FLOAT64 )
+      && ( operator != GAL_ARITHMETIC_OP_RA_TO_DEGREE
+      &&   operator != GAL_ARITHMETIC_OP_DEC_TO_DEGREE
+      &&   operator != GAL_ARITHMETIC_OP_DEGREE_TO_RA
+      &&   operator != GAL_ARITHMETIC_OP_DEGREE_TO_DEC ) )
     inplace=1;
 
+  /* Set the output pointer. */
   if(inplace)
     {
       o = in;
@@ -456,9 +517,19 @@ arithmetic_unary_function(int operator, int flags, 
gal_data_t *in)
     }
   else
     {
-      otype = ( in->type==GAL_TYPE_FLOAT64
-                ? GAL_TYPE_FLOAT64
-                : GAL_TYPE_FLOAT32 );
+      /* Check for operators which have fixed output types */
+      if(         operator == GAL_ARITHMETIC_OP_RA_TO_DEGREE
+               || operator == GAL_ARITHMETIC_OP_DEC_TO_DEGREE )
+        otype = GAL_TYPE_FLOAT64;
+      else if(    operator == GAL_ARITHMETIC_OP_DEGREE_TO_RA
+               || operator == GAL_ARITHMETIC_OP_DEGREE_TO_DEC )
+        otype = GAL_TYPE_STRING;
+      else
+        otype = ( in->type==GAL_TYPE_FLOAT64
+                  ? GAL_TYPE_FLOAT64
+                  : GAL_TYPE_FLOAT32 );
+
+      /* Set the final output type. */
       o = gal_data_alloc(NULL, otype, in->ndim, in->dsize, in->wcs,
                          0, in->minmapsize, in->quietmmap,
                          NULL, NULL, NULL);
@@ -468,28 +539,57 @@ arithmetic_unary_function(int operator, int flags, 
gal_data_t *in)
   switch(operator)
     {
     case GAL_ARITHMETIC_OP_SQRT:
-      UNIARY_FUNCTION_ON_ELEMENT( sqrt );
-      break;
-
+      UNIARY_FUNCTION_ON_ELEMENT( sqrt,  +0, +0);         break;
     case GAL_ARITHMETIC_OP_LOG:
-      UNIARY_FUNCTION_ON_ELEMENT( log );
-      break;
-
+      UNIARY_FUNCTION_ON_ELEMENT( log,   +0, +0);         break;
     case GAL_ARITHMETIC_OP_LOG10:
-      UNIARY_FUNCTION_ON_ELEMENT( log10 );
+      UNIARY_FUNCTION_ON_ELEMENT( log10, +0, +0);         break;
+    case GAL_ARITHMETIC_OP_SIN:
+      UNIARY_FUNCTION_ON_ELEMENT( sin,   *pi/180.0f, +0); break;
+    case GAL_ARITHMETIC_OP_COS:
+      UNIARY_FUNCTION_ON_ELEMENT( cos,   *pi/180.0f, +0); break;
+    case GAL_ARITHMETIC_OP_TAN:
+      UNIARY_FUNCTION_ON_ELEMENT( tan,   *pi/180.0f, +0); break;
+    case GAL_ARITHMETIC_OP_ASIN:
+      UNIARY_FUNCTION_ON_ELEMENT( asin,  +0, *180.0f/pi); break;
+    case GAL_ARITHMETIC_OP_ACOS:
+      UNIARY_FUNCTION_ON_ELEMENT( acos,  +0, *180.0f/pi); break;
+    case GAL_ARITHMETIC_OP_ATAN:
+      UNIARY_FUNCTION_ON_ELEMENT( atan,  +0, *180.0f/pi); break;
+    case GAL_ARITHMETIC_OP_SINH:
+      UNIARY_FUNCTION_ON_ELEMENT( sinh,  +0, +0);         break;
+    case GAL_ARITHMETIC_OP_COSH:
+      UNIARY_FUNCTION_ON_ELEMENT( cosh,  +0, +0);         break;
+    case GAL_ARITHMETIC_OP_TANH:
+      UNIARY_FUNCTION_ON_ELEMENT( tanh,  +0, +0);         break;
+    case GAL_ARITHMETIC_OP_ASINH:
+      UNIARY_FUNCTION_ON_ELEMENT( asinh, +0, +0);         break;
+    case GAL_ARITHMETIC_OP_ACOSH:
+      UNIARY_FUNCTION_ON_ELEMENT( acosh, +0, +0);         break;
+    case GAL_ARITHMETIC_OP_ATANH:
+      UNIARY_FUNCTION_ON_ELEMENT( atanh, +0, +0);         break;
+    case GAL_ARITHMETIC_OP_RA_TO_DEGREE:
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_STRING(double, gal_units_ra_to_degree);
+      break;
+    case GAL_ARITHMETIC_OP_DEC_TO_DEGREE:
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_STRING(double, gal_units_dec_to_degree);
+      break;
+    case GAL_ARITHMETIC_OP_DEGREE_TO_RA:
+      UNIARY_FUNCTION_ON_ELEMENT_OUTPUT_STRING(arithmetic_units_degree_to_ra);
+      break;
+    case GAL_ARITHMETIC_OP_DEGREE_TO_DEC:
+      UNIARY_FUNCTION_ON_ELEMENT_OUTPUT_STRING(arithmetic_units_degree_to_dec);
       break;
-
     default:
       error(EXIT_FAILURE, 0, "%s: operator code %d not recognized",
             __func__, operator);
     }
 
-
   /* Clean up. Note that if the input arrays can be freed, and any of right
-     or left arrays needed conversion, `UNIFUNC_CONVERT_TO_COMPILED_TYPE'
-     has already freed the input arrays, and we only have `r' and `l'
+     or left arrays needed conversion, 'UNIFUNC_CONVERT_TO_COMPILED_TYPE'
+     has already freed the input arrays, and we only have 'r' and 'l'
      allocated in any case. Alternatively, when the inputs shouldn't be
-     freed, the only allocated spaces are the `r' and `l' arrays if their
+     freed, the only allocated spaces are the 'r' and 'l' arrays if their
      types weren't compiled for binary operations, we can tell this from
      the pointers: if they are different from the original pointers, they
      were allocated. */
@@ -504,7 +604,7 @@ arithmetic_unary_function(int operator, int flags, 
gal_data_t *in)
 
 
 
-/* Call functions in the `gnuastro/statistics' library. */
+/* Call functions in the 'gnuastro/statistics' library. */
 static gal_data_t *
 arithmetic_from_statistics(int operator, int flags, gal_data_t *input)
 {
@@ -551,9 +651,93 @@ arithmetic_from_statistics(int operator, int flags, 
gal_data_t *input)
 
 
 /***********************************************************************/
+/***************                  Metadata                **************/
+/***********************************************************************/
+
+/* The size operator. Reports the size along a given dimension. */
+static gal_data_t *
+arithmetic_size(int operator, int flags, gal_data_t *in, gal_data_t *arg)
+{
+  size_t one=1, arg_val;
+  gal_data_t *usearg=NULL, *out=NULL;
+
+  /* Sanity checks on argument (dimension number): it should be an integer,
+     and have a size of 1. */
+  if(arg->type==GAL_TYPE_FLOAT32 || arg->type==GAL_TYPE_FLOAT64)
+    error(EXIT_FAILURE, 0, "%s: size operator's dimension argument"
+          "must have an integer type", __func__);
+  if(arg->size!=1)
+    error(EXIT_FAILURE, 0, "%s: size operator's dimension argument"
+          "must be a single number, but it has %zu elements", __func__,
+          arg->size);
+
+
+  /* Convert 'arg' to 'size_t' and read it. Note that we can only free the
+     'arg' array (while changing its type), when the freeing flag has been
+     set. */
+  if(flags & GAL_ARITHMETIC_FREE)
+    {
+      arg=gal_data_copy_to_new_type_free(arg, GAL_TYPE_SIZE_T);
+      arg_val=*(size_t *)(arg->array);
+      gal_data_free(arg);
+    }
+  else
+    {
+      usearg=gal_data_copy_to_new_type(arg, GAL_TYPE_SIZE_T);
+      arg_val=*(size_t *)(usearg->array);
+      gal_data_free(usearg);
+    }
+
+
+  /* Sanity checks on the value of the given argument.*/
+  if(arg_val>in->ndim)
+    error(EXIT_FAILURE, 0, "%s: size operator's dimension argument "
+          "(given %zu) cannot be larger than the dimensions of the "
+          "given input (%zu)", __func__, arg_val, in->ndim);
+  if(arg_val==0)
+    error(EXIT_FAILURE, 0, "%s: size operator's dimension argument "
+          "(given %zu) cannot be zero: dimensions are counted from 1",
+          __func__, arg_val);
+
+
+  /* Allocate the output array and write the desired dimension. Note that
+     'dsize' is in the C order, while the output must be in FITS/Fortran
+     order. Also that C order starts from 0, while the FITS order starts
+     from 1. */
+  out=gal_data_alloc(NULL, GAL_TYPE_SIZE_T, 1, &one, NULL, 0,
+                     in->minmapsize, 0, NULL, NULL, NULL);
+  *(size_t *)(out->array)=in->dsize[in->ndim-arg_val];
+
+
+  /* Clean up and return */
+  if(flags & GAL_ARITHMETIC_FREE)
+    gal_data_free(in);
+  return out;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/***********************************************************************/
 /***************                  Where                   **************/
 /***********************************************************************/
-/* When the `iftrue' dataset only has one element and the element is blank,
+/* When the 'iftrue' dataset only has one element and the element is blank,
    then it will be replaced with the blank value of the type of the output
    data. */
 #define DO_WHERE_OPERATION(ITT, OT) {                                   \
@@ -595,7 +779,7 @@ arithmetic_from_statistics(int operator, int flags, 
gal_data_t *input)
     case GAL_TYPE_FLOAT64:  DO_WHERE_OPERATION( double,   OT);  break;  \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: type code %d not recognized for the " \
-            "`iftrue' dataset", "WHERE_OUT_SET", iftrue->type);         \
+            "'iftrue' dataset", "WHERE_OUT_SET", iftrue->type);         \
     }
 
 
@@ -612,14 +796,14 @@ arithmetic_where(int flags, gal_data_t *out, gal_data_t 
*cond,
   /* The condition operator has to be unsigned char. */
   if(cond->type!=GAL_TYPE_UINT8)
     error(EXIT_FAILURE, 0, "%s: the condition operand must be an "
-          "`uint8' type, but the given condition operand has a "
-          "`%s' type", __func__, gal_type_name(cond->type, 1));
+          "'uint8' type, but the given condition operand has a "
+          "'%s' type", __func__, gal_type_name(cond->type, 1));
 
   /* The dimension and sizes of the out and condition data sets must be the
      same. */
   if( gal_dimension_is_different(out, cond) )
-    error(EXIT_FAILURE, 0, "%s: the output and condition data sets of the "
-          "must be the same size", __func__);
+    error(EXIT_FAILURE, 0, "%s: the output and condition datasets "
+          "must have the same size", __func__);
 
   /* See if the condition array has blank values. */
   chb=gal_blank_present(cond, 0);
@@ -638,7 +822,7 @@ arithmetic_where(int flags, gal_data_t *out, gal_data_t 
*cond,
     case GAL_TYPE_FLOAT32:       WHERE_OUT_SET( float    );      break;
     case GAL_TYPE_FLOAT64:       WHERE_OUT_SET( double   );      break;
     default:
-      error(EXIT_FAILURE, 0, "%s: type code %d not recognized for the `out'",
+      error(EXIT_FAILURE, 0, "%s: type code %d not recognized for the 'out'",
             __func__, out->type);
     }
 
@@ -695,7 +879,7 @@ struct multioperandparams
     /* Go over all the pixels assigned to this thread. */               \
     for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
       {                                                                 \
-        /* Initialize, `j' is desired pixel's index. */                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
         n=0;                                                            \
         t=max;                                                          \
         j=tprm->indexs[tind];                                           \
@@ -725,7 +909,7 @@ struct multioperandparams
     /* Go over all the pixels assigned to this thread. */               \
     for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
       {                                                                 \
-        /* Initialize, `j' is desired pixel's index. */                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
         n=0;                                                            \
         t=min;                                                          \
         j=tprm->indexs[tind];                                           \
@@ -755,7 +939,7 @@ struct multioperandparams
     /* Go over all the pixels assigned to this thread. */               \
     for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
       {                                                                 \
-        /* Initialize, `j' is desired pixel's index. */                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
         n=0;                                                            \
         j=tprm->indexs[tind];                                           \
                                                                         \
@@ -788,7 +972,7 @@ struct multioperandparams
     /* Go over all the pixels assigned to this thread. */               \
     for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
       {                                                                 \
-        /* Initialize, `j' is desired pixel's index. */                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
         n=0;                                                            \
         sum=0.0f;                                                       \
         j=tprm->indexs[tind];                                           \
@@ -822,7 +1006,7 @@ struct multioperandparams
     /* Go over all the pixels assigned to this thread. */               \
     for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
       {                                                                 \
-        /* Initialize, `j' is desired pixel's index. */                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
         n=0;                                                            \
         sum=0.0f;                                                       \
         j=tprm->indexs[tind];                                           \
@@ -856,7 +1040,7 @@ struct multioperandparams
     /* Go over all the pixels assigned to this thread. */               \
     for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
       {                                                                 \
-        /* Initialize, `j' is desired pixel's index. */                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
         n=0;                                                            \
         sum=sum2=0.0f;                                                  \
         j=tprm->indexs[tind];                                           \
@@ -896,11 +1080,11 @@ struct multioperandparams
     /* Go over all the pixels assigned to this thread. */               \
     for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
       {                                                                 \
-        /* Initialize, `j' is desired pixel's index. */                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
         n=0;                                                            \
         j=tprm->indexs[tind];                                           \
                                                                         \
-        /* Loop over each array: `i' is input dataset's index. */       \
+        /* Loop over each array: 'i' is input dataset's index. */       \
         for(i=0;i<p->dnum;++i)                                          \
           {                                                             \
             /* Only integers and non-NaN floats: v==v is 1. */          \
@@ -932,6 +1116,51 @@ struct multioperandparams
 
 
 
+#define MULTIOPERAND_QUANTILE(TYPE) {                                   \
+    size_t n, j;                                                        \
+    gal_data_t *quantile;                                               \
+    TYPE *o=p->out->array;                                              \
+    TYPE *pixs=gal_pointer_allocate(p->list->type, p->dnum, 0,          \
+                                    __func__, "pixs");                  \
+    gal_data_t *cont=gal_data_alloc(pixs, p->list->type, 1, &p->dnum,   \
+                                    NULL, 0, -1, 1, NULL, NULL, NULL);  \
+                                                                        \
+    /* Go over all the pixels assigned to this thread. */               \
+    for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
+      {                                                                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
+        n=0;                                                            \
+        j=tprm->indexs[tind];                                           \
+                                                                        \
+        /* Read the necessay values from each input. */                 \
+        for(i=0;i<p->dnum;++i) pixs[n++]=a[i][j];                       \
+                                                                        \
+        /* If there are any elements, measure the  */                   \
+        if(n)                                                           \
+          {                                                             \
+            /* Calculate the quantile and put it in the output. */      \
+            quantile=gal_statistics_quantile(cont, p->p1, 1);           \
+            memcpy(&o[j], quantile->array,                              \
+                   gal_type_sizeof(p->list->type));                     \
+            gal_data_free(quantile);                                    \
+                                                                        \
+            /* Since we are doing sigma-clipping in place, the size, */ \
+            /* and flags need to be reset. */                           \
+            cont->flag=0;                                               \
+            cont->size=cont->dsize[0]=p->dnum;                          \
+          }                                                             \
+        else                                                            \
+          o[j]=b;                                                       \
+      }                                                                 \
+                                                                        \
+    /* Clean up (note that 'pixs' is inside of 'cont'). */              \
+    gal_data_free(cont);                                                \
+  }
+
+
+
+
+
 #define MULTIOPERAND_SIGCLIP(TYPE) {                                    \
     size_t n, j;                                                        \
     gal_data_t *sclip;                                                  \
@@ -945,7 +1174,7 @@ struct multioperandparams
     /* Go over all the pixels assigned to this thread. */               \
     for(tind=0; tprm->indexs[tind] != GAL_BLANK_SIZE_T; ++tind)         \
       {                                                                 \
-        /* Initialize, `j' is desired pixel's index. */                 \
+        /* Initialize, 'j' is desired pixel's index. */                 \
         n=0;                                                            \
         j=tprm->indexs[tind];                                           \
                                                                         \
@@ -955,6 +1184,7 @@ struct multioperandparams
         /* If there are any elements, measure the  */                   \
         if(n)                                                           \
           {                                                             \
+            /* Calculate the sigma-clip and write it in. */             \
             sclip=gal_statistics_sigma_clip(cont, p->p1, p->p2, 1, 1);  \
             sarr=sclip->array;                                          \
             switch(p->operator)                                         \
@@ -968,6 +1198,7 @@ struct multioperandparams
                       "valid for sigma-clipping results", __func__,     \
                       p->operator);                                     \
               }                                                         \
+            gal_data_free(sclip);                                       \
                                                                         \
             /* Since we are doing sigma-clipping in place, the size, */ \
             /* and flags need to be reset. */                           \
@@ -978,7 +1209,7 @@ struct multioperandparams
           o[j]=b;                                                       \
       }                                                                 \
                                                                         \
-    /* Clean up. */                                                     \
+    /* Clean up (note that 'pixs' is inside of 'cont'). */              \
     gal_data_free(cont);                                                \
   }
 
@@ -990,14 +1221,13 @@ struct multioperandparams
     TYPE b, **a;                                                        \
     gal_data_t *tmp;                                                    \
     size_t i=0, tind;                                                   \
-                                                                        \
     /* Allocate space to keep the pointers to the arrays of each. */    \
     /* Input data structure. The operators will increment these */      \
     /* pointers while parsing them. */                                  \
     errno=0;                                                            \
     a=malloc(p->dnum*sizeof *a);                                        \
     if(a==NULL)                                                         \
-      error(EXIT_FAILURE, 0, "%s: %zu bytes for `a'",                   \
+      error(EXIT_FAILURE, 0, "%s: %zu bytes for 'a'",                   \
             "MULTIOPERAND_TYPE_SET", p->dnum*sizeof *a);                \
                                                                         \
     /* Fill in the array pointers and the blank value for this type. */ \
@@ -1036,6 +1266,10 @@ struct multioperandparams
         MULTIOPERAND_MEDIAN(TYPE, QSORT_F);                             \
         break;                                                          \
                                                                         \
+      case GAL_ARITHMETIC_OP_QUANTILE:                                  \
+        MULTIOPERAND_QUANTILE(TYPE);                                    \
+        break;                                                          \
+                                                                        \
       case GAL_ARITHMETIC_OP_SIGCLIP_STD:                               \
       case GAL_ARITHMETIC_OP_SIGCLIP_MEAN:                              \
       case GAL_ARITHMETIC_OP_SIGCLIP_MEDIAN:                            \
@@ -1113,7 +1347,7 @@ multioperand_on_thread(void *in_prm)
 
 /* The single operator in this function is assumed to be a linked list. The
    number of operators is determined from the fact that the last node in
-   the linked list must have a NULL pointer as its `next' element. */
+   the linked list must have a NULL pointer as its 'next' element. */
 static gal_data_t *
 arithmetic_multioperand(int operator, int flags, gal_data_t *list,
                         gal_data_t *params, size_t numthreads)
@@ -1125,7 +1359,7 @@ arithmetic_multioperand(int operator, int flags, 
gal_data_t *list,
   gal_data_t *out, *tmp, *ttmp;
 
 
-  /* For generality, `list' can be a NULL pointer, in that case, this
+  /* For generality, 'list' can be a NULL pointer, in that case, this
      function will return a NULL pointer and avoid further processing. */
   if(list==NULL) return NULL;
 
@@ -1144,6 +1378,17 @@ arithmetic_multioperand(int operator, int flags, 
gal_data_t *list,
       /* Write them */
       if(isnan(p1)) p1=((float *)(tmp->array))[0];
       else          p2=((float *)(tmp->array))[0];
+
+      /* Operator specific, parameter sanity checks. */
+      switch(operator)
+        {
+        case GAL_ARITHMETIC_OP_QUANTILE:
+          if(p1<0 || p1>1)
+            error(EXIT_FAILURE, 0, "%s: the parameter given to the 'quantile' "
+                  "operator must be between (and including) 0 and 1. The "
+                  "given value is: %g", __func__, p1);
+          break;
+        }
     }
 
 
@@ -1156,13 +1401,13 @@ arithmetic_multioperand(int operator, int flags, 
gal_data_t *list,
 
       /* Check the types. */
       if(tmp->type!=list->type)
-        error(EXIT_FAILURE, 0, "%s: the types of all operands to the %s "
+        error(EXIT_FAILURE, 0, "%s: the types of all operands to the '%s' "
               "operator must be same", __func__,
               gal_arithmetic_operator_string(operator));
 
       /* Check the sizes. */
       if( gal_dimension_is_different(list, tmp) )
-        error(EXIT_FAILURE, 0, "%s: the sizes of all operands to the %s "
+        error(EXIT_FAILURE, 0, "%s: the sizes of all operands to the '%s' "
               "operator must be same", __func__,
               gal_arithmetic_operator_string(operator));
     }
@@ -1178,6 +1423,7 @@ arithmetic_multioperand(int operator, int flags, 
gal_data_t *list,
     case GAL_ARITHMETIC_OP_MEAN:           otype=GAL_TYPE_FLOAT32; break;
     case GAL_ARITHMETIC_OP_STD:            otype=GAL_TYPE_FLOAT32; break;
     case GAL_ARITHMETIC_OP_MEDIAN:         otype=GAL_TYPE_FLOAT32; break;
+    case GAL_ARITHMETIC_OP_QUANTILE:       otype=list->type;       break;
     case GAL_ARITHMETIC_OP_SIGCLIP_STD:    otype=GAL_TYPE_FLOAT32; break;
     case GAL_ARITHMETIC_OP_SIGCLIP_MEAN:   otype=GAL_TYPE_FLOAT32; break;
     case GAL_ARITHMETIC_OP_SIGCLIP_MEDIAN: otype=GAL_TYPE_FLOAT32; break;
@@ -1214,7 +1460,8 @@ arithmetic_multioperand(int operator, int flags, 
gal_data_t *list,
   p.dnum=dnum;
   p.operator=operator;
   p.hasblank=hasblank;
-  gal_threads_spin_off(multioperand_on_thread, &p, out->size, numthreads);
+  gal_threads_spin_off(multioperand_on_thread, &p, out->size, numthreads,
+                       list->minmapsize, list->quietmmap);
 
 
   /* Clean up and return. Note that the operation might have been done in
@@ -1261,7 +1508,7 @@ arithmetic_multioperand(int operator, int flags, 
gal_data_t *list,
    don't need to be checked (the floating point standard will do the job
    for us). It is also not necessary to check blanks in bitwise operators,
    but bitwise operators have their own macro
-   (`BINARY_OP_INCR_OT_RT_LT_SET') which doesn' use `checkblanks'.*/
+   ('BINARY_OP_INCR_OT_RT_LT_SET') which doesn' use 'checkblanks'.*/
 int
 gal_arithmetic_binary_checkblank(gal_data_t *l, gal_data_t *r)
 {
@@ -1280,14 +1527,18 @@ arithmetic_binary_out_type(int operator, gal_data_t *l, 
gal_data_t *r)
 {
   switch(operator)
     {
-    case GAL_ARITHMETIC_OP_PLUS:
-    case GAL_ARITHMETIC_OP_MINUS:
-    case GAL_ARITHMETIC_OP_MULTIPLY:
-    case GAL_ARITHMETIC_OP_DIVIDE:
-      return gal_type_out(l->type, r->type);
+    case GAL_ARITHMETIC_OP_LT:
+    case GAL_ARITHMETIC_OP_LE:
+    case GAL_ARITHMETIC_OP_GT:
+    case GAL_ARITHMETIC_OP_GE:
+    case GAL_ARITHMETIC_OP_EQ:
+    case GAL_ARITHMETIC_OP_NE:
+    case GAL_ARITHMETIC_OP_AND:
+    case GAL_ARITHMETIC_OP_OR:
+      return GAL_TYPE_UINT8;
 
     default:
-      return GAL_TYPE_UINT8;
+      return gal_type_out(l->type, r->type);
     }
   return -1;
 }
@@ -1299,7 +1550,7 @@ arithmetic_binary_out_type(int operator, gal_data_t *l, 
gal_data_t *r)
 static gal_data_t *
 arithmetic_binary(int operator, int flags, gal_data_t *l, gal_data_t *r)
 {
-  /* Read the variable arguments. `lo' and `ro' keep the original data, in
+  /* Read the variable arguments. 'lo' and 'ro' keep the original data, in
      case their type isn't built (based on configure options are configure
      time). */
   int32_t otype;
@@ -1311,16 +1562,16 @@ arithmetic_binary(int operator, int flags, gal_data_t 
*l, gal_data_t *r)
   /* Simple sanity check on the input sizes */
   if( !( (flags & GAL_ARITHMETIC_NUMOK) && (l->size==1 || r->size==1))
       && gal_dimension_is_different(l, r) )
-    error(EXIT_FAILURE, 0, "%s: the non-number inputs to %s don't have the "
-          "same dimension/size", __func__,
+    error(EXIT_FAILURE, 0, "%s: the non-number inputs to '%s' don't "
+          "have the same dimension/size", __func__,
           gal_arithmetic_operator_string(operator));
 
 
   /* Set the output type. For the comparison operators, the output type is
-     either 0 or 1, so we will set the output type to `unsigned char' for
+     either 0 or 1, so we will set the output type to 'unsigned char' for
      efficient memory and CPU usage. Since the number of operators without
-     a fixed output type (like the conditionals) is less, by `default' we
-     will set the output type to `unsigned char', and if any of the other
+     a fixed output type (like the conditionals) is less, by 'default' we
+     will set the output type to 'unsigned char', and if any of the other
      operatrs are given, it will be chosen based on the input types.*/
   otype=arithmetic_binary_out_type(operator, l, r);
 
@@ -1341,8 +1592,8 @@ arithmetic_binary(int operator, int flags, gal_data_t *l, 
gal_data_t *r)
 
 
   /* If the output pointer was not set above for any of the possible
-     reasons, allocate it. For `mmapsize', note that since its `size_t', it
-     will always be positive. The `-1' that is recommended to give when you
+     reasons, allocate it. For 'mmapsize', note that since its 'size_t', it
+     will always be positive. The '-1' that is recommended to give when you
      want the value in RAM is actually the largest possible memory
      location. So we just have to choose the smaller minmapsize of the two
      to decide if the output array should be in RAM or not. */
@@ -1403,24 +1654,24 @@ arithmetic_binary(int operator, int flags, gal_data_t 
*l, gal_data_t *r)
 
 
 
-#define BINFUNC_RUN_FUNCTION(OT, RT, LT, OP){                           \
+#define BINFUNC_RUN_FUNCTION(OT, RT, LT, OP, AFTER){                    \
     LT *la=l->array;                                                    \
     RT *ra=r->array;                                                    \
     OT *oa=o->array, *of=oa + o->size;                                  \
-    if(l->size==r->size) do *oa = OP(*la++, *ra++); while(++oa<of);     \
-    else if(l->size==1)  do *oa = OP(*la,   *ra++); while(++oa<of);     \
-    else                 do *oa = OP(*la++, *ra  ); while(++oa<of);     \
+    if(l->size==r->size) do *oa = OP(*la++, *ra++) AFTER; while(++oa<of); \
+    else if(l->size==1)  do *oa = OP(*la,   *ra++) AFTER; while(++oa<of); \
+    else                 do *oa = OP(*la++, *ra  ) AFTER; while(++oa<of); \
   }
 
 
-#define BINFUNC_F_OPERATOR_LEFT_RIGHT_SET(RT, LT, OP)                   \
+#define BINFUNC_F_OPERATOR_LEFT_RIGHT_SET(RT, LT, OP, AFTER)            \
   switch(o->type)                                                       \
     {                                                                   \
     case GAL_TYPE_FLOAT32:                                              \
-      BINFUNC_RUN_FUNCTION(float, RT, LT, OP);                          \
+      BINFUNC_RUN_FUNCTION(float, RT, LT, OP, AFTER);                   \
       break;                                                            \
     case GAL_TYPE_FLOAT64:                                              \
-      BINFUNC_RUN_FUNCTION(double, RT, LT, OP);                         \
+      BINFUNC_RUN_FUNCTION(double, RT, LT, OP, AFTER);                  \
       break;                                                            \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: type %d not recognized for o->type ", \
@@ -1428,14 +1679,14 @@ arithmetic_binary(int operator, int flags, gal_data_t 
*l, gal_data_t *r)
     }
 
 
-#define BINFUNC_F_OPERATOR_LEFT_SET(LT, OP)                             \
+#define BINFUNC_F_OPERATOR_LEFT_SET(LT, OP, AFTER)                      \
   switch(r->type)                                                       \
     {                                                                   \
     case GAL_TYPE_FLOAT32:                                              \
-      BINFUNC_F_OPERATOR_LEFT_RIGHT_SET(float, LT, OP);                 \
+      BINFUNC_F_OPERATOR_LEFT_RIGHT_SET(float, LT, OP, AFTER);          \
       break;                                                            \
     case GAL_TYPE_FLOAT64:                                              \
-      BINFUNC_F_OPERATOR_LEFT_RIGHT_SET(double, LT, OP);                \
+      BINFUNC_F_OPERATOR_LEFT_RIGHT_SET(double, LT, OP, AFTER);         \
       break;                                                            \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: type %d not recognized for r->type",  \
@@ -1443,14 +1694,14 @@ arithmetic_binary(int operator, int flags, gal_data_t 
*l, gal_data_t *r)
     }
 
 
-#define BINFUNC_F_OPERATOR_SET(OP)                                      \
+#define BINFUNC_F_OPERATOR_SET(OP, AFTER)                               \
   switch(l->type)                                                       \
     {                                                                   \
     case GAL_TYPE_FLOAT32:                                              \
-      BINFUNC_F_OPERATOR_LEFT_SET(float, OP);                           \
+      BINFUNC_F_OPERATOR_LEFT_SET(float, OP, AFTER);                    \
       break;                                                            \
     case GAL_TYPE_FLOAT64:                                              \
-      BINFUNC_F_OPERATOR_LEFT_SET(double, OP);                          \
+      BINFUNC_F_OPERATOR_LEFT_SET(double, OP, AFTER);                   \
       break;                                                            \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: type %d not recognized for l->type",  \
@@ -1459,31 +1710,38 @@ arithmetic_binary(int operator, int flags, gal_data_t 
*l, gal_data_t *r)
 
 
 static gal_data_t *
-arithmetic_binary_function_flt(int operator, int flags, gal_data_t *l,
-                               gal_data_t *r)
+arithmetic_function_binary_flt(int operator, int flags, gal_data_t *il,
+                               gal_data_t *ir)
 {
   int final_otype;
-  gal_data_t *o=NULL;
   size_t out_size, minmapsize;
-  int quietmmap=l->quietmmap && r->quietmmap;
-
+  gal_data_t *l, *r, *o=NULL;
+  double pi=3.14159265358979323846264338327;
+  int quietmmap=il->quietmmap && ir->quietmmap;
 
   /* Simple sanity check on the input sizes */
-  if( !( (flags & GAL_ARITHMETIC_NUMOK) && (l->size==1 || r->size==1))
-      && gal_dimension_is_different(l, r) )
+  if( !( (flags & GAL_ARITHMETIC_NUMOK) && (il->size==1 || ir->size==1))
+      && gal_dimension_is_different(il, ir) )
     error(EXIT_FAILURE, 0, "%s: the input datasets don't have the same "
           "dimension/size", __func__);
 
-  /* Check for the types of the left and right operands. */
-  arithmetic_check_float_input(l, operator, "first");
-  arithmetic_check_float_input(r, operator, "second");
+
+  /* Convert the values to double precision floating point if they are
+     integer. */
+  l = ( (il->type==GAL_TYPE_FLOAT32 || il->type==GAL_TYPE_FLOAT64)
+         ? il : gal_data_copy_to_new_type(il, GAL_TYPE_FLOAT64) );
+  r = ( (ir->type==GAL_TYPE_FLOAT32 || ir->type==GAL_TYPE_FLOAT64)
+         ? ir : gal_data_copy_to_new_type(ir, GAL_TYPE_FLOAT64) );
+
 
   /* Set the output type. */
   final_otype = gal_type_out(l->type, r->type);
 
+
   /* Set the output sizes. */
   minmapsize = ( l->minmapsize < r->minmapsize
-                 ? l->minmapsize : r->minmapsize );
+                 ? l->minmapsize
+                 : r->minmapsize );
   out_size = l->size > r->size ? l->size : r->size;
 
 
@@ -1497,8 +1755,8 @@ arithmetic_binary_function_flt(int operator, int flags, 
gal_data_t *l,
 
 
   /* If the output pointer was not set for any reason, allocate it. For
-     `mmapsize', note that since its `size_t', it will always be
-     Positive. The `-1' that is recommended to give when you want the value
+     'mmapsize', note that since its 'size_t', it will always be
+     Positive. The '-1' that is recommended to give when you want the value
      in RAM is actually the largest possible memory location. So we just
      have to choose the smaller minmapsize of the two to decide if the
      output array should be in RAM or not. */
@@ -1513,7 +1771,10 @@ arithmetic_binary_function_flt(int operator, int flags, 
gal_data_t *l,
   /* Start setting the operator and operands. */
   switch(operator)
     {
-    case GAL_ARITHMETIC_OP_POW:  BINFUNC_F_OPERATOR_SET( pow  ); break;
+    case GAL_ARITHMETIC_OP_POW:
+      BINFUNC_F_OPERATOR_SET( pow,   +0 );         break;
+    case GAL_ARITHMETIC_OP_ATAN2:
+      BINFUNC_F_OPERATOR_SET( atan2, *180.0f/pi ); break;
     default:
       error(EXIT_FAILURE, 0, "%s: operator code %d not recognized",
             __func__, operator);
@@ -1521,18 +1782,33 @@ arithmetic_binary_function_flt(int operator, int flags, 
gal_data_t *l,
 
 
   /* Clean up. Note that if the input arrays can be freed, and any of right
-     or left arrays needed conversion, `BINFUNC_CONVERT_TO_COMPILED_TYPE'
-     has already freed the input arrays, and we only have `r' and `l'
+     or left arrays needed conversion, 'BINFUNC_CONVERT_TO_COMPILED_TYPE'
+     has already freed the input arrays, and we only have 'r' and 'l'
      allocated in any case. Alternatively, when the inputs shouldn't be
-     freed, the only allocated spaces are the `r' and `l' arrays if their
+     freed, the only allocated spaces are the 'r' and 'l' arrays if their
      types weren't compiled for binary operations, we can tell this from
      the pointers: if they are different from the original pointers, they
      were allocated. */
   if(flags & GAL_ARITHMETIC_FREE)
     {
+      /* Clean the main used (temporarily allocated) datasets. */
       if     (o==l)       gal_data_free(r);
       else if(o==r)       gal_data_free(l);
       else              { gal_data_free(l); gal_data_free(r); }
+
+      /* Clean the raw inputs, if they weren't equal to the datasets. */
+      if     (o==il) { if(ir!=r) gal_data_free(ir); }
+      else if(o==ir) { if(il!=l) gal_data_free(il); }
+      else           { if(il!=l) gal_data_free(il);
+                       if(ir!=r) gal_data_free(ir); }
+    }
+  else
+    {
+      /* Input datasets should be kept, but we don't want the temporary
+         datasets, so if they were allocated (they don't equal the input
+         pointers, free them). */
+      if (l!=il) gal_data_free(l);
+      if (r!=ir) gal_data_free(r);
     }
 
   /* Return */
@@ -1543,6 +1819,60 @@ arithmetic_binary_function_flt(int operator, int flags, 
gal_data_t *l,
 
 
 
+/* Make a new dataset. */
+gal_data_t *
+arithmetic_makenew(gal_data_t *sizes)
+{
+  gal_data_t *out, *tmp, *ttmp;
+  int quietmmap=sizes->quietmmap;
+  size_t minmapsize=sizes->minmapsize;
+  size_t i, *dsize, ndim=gal_list_data_number(sizes);
+
+  /* Make sure all the elements are a single, integer number. */
+  for(tmp=sizes; tmp!=NULL; tmp=tmp->next)
+    {
+      if(tmp->size!=1)
+        error(EXIT_FAILURE, 0, "%s: operands given to 'makenew' operator "
+              "should only be a single number. However, at least one of "
+              "the input operands has %zu elements", __func__, tmp->size);
+
+      if( tmp->type==GAL_TYPE_FLOAT32 || tmp->type==GAL_TYPE_FLOAT64)
+        error(EXIT_FAILURE, 0, "%s: operands given to 'makenew' operator "
+              "should have integer types. However, at least one of "
+              "the input operands is floating point", __func__);
+    }
+
+  /* Fill the 'dsize' array based on the given values. */
+  i=ndim-1;
+  tmp=sizes;
+  dsize=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 1, __func__, "dsize");
+  while(tmp!=NULL)
+    {
+      /* Set the next pointer and conver this one to size_t.  */
+      ttmp=tmp->next;
+      tmp=gal_data_copy_to_new_type_free(tmp, GAL_TYPE_SIZE_T);
+
+      /* Write the dimension's length into 'dsize'. */
+      dsize[i--] = ((size_t *)(tmp->array))[0];
+
+      /* Free 'tmp' and re-set it to the next element. */
+      free(tmp);
+      tmp=ttmp;
+    }
+
+  /* allocate the necessary dataset. */
+  out=gal_data_alloc(NULL, GAL_TYPE_UINT8, ndim, dsize, NULL, 1, minmapsize,
+                     quietmmap, "EMPTY", "NOT-SET",
+                     "Empty dataset created by arithmetic.");
+
+  /* Clean up and return. */
+  free(dsize);
+  return out;
+}
+
+
+
+
 
 
 
@@ -1591,6 +1921,44 @@ gal_arithmetic_set_operator(char *string, size_t 
*num_operands)
   else if (!strcmp(string, "log10"))
     { op=GAL_ARITHMETIC_OP_LOG10;             *num_operands=1;  }
 
+  /* Trigonometric functions. */
+  else if( !strcmp(string, "sin"))
+    { op=GAL_ARITHMETIC_OP_SIN;               *num_operands=1; }
+  else if( !strcmp(string, "cos"))
+    { op=GAL_ARITHMETIC_OP_COS;               *num_operands=1; }
+  else if( !strcmp(string, "tan"))
+    { op=GAL_ARITHMETIC_OP_TAN;               *num_operands=1; }
+  else if( !strcmp(string, "asin"))
+    { op=GAL_ARITHMETIC_OP_ASIN;              *num_operands=1; }
+  else if( !strcmp(string, "acos"))
+    { op=GAL_ARITHMETIC_OP_ACOS;              *num_operands=1; }
+  else if( !strcmp(string, "atan"))
+    { op=GAL_ARITHMETIC_OP_ATAN;              *num_operands=1; }
+  else if( !strcmp(string, "atan2"))
+    { op=GAL_ARITHMETIC_OP_ATAN2;             *num_operands=2; }
+  else if( !strcmp(string, "sinh"))
+    { op=GAL_ARITHMETIC_OP_SINH;              *num_operands=1; }
+  else if( !strcmp(string, "cosh"))
+    { op=GAL_ARITHMETIC_OP_COSH;              *num_operands=1; }
+  else if( !strcmp(string, "tanh"))
+    { op=GAL_ARITHMETIC_OP_TANH;              *num_operands=1; }
+  else if( !strcmp(string, "asinh"))
+    { op=GAL_ARITHMETIC_OP_ASINH;             *num_operands=1; }
+  else if( !strcmp(string, "acosh"))
+    { op=GAL_ARITHMETIC_OP_ACOSH;             *num_operands=1; }
+  else if( !strcmp(string, "atanh"))
+    { op=GAL_ARITHMETIC_OP_ATANH;             *num_operands=1; }
+
+  /* Units conversion functions */
+  else if (!strcmp(string, "ra-to-degree"))
+    { op=GAL_ARITHMETIC_OP_RA_TO_DEGREE;      *num_operands=1;  }
+  else if (!strcmp(string, "dec-to-degree"))
+    { op=GAL_ARITHMETIC_OP_DEC_TO_DEGREE;     *num_operands=1;  }
+  else if (!strcmp(string, "degree-to-ra"))
+    { op=GAL_ARITHMETIC_OP_DEGREE_TO_RA;      *num_operands=1;  }
+  else if (!strcmp(string, "degree-to-dec"))
+    { op=GAL_ARITHMETIC_OP_DEGREE_TO_DEC;     *num_operands=1;  }
+
   /* Statistical/higher-level operators. */
   else if (!strcmp(string, "minvalue"))
     { op=GAL_ARITHMETIC_OP_MINVAL;            *num_operands=1;  }
@@ -1620,6 +1988,8 @@ gal_arithmetic_set_operator(char *string, size_t 
*num_operands)
     { op=GAL_ARITHMETIC_OP_STD;               *num_operands=-1; }
   else if (!strcmp(string, "median"))
     { op=GAL_ARITHMETIC_OP_MEDIAN;            *num_operands=-1; }
+  else if (!strcmp(string, "quantile"))
+    { op=GAL_ARITHMETIC_OP_QUANTILE;          *num_operands=-1; }
   else if (!strcmp(string, "sigclip-number"))
     { op=GAL_ARITHMETIC_OP_SIGCLIP_NUMBER;    *num_operands=-1; }
   else if (!strcmp(string, "sigclip-mean"))
@@ -1629,6 +1999,10 @@ gal_arithmetic_set_operator(char *string, size_t 
*num_operands)
   else if (!strcmp(string, "sigclip-std"))
     { op=GAL_ARITHMETIC_OP_SIGCLIP_STD;       *num_operands=-1; }
 
+  /* The size operator */
+  else if (!strcmp(string, "size"))
+    { op=GAL_ARITHMETIC_OP_SIZE;              *num_operands=2;  }
+
   /* Conditional operators. */
   else if (!strcmp(string, "lt" ))
     { op=GAL_ARITHMETIC_OP_LT;                *num_operands=2;  }
@@ -1689,6 +2063,10 @@ gal_arithmetic_set_operator(char *string, size_t 
*num_operands)
   else if (!strcmp(string, "float64"))
     { op=GAL_ARITHMETIC_OP_TO_FLOAT64;        *num_operands=1;  }
 
+  /* New dataset. */
+  else if (!strcmp(string, "makenew"))
+    { op=GAL_ARITHMETIC_OP_MAKENEW;           *num_operands=-1;  }
+
   /* Operator not defined. */
   else
     { op=GAL_ARITHMETIC_OP_INVALID; *num_operands=GAL_BLANK_INT; }
@@ -1706,16 +2084,16 @@ gal_arithmetic_operator_string(int operator)
     {
     case GAL_ARITHMETIC_OP_PLUS:            return "+";
     case GAL_ARITHMETIC_OP_MINUS:           return "-";
-    case GAL_ARITHMETIC_OP_MULTIPLY:        return "*";
+    case GAL_ARITHMETIC_OP_MULTIPLY:        return "x";
     case GAL_ARITHMETIC_OP_DIVIDE:          return "/";
     case GAL_ARITHMETIC_OP_MODULO:          return "%";
 
-    case GAL_ARITHMETIC_OP_LT:              return "<";
-    case GAL_ARITHMETIC_OP_LE:              return "<=";
-    case GAL_ARITHMETIC_OP_GT:              return ">";
-    case GAL_ARITHMETIC_OP_GE:              return ">=";
-    case GAL_ARITHMETIC_OP_EQ:              return "==";
-    case GAL_ARITHMETIC_OP_NE:              return "!=";
+    case GAL_ARITHMETIC_OP_LT:              return "lt";
+    case GAL_ARITHMETIC_OP_LE:              return "le";
+    case GAL_ARITHMETIC_OP_GT:              return "gt";
+    case GAL_ARITHMETIC_OP_GE:              return "ge";
+    case GAL_ARITHMETIC_OP_EQ:              return "eq";
+    case GAL_ARITHMETIC_OP_NE:              return "ne";
     case GAL_ARITHMETIC_OP_AND:             return "and";
     case GAL_ARITHMETIC_OP_OR:              return "or";
     case GAL_ARITHMETIC_OP_NOT:             return "not";
@@ -1735,6 +2113,25 @@ gal_arithmetic_operator_string(int operator)
     case GAL_ARITHMETIC_OP_LOG:             return "log";
     case GAL_ARITHMETIC_OP_LOG10:           return "log10";
 
+    case GAL_ARITHMETIC_OP_SIN:             return "sin";
+    case GAL_ARITHMETIC_OP_COS:             return "cos";
+    case GAL_ARITHMETIC_OP_TAN:             return "tan";
+    case GAL_ARITHMETIC_OP_ASIN:            return "asin";
+    case GAL_ARITHMETIC_OP_ACOS:            return "acos";
+    case GAL_ARITHMETIC_OP_ATAN:            return "atan";
+    case GAL_ARITHMETIC_OP_SINH:            return "sinh";
+    case GAL_ARITHMETIC_OP_COSH:            return "cosh";
+    case GAL_ARITHMETIC_OP_TANH:            return "tanh";
+    case GAL_ARITHMETIC_OP_ASINH:           return "asinh";
+    case GAL_ARITHMETIC_OP_ACOSH:           return "acosh";
+    case GAL_ARITHMETIC_OP_ATANH:           return "atanh";
+    case GAL_ARITHMETIC_OP_ATAN2:           return "atan2";
+
+    case GAL_ARITHMETIC_OP_RA_TO_DEGREE:    return "ra-to-degree";
+    case GAL_ARITHMETIC_OP_DEC_TO_DEGREE:   return "dec-to-degree";
+    case GAL_ARITHMETIC_OP_DEGREE_TO_RA:    return "degree-to-ra";
+    case GAL_ARITHMETIC_OP_DEGREE_TO_DEC:   return "degree-to-dec";
+
     case GAL_ARITHMETIC_OP_MINVAL:          return "minvalue";
     case GAL_ARITHMETIC_OP_MAXVAL:          return "maxvalue";
     case GAL_ARITHMETIC_OP_NUMBERVAL:       return "numbervalue";
@@ -1750,11 +2147,14 @@ gal_arithmetic_operator_string(int operator)
     case GAL_ARITHMETIC_OP_MEAN:            return "mean";
     case GAL_ARITHMETIC_OP_STD:             return "std";
     case GAL_ARITHMETIC_OP_MEDIAN:          return "median";
+    case GAL_ARITHMETIC_OP_QUANTILE:        return "quantile";
     case GAL_ARITHMETIC_OP_SIGCLIP_NUMBER:  return "sigclip-number";
     case GAL_ARITHMETIC_OP_SIGCLIP_MEDIAN:  return "sigclip-median";
     case GAL_ARITHMETIC_OP_SIGCLIP_MEAN:    return "sigclip-mean";
     case GAL_ARITHMETIC_OP_SIGCLIP_STD:     return "sigclip-number";
 
+    case GAL_ARITHMETIC_OP_SIZE:            return "size";
+
     case GAL_ARITHMETIC_OP_TO_UINT8:        return "uchar";
     case GAL_ARITHMETIC_OP_TO_INT8:         return "char";
     case GAL_ARITHMETIC_OP_TO_UINT16:       return "ushort";
@@ -1766,6 +2166,8 @@ gal_arithmetic_operator_string(int operator)
     case GAL_ARITHMETIC_OP_TO_FLOAT32:      return "float32";
     case GAL_ARITHMETIC_OP_TO_FLOAT64:      return "float64";
 
+    case GAL_ARITHMETIC_OP_MAKENEW:         return "makenew";
+
     default:                                return NULL;
     }
   return NULL;
@@ -1787,7 +2189,6 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
   /* Depending on the operator, do the job: */
   switch(operator)
     {
-
     /* Binary operators with any data type. */
     case GAL_ARITHMETIC_OP_PLUS:
     case GAL_ARITHMETIC_OP_MINUS:
@@ -1825,13 +2226,36 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
       out=d1;
       break;
 
-
     /* Unary function operators. */
     case GAL_ARITHMETIC_OP_SQRT:
     case GAL_ARITHMETIC_OP_LOG:
     case GAL_ARITHMETIC_OP_LOG10:
+    case GAL_ARITHMETIC_OP_SIN:
+    case GAL_ARITHMETIC_OP_COS:
+    case GAL_ARITHMETIC_OP_TAN:
+    case GAL_ARITHMETIC_OP_ASIN:
+    case GAL_ARITHMETIC_OP_ACOS:
+    case GAL_ARITHMETIC_OP_ATAN:
+    case GAL_ARITHMETIC_OP_SINH:
+    case GAL_ARITHMETIC_OP_COSH:
+    case GAL_ARITHMETIC_OP_TANH:
+    case GAL_ARITHMETIC_OP_ASINH:
+    case GAL_ARITHMETIC_OP_ACOSH:
+    case GAL_ARITHMETIC_OP_ATANH:
+    case GAL_ARITHMETIC_OP_RA_TO_DEGREE:
+    case GAL_ARITHMETIC_OP_DEC_TO_DEGREE:
+    case GAL_ARITHMETIC_OP_DEGREE_TO_RA:
+    case GAL_ARITHMETIC_OP_DEGREE_TO_DEC:
       d1 = va_arg(va, gal_data_t *);
-      out=arithmetic_unary_function(operator, flags, d1);
+      out=arithmetic_function_unary(operator, flags, d1);
+      break;
+
+    /* Binary function operators. */
+    case GAL_ARITHMETIC_OP_POW:
+    case GAL_ARITHMETIC_OP_ATAN2:
+      d1 = va_arg(va, gal_data_t *);
+      d2 = va_arg(va, gal_data_t *);
+      out=arithmetic_function_binary_flt(operator, flags, d1, d2);
       break;
 
     /* Statistical operators that return one value. */
@@ -1860,6 +2284,7 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
     case GAL_ARITHMETIC_OP_MEAN:
     case GAL_ARITHMETIC_OP_STD:
     case GAL_ARITHMETIC_OP_MEDIAN:
+    case GAL_ARITHMETIC_OP_QUANTILE:
     case GAL_ARITHMETIC_OP_SIGCLIP_STD:
     case GAL_ARITHMETIC_OP_SIGCLIP_MEAN:
     case GAL_ARITHMETIC_OP_SIGCLIP_MEDIAN:
@@ -1869,15 +2294,6 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
       out=arithmetic_multioperand(operator, flags, d1, d2, numthreads);
       break;
 
-
-    /* Binary function operators. */
-    case GAL_ARITHMETIC_OP_POW:
-      d1 = va_arg(va, gal_data_t *);
-      d2 = va_arg(va, gal_data_t *);
-      out=arithmetic_binary_function_flt(operator, flags, d1, d2);
-      break;
-
-
     /* Binary operators that only work on integer types. */
     case GAL_ARITHMETIC_OP_BITAND:
     case GAL_ARITHMETIC_OP_BITOR:
@@ -1895,6 +2311,12 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
       out=arithmetic_bitwise_not(flags, d1);
       break;
 
+    /* Size operator */
+    case GAL_ARITHMETIC_OP_SIZE:
+      d1 = va_arg(va, gal_data_t *);
+      d2 = va_arg(va, gal_data_t *);
+      out=arithmetic_size(operator, flags, d1, d2);
+      break;
 
     /* Conversion operators. */
     case GAL_ARITHMETIC_OP_TO_UINT8:
@@ -1911,10 +2333,15 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
       out=arithmetic_change_type(d1, operator, flags);
       break;
 
+    /* Build dataset from scratch. */
+    case GAL_ARITHMETIC_OP_MAKENEW:
+      d1 = va_arg(va, gal_data_t *);
+      out=arithmetic_makenew(d1);
+      break;
 
     /* When the operator is not recognized. */
     default:
-      error(EXIT_FAILURE, 0, "%s: the argument \"%d\" could not be "
+      error(EXIT_FAILURE, 0, "%s: the argument '%d' could not be "
             "interpretted as an operator", __func__, operator);
     }
 
diff --git a/lib/array.c b/lib/array.c
index 8142bd0..26bd646 100644
--- a/lib/array.c
+++ b/lib/array.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
diff --git a/lib/binary.c b/lib/binary.c
index a99461e..ba0568f 100644
--- a/lib/binary.c
+++ b/lib/binary.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -55,7 +55,7 @@ binary_erode_dilate_2d_4con(gal_data_t *input, int 
dilate0_erode1)
   /* Do a sanity check: */
   if(dilate0_erode1!=1 && dilate0_erode1!=0)
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we can "
-          "fix this problem. The value to `dilate0_erode1' is %u while it "
+          "fix this problem. The value to 'dilate0_erode1' is %u while it "
           "should be 0 or 1", __func__, PACKAGE_BUGREPORT, dilate0_erode1);
 
   /* Set the foreground and background values. */
@@ -259,15 +259,15 @@ binary_erode_dilate_general(gal_data_t *input, unsigned 
char dilate0_erode1,
 
 
 
-/* Erode a binary dataset any number of times. If `inplace' is given a
-   value of `1', then do the erosion within the already allocated space,
+/* Erode a binary dataset any number of times. If 'inplace' is given a
+   value of '1', then do the erosion within the already allocated space,
    otherwise, allocate a new array and save the result into that.
 
    This function will only work on the elements with a value of 1 or 0. It
    will leave all the rest unchanged. Also note that it only works on
-   `uint8_t' type datasets. So if the input doesn't have that type, it is
+   'uint8_t' type datasets. So if the input doesn't have that type, it is
    going to copy it this type and return the newlyallocated dataset. So
-   when the input's type isn't `uint8_t', `inplace' is irrelevant. */
+   when the input's type isn't 'uint8_t', 'inplace' is irrelevant. */
 static gal_data_t *
 binary_erode_dilate(gal_data_t *input, size_t num, int connectivity,
                     int inplace, int d0e1)
@@ -279,7 +279,7 @@ binary_erode_dilate(gal_data_t *input, size_t num, int 
connectivity,
   /* Currently this only works on blocks. */
   if(input->block)
     error(EXIT_FAILURE, 0, "%s: currently only works on a fully "
-          "allocated block of memory, but the input is a tile (its `block' "
+          "allocated block of memory, but the input is a tile (its 'block' "
           "element is not NULL)", __func__);
 
   /* Set the dataset to work on. */
@@ -352,8 +352,8 @@ gal_binary_open(gal_data_t *input, size_t num, int 
connectivity,
   /* First do the necessary number of erosions. */
   out=gal_binary_erode(input, num, connectivity, inplace);
 
-  /* If `inplace' was called, then `out' is the same as `input', if it
-     wasn't, then `out' is a newly allocated array. In any case, we should
+  /* If 'inplace' was called, then 'out' is the same as 'input', if it
+     wasn't, then 'out' is a newly allocated array. In any case, we should
      dilate in the same allocated space. */
   gal_binary_dilate(input, num, connectivity, 1);
 
@@ -397,7 +397,7 @@ gal_binary_connected_components(gal_data_t *binary, 
gal_data_t **out,
 
   /* Two small sanity checks. */
   if(binary->type!=GAL_TYPE_UINT8)
-    error(EXIT_FAILURE, 0, "%s: the input data set type must be `uint8'",
+    error(EXIT_FAILURE, 0, "%s: the input data set type must be 'uint8'",
           __func__);
   if(binary->block)
     error(EXIT_FAILURE, 0, "%s: currently, the input data structure to "
@@ -412,13 +412,13 @@ gal_binary_connected_components(gal_data_t *binary, 
gal_data_t **out,
 
       /* Make sure the given dataset has the same size as the input. */
       if( gal_dimension_is_different(binary, lab) )
-        error(EXIT_FAILURE, 0, "%s: the `binary' and `out' datasets must "
+        error(EXIT_FAILURE, 0, "%s: the 'binary' and 'out' datasets must "
               "have the same size", __func__);
 
-      /* Make sure it has a `int32' type. */
+      /* Make sure it has a 'int32' type. */
       if( lab->type!=GAL_TYPE_INT32 )
-        error(EXIT_FAILURE, 0, "%s: the `out' dataset must have `int32' type"
-              "but the array you have given is `%s' type", __func__,
+        error(EXIT_FAILURE, 0, "%s: the 'out' dataset must have 'int32' type"
+              "but the array you have given is '%s' type", __func__,
               gal_type_name(lab->type, 1));
 
       /* Reset all its values to zero. */
@@ -491,7 +491,7 @@ gal_binary_connected_components(gal_data_t *binary, 
gal_data_t **out,
 
 
 
-/* Put the indexs of connected labels in a list of `gal_data_t's, each with
+/* Put the indexs of connected labels in a list of 'gal_data_t's, each with
    a one-dimensional array that has the indexs of that connected
    component.*/
 #define BINARY_CONINDEX_VAL 2
@@ -506,7 +506,7 @@ gal_binary_connected_indexs(gal_data_t *binary, int 
connectivity)
 
   /* Small sanity checks. */
   if(binary->type!=GAL_TYPE_UINT8)
-    error(EXIT_FAILURE, 0, "%s: the input data set type must be `uint8'",
+    error(EXIT_FAILURE, 0, "%s: the input data set type must be 'uint8'",
           __func__);
   if(binary->block)
     error(EXIT_FAILURE, 0, "%s: currently, the input data structure to "
@@ -516,7 +516,7 @@ gal_binary_connected_indexs(gal_data_t *binary, int 
connectivity)
   b=binary->array;
   for(i=0;i<binary->size;++i)
     /* A pixel that has already been recorded is given a value of
-       `BINARY_CONINDEX_VAL'. */
+       'BINARY_CONINDEX_VAL'. */
     if( b[i]==1 )
       {
         /* Add this pixel to the queue of pixels to work with. */
@@ -569,7 +569,7 @@ gal_binary_connected_indexs(gal_data_t *binary, int 
connectivity)
   }
   */
 
-  /* Set all the `2' values back to `1'. */
+  /* Set all the '2' values back to '1'. */
   bf=(b=binary->array)+binary->size;
   do if(*b==BINARY_CONINDEX_VAL) *b=1; while(++b<bf);
 
@@ -597,8 +597,8 @@ gal_binary_connected_adjacency_matrix(gal_data_t *adjacency,
 
   /* Some small sanity checks. */
   if(adjacency->type != GAL_TYPE_UINT8)
-    error(EXIT_FAILURE, 0, "%s: input must have type `uint8'. However, the "
-          "input dataset has type of `%s'", __func__,
+    error(EXIT_FAILURE, 0, "%s: input must have type 'uint8'. However, the "
+          "input dataset has type of '%s'", __func__,
           gal_type_name(adjacency->type, 1));
 
   if(adjacency->ndim != 2)
@@ -653,7 +653,6 @@ gal_binary_connected_adjacency_matrix(gal_data_t *adjacency,
         ++curlab;
       }
 
-
   /* For a check.
   printf("=== Old labels --> new labels ===\n");
   for(i=1;i<num;++i) printf("%zu: %u\n", i, newlabs[i]);
@@ -668,6 +667,84 @@ gal_binary_connected_adjacency_matrix(gal_data_t 
*adjacency,
 
 
 
+/* List-based adjacency matrix: when the number of points to find adjacent
+   elements is large, the array-based adjacency solution of
+   'gal_binary_connected_adjacency_matrix' will consume far too much memory
+   (since it is a square). In those cases, we need to work based on
+   lists.
+
+   The input is an array of 'size_t' list pointers. Each one points to the
+   labels that are touching it.
+
+                     indexs        b       d
+                                   ^       ^
+                     indexs        a   b   c   a
+                                   ^   ^   ^   ^
+                     listarr:    | * | * | * | * |
+*/
+gal_data_t *
+gal_binary_connected_adjacency_list(gal_list_sizet_t **listarr,
+                                    size_t number, size_t minmapsize,
+                                    int quietmmap, size_t *numconnected)
+{
+  size_t i, p;
+  gal_list_sizet_t *tmp;
+  gal_data_t *newlabs_d;
+  gal_list_sizet_t *Q=NULL;
+  int32_t *newlabs, curlab=1;
+
+  /* Allocate (and clear) the output datastructure. */
+  newlabs_d=gal_data_alloc(NULL, GAL_TYPE_INT32, 1, &number, NULL, 1,
+                           minmapsize, quietmmap, NULL, NULL, NULL);
+  newlabs=newlabs_d->array;
+
+  /* Go over the input matrix and apply the same principle as we used to
+     identify connected components in an image: through a queue, find those
+     elements that are connected. */
+  for(i=1;i<number;++i)
+    if(newlabs[i]==0)
+      {
+        /* Add this old label to the list that must be corrected. */
+        gal_list_sizet_add(&Q, i);
+
+        /* Continue while the list has elements. */
+        while(Q!=NULL)
+          {
+            /* Pop the top old-label from the list. */
+            p=gal_list_sizet_pop(&Q);
+
+            /* If it has already been labeled then ignore it. */
+            if( newlabs[p]!=curlab )
+              {
+                /* Give it the new label. */
+                newlabs[p]=curlab;
+
+                /* Go over the adjacency list for this touching object and
+                   see if there are any not-yet-labeled objects that are
+                   touching it. */
+                for(tmp=listarr[p]; tmp!=NULL; tmp=tmp->next)
+                  if( newlabs[tmp->v]==0 )
+                    gal_list_sizet_add(&Q, tmp->v);
+              }
+          }
+
+        /* Increment the current label. */
+        ++curlab;
+      }
+
+  /* For a check.
+  printf("=== Old labels --> new labels ===\n");
+  for(i=1;i<number;++i) printf("%zu: %u\n", i, newlabs[i]);
+  */
+
+  /* Return the output. */
+  *numconnected = curlab-1;
+  return newlabs_d;
+}
+
+
+
+
 
 
 
@@ -766,8 +843,8 @@ gal_binary_holes_label(gal_data_t *input, int connectivity,
 
   /* A small sanity check. */
   if( input->type != GAL_TYPE_UINT8 )
-    error(EXIT_FAILURE, 0, "%s: input must have `uint8' type, but its "
-          "input dataset has `%s' type", __func__,
+    error(EXIT_FAILURE, 0, "%s: input must have 'uint8' type, but its "
+          "input dataset has '%s' type", __func__,
           gal_type_name(input->type, 1));
 
 
@@ -785,13 +862,13 @@ gal_binary_holes_label(gal_data_t *input, int 
connectivity,
      we should invert the respective pixel. To do it, we'll use the tile
      that was defined before, just change its block and array.*/
   tile->array=gal_tile_block_relative_to_other(tile, holelabs);
-  tile->block=holelabs; /* has to be after correcting `tile->array'. */
+  tile->block=holelabs; /* has to be after correcting 'tile->array'. */
 
 
-  /* The type of the tile is already known (it is `int32_t') and we have no
-     output/other, so we'll just put `int' as a place-holder. In this way
+  /* The type of the tile is already known (it is 'int32_t') and we have no
+     output/other, so we'll just put 'int' as a place-holder. In this way
      we can avoid the switch statement of GAL_TILE_PARSE_OPERATE, and
-     directly use the workhorse macro `GAL_TILE_PO_OISET'. */
+     directly use the workhorse macro 'GAL_TILE_PO_OISET'. */
   lab=(holelabs)->array;
   GAL_TILE_PO_OISET(int32_t, int, tile, NULL, 0, 0, {
       *lab++ = ( *i
@@ -812,7 +889,7 @@ gal_binary_holes_label(gal_data_t *input, int connectivity,
      from the start, effectively removing the paddings. Therefore, ee will
      just correct the sizes and we won't bother actually re-allocating the
      array size in memory. According to the GNU C library's description
-     after `realloc': "In several allocation implementations, making a
+     after 'realloc': "In several allocation implementations, making a
      block smaller sometimes necessitates copying it, so it can fail if no
      other space is available.". The extra padding is only 2 pixels wide,
      thus, the extra space is negligible compared to the actual array. So
@@ -850,7 +927,7 @@ gal_binary_holes_label(gal_data_t *input, int connectivity,
 
    3. Since we had a 2 pixel padding on the edges of the image, we
       know for sure that all labeled regions with a label of 1 are
-      actually connected `holes' in the input image.
+      actually connected 'holes' in the input image.
 
       Any pixel with a label larger than 1, is therefore a bounded
       hole that is not 8-connected to the rest of the holes.  */
@@ -864,8 +941,8 @@ gal_binary_holes_fill(gal_data_t *input, int connectivity, 
size_t maxsize)
 
   /* Small sanity checks. */
   if( input->type != GAL_TYPE_UINT8 )
-    error(EXIT_FAILURE, 0, "%s: input must have `uint8' type, but its "
-          "input dataset has `%s' type", __func__,
+    error(EXIT_FAILURE, 0, "%s: input must have 'uint8' type, but its "
+          "input dataset has '%s' type", __func__,
           gal_type_name(input->type, 1));
   if(connectivity<1 || connectivity>input->ndim)
     error(EXIT_FAILURE, 0, "%s: connectivity value %d is not acceptable. "
@@ -886,7 +963,7 @@ gal_binary_holes_fill(gal_data_t *input, int connectivity, 
size_t maxsize)
      that was defined before, just change its block and array.*/
   in=input->array;
   tile->array=gal_tile_block_relative_to_other(tile, holelabs);
-  tile->block=holelabs; /* has to be after correcting `tile->array'. */
+  tile->block=holelabs; /* has to be after correcting 'tile->array'. */
 
   /* If the user wants to only fill holes to a certain size, then remove
      those with a larger size. */
@@ -908,10 +985,10 @@ gal_binary_holes_fill(gal_data_t *input, int 
connectivity, size_t maxsize)
       free(sizes);
     }
 
-  /* The type of the tile is already known (it is `int32_t') and we have no
-     output, so we'll just put `int' as a place-holder. In this way we can
+  /* The type of the tile is already known (it is 'int32_t') and we have no
+     output, so we'll just put 'int' as a place-holder. In this way we can
      avoid the switch statement of GAL_TILE_PARSE_OPERATE, and directly use
-     the workhorse macro `GAL_TILE_PO_OISET'. */
+     the workhorse macro 'GAL_TILE_PO_OISET'. */
   GAL_TILE_PO_OISET(int32_t, int, tile, NULL, 0, 0, {
       *in = *i>1 && *i!=GAL_BLANK_INT32 ? 1 : *in;
       ++in;
diff --git a/lib/blank.c b/lib/blank.c
index 77e6969..dda0e91 100644
--- a/lib/blank.c
+++ b/lib/blank.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -41,7 +41,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 /* Write the blank value of the type into an already allocate space. Note
-   that for STRINGS, pointer should actually be `char **'. */
+   that for STRINGS, pointer should actually be 'char **'. */
 void
 gal_blank_write(void *ptr, uint8_t type)
 {
@@ -134,7 +134,7 @@ gal_blank_initialize_array(void *array, size_t size, 
uint8_t type)
 
 
 /* Print the blank value as a string. For the integer types, we'll use the
-   PRIxNN keywords of `inttypes.h' (which is imported into Gnuastro from
+   PRIxNN keywords of 'inttypes.h' (which is imported into Gnuastro from
    Gnulib, so we don't necessarily rely on the host system having it). */
 char *
 gal_blank_as_string(uint8_t type, int width)
@@ -364,13 +364,13 @@ gal_blank_is(void *pointer, uint8_t type)
 
 /* Return 1 if the dataset has a blank value and zero if it doesn't. Before
    checking the dataset, this function will look at its flags. If the
-   `GAL_DATA_FLAG_HASBLANK' or `GAL_DATA_FLAG_DONT_CHECK_ZERO' bits of
-   `input->flag' are set to 1, this function will not do any check and will
+   'GAL_DATA_FLAG_HASBLANK' or 'GAL_DATA_FLAG_DONT_CHECK_ZERO' bits of
+   'input->flag' are set to 1, this function will not do any check and will
    just use the information in the flags.
 
    If you want to re-check a dataset which has non-zero flags, then
    explicitly set the appropriate flag to zero before calling this
-   function. When there are no other flags, you can just set `input->flags'
+   function. When there are no other flags, you can just set 'input->flags'
    to zero, otherwise you can use this expression:
 
        input->flags &= ~ (GAL_DATA_FLAG_HASBLANK | GAL_DATA_FLAG_USE_ZERO);
@@ -445,7 +445,9 @@ gal_blank_present(gal_data_t *input, int updateflag)
         error(EXIT_FAILURE, 0, "%s: tile mode is currently not supported for "
               "strings", __func__);
       strf = (str=input->array) + input->size;
-      do if(!strcmp(*str,GAL_BLANK_STRING)) return 1; while(++str<strf);
+      do
+        if(*str==NULL || !strcmp(*str,GAL_BLANK_STRING)) return 1;
+      while(++str<strf);
       break;
 
     /* Complex types */
@@ -486,17 +488,34 @@ gal_blank_present(gal_data_t *input, int updateflag)
 size_t
 gal_blank_number(gal_data_t *input, int updateflag)
 {
+  size_t nblank;
+  char **strarr;
   gal_data_t *number;
-  size_t num_not_blank;
+  size_t i, num_not_blank;
 
   if(input)
     {
       if( gal_blank_present(input, updateflag) )
         {
-          number=gal_statistics_number(input);
-          num_not_blank=((size_t *)(number->array))[0];
-          gal_data_free(number);
-          return input->size - num_not_blank;
+          if(input->type==GAL_TYPE_STRING)
+            {
+              nblank=0;
+              strarr=input->array;
+              for(i=0;i<input->size;++i)
+                {
+                  if( strarr[i]==NULL
+                      || strcmp(strarr[i], GAL_BLANK_STRING)==0 )
+                    ++nblank;
+                }
+              return nblank;
+            }
+          else
+            {
+              number=gal_statistics_number(input);
+              num_not_blank=((size_t *)(number->array))[0];
+              gal_data_free(number);
+              return input->size - num_not_blank;
+            }
         }
       else
         return 0;
@@ -597,17 +616,18 @@ gal_blank_flag(gal_data_t *input)
 void
 gal_blank_flag_apply(gal_data_t *input, gal_data_t *flag)
 {
-  char **str=input->array;
+  size_t j;
+  char **strarr=input->array;
   uint8_t *f=flag->array, *ff=f+flag->size;
 
   /* Sanity check. */
   if(flag->type!=GAL_TYPE_UINT8)
-    error(EXIT_FAILURE, 0, "%s: the `flag' argument has a `%s' type, it "
+    error(EXIT_FAILURE, 0, "%s: the 'flag' argument has a '%s' type, it "
           "must have an unsigned 8-bit type", __func__,
           gal_type_name(flag->type, 1));
   if(gal_dimension_is_different(input, flag))
-    error(EXIT_FAILURE, 0, "%s: the `flag' argument doesn't have the same "
-          "size as the `input' argument", __func__);
+    error(EXIT_FAILURE, 0, "%s: the 'flag' argument doesn't have the same "
+          "size as the 'input' argument", __func__);
 
   /* Write the blank values. */
   switch(input->type)
@@ -626,16 +646,15 @@ gal_blank_flag_apply(gal_data_t *input, gal_data_t *flag)
 
     /* Strings. */
     case GAL_TYPE_STRING:
-      do
+      for(j=0; j<input->size; ++j)
         {
           if(*f && *f!=GAL_BLANK_UINT8)
             {
-              free(*str);
-              *str=gal_blank_as_string(GAL_TYPE_STRING, 0);
+              free(strarr[j]);
+              gal_checkset_allocate_copy(GAL_BLANK_STRING, &strarr[j]);
             }
-          ++str;
+          ++f;
         }
-      while(++f<ff);
       break;
 
     /* Currently unsupported types. */
@@ -659,10 +678,76 @@ gal_blank_flag_apply(gal_data_t *input, gal_data_t *flag)
 
 
 
+/* Remove flagged elements from a dataset (which may not necessarily
+   blank), convert it to a 1D dataset and adjust the size properly. In
+   practice this function doesn't 'realloc' the input array, all it does is
+   to shift the blank eleemnts to the end and adjust the size elements of
+   the 'gal_data_t'. */
+#define BLANK_FLAG_REMOVE(IT) {                                         \
+    IT *a=input->array, *af=a+input->size, *o=input->array;             \
+    do {                                                                \
+      if(*f==0) {++num; *o++=*a; }                                      \
+      ++f;                                                              \
+    }                                                                   \
+    while(++a<af);                                                      \
+  }
+void
+gal_blank_flag_remove(gal_data_t *input, gal_data_t *flag)
+{
+  char **strarr;
+  size_t i, num=0;
+  uint8_t *f=flag->array;
+
+  /* Sanity check. */
+  if(flag->type!=GAL_TYPE_UINT8)
+    error(EXIT_FAILURE, 0, "%s: the 'flag' argument has a '%s' type, it "
+          "must have an unsigned 8-bit type", __func__,
+          gal_type_name(flag->type, 1));
+  if(gal_dimension_is_different(input, flag))
+    error(EXIT_FAILURE, 0, "%s: the 'flag' argument doesn't have the same "
+          "size as the 'input' argument", __func__);
+
+  /* Shift all non-blank elements to the start of the array. */
+  switch(input->type)
+    {
+    case GAL_TYPE_UINT8:    BLANK_FLAG_REMOVE( uint8_t  );    break;
+    case GAL_TYPE_INT8:     BLANK_FLAG_REMOVE( int8_t   );    break;
+    case GAL_TYPE_UINT16:   BLANK_FLAG_REMOVE( uint16_t );    break;
+    case GAL_TYPE_INT16:    BLANK_FLAG_REMOVE( int16_t  );    break;
+    case GAL_TYPE_UINT32:   BLANK_FLAG_REMOVE( uint32_t );    break;
+    case GAL_TYPE_INT32:    BLANK_FLAG_REMOVE( int32_t  );    break;
+    case GAL_TYPE_UINT64:   BLANK_FLAG_REMOVE( uint64_t );    break;
+    case GAL_TYPE_INT64:    BLANK_FLAG_REMOVE( int64_t  );    break;
+    case GAL_TYPE_FLOAT32:  BLANK_FLAG_REMOVE( float    );    break;
+    case GAL_TYPE_FLOAT64:  BLANK_FLAG_REMOVE( double   );    break;
+    case GAL_TYPE_STRING:
+      strarr=input->array;
+      for(i=0;i<input->size;++i)
+        {
+          if( *f && *f!=GAL_BLANK_UINT8 )        /* Flagged to be removed */
+            { free(strarr[i]); strarr[i]=NULL; }
+          else strarr[num++]=strarr[i];          /* Keep. */
+          ++f;
+        }
+      break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
+            __func__, input->type);
+    }
+
+  /* Adjust the size elements of the dataset. */
+  input->ndim=1;
+  input->dsize[0]=input->size=num;
+}
+
+
+
+
+
 /* Remove blank elements from a dataset, convert it to a 1D dataset and
-   adjust the size properly. In practice this function doesn't `realloc'
+   adjust the size properly. In practice this function doesn't 'realloc'
    the input array, all it does is to shift the blank eleemnts to the end
-   and adjust the size elements of the `gal_data_t'. */
+   and adjust the size elements of the 'gal_data_t'. */
 #define BLANK_REMOVE(IT) {                                              \
     IT b, *a=input->array, *af=a+input->size, *o=input->array;          \
     gal_blank_write(&b, input->type);                                   \
@@ -674,7 +759,8 @@ gal_blank_flag_apply(gal_data_t *input, gal_data_t *flag)
 void
 gal_blank_remove(gal_data_t *input)
 {
-  size_t num=0;
+  char **strarr;
+  size_t i, num=0;
 
   /* This function currently assumes a contiguous patch of memory. */
   if(input->block && input->ndim!=1 )
@@ -698,6 +784,13 @@ gal_blank_remove(gal_data_t *input)
         case GAL_TYPE_INT64:    BLANK_REMOVE( int64_t  );    break;
         case GAL_TYPE_FLOAT32:  BLANK_REMOVE( float    );    break;
         case GAL_TYPE_FLOAT64:  BLANK_REMOVE( double   );    break;
+        case GAL_TYPE_STRING:
+          strarr=input->array;
+          for(i=0;i<input->size;++i)
+            if( strcmp(strarr[i], GAL_BLANK_STRING) ) /* Not blank. */
+              { strarr[num++]=strarr[i]; }
+            else { free(strarr[i]); strarr[i]=NULL; }  /* Is blank. */
+          break;
         default:
           error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
                 __func__, input->type);
@@ -713,3 +806,109 @@ gal_blank_remove(gal_data_t *input)
   input->flag |= GAL_DATA_FLAG_BLANK_CH;
   input->flag &= ~GAL_DATA_FLAG_HASBLANK;
 }
+
+
+
+
+
+/* Similar to 'gal_blank_remove', but also reallocates/frees the extra
+   space. */
+void
+gal_blank_remove_realloc(gal_data_t *input)
+{
+  /* Remove the blanks and fix the size of the dataset. */
+  gal_blank_remove(input);
+
+  /* Run realloc to shrink the allocated space. */
+  input->array=realloc(input->array,
+                       input->size*gal_type_sizeof(input->type));
+  if(input->array==NULL)
+    error(EXIT_FAILURE, 0, "%s: couldn't reallocate memory", __func__);
+}
+
+
+
+
+
+static gal_data_t *
+blank_remove_in_list_merge_flags(gal_data_t *thisdata, gal_data_t *flag)
+{
+  size_t i;
+  uint8_t *u, *tu;
+  gal_data_t *flagtmp;
+
+  /* Build the flag of blank elements for this column. */
+  flagtmp=gal_blank_flag(thisdata);
+
+  /* If this is the first column, then use flagtmp as flag. */
+  if(flag)
+    {
+      u=flag->array;
+      tu=flagtmp->array;
+      for(i=0;i<flag->size;++i) u[i] = u[i] || tu[i];
+      gal_data_free(flagtmp);
+    }
+  else
+    flag=flagtmp;
+
+  /* For a check.
+  u=flag->array;
+  double *d=thisdata->array;
+  for(i=0;i<flag->size;++i) printf("%f, %u\n", d[i], u[i]);
+  printf("\n");
+  */
+
+  /* Return the flag dataset. */
+  return flag;
+}
+
+
+
+
+
+/* Remove any row that has a blank in any of the given columns. */
+gal_data_t *
+gal_blank_remove_rows(gal_data_t *columns, gal_list_sizet_t *column_indexs)
+{
+  size_t i;
+  gal_list_sizet_t *tcol;
+  gal_data_t *tmp, *flag=NULL;
+
+  /* If any columns are requested, only use the given columns for the
+     flags, otherwise use all the input columns. */
+  if(column_indexs)
+    for(tcol=column_indexs; tcol!=NULL; tcol=tcol->next)
+      {
+        /* Find the correct column in the full list. */
+        i=0;
+        for(tmp=columns; tmp!=NULL; tmp=tmp->next)
+          if(i++==tcol->v) break;
+
+        /* If the given index is larger than the number of elements in the
+           input list, then print an error. */
+        if(tmp==NULL)
+          error(EXIT_FAILURE, 0, "%s: input list has %zu elements, but the "
+                "column %zu (counting from zero) has been requested",
+                __func__, gal_list_data_number(columns), tcol->v);
+
+        /* Build the flag of blank elements for this column. */
+        flag=blank_remove_in_list_merge_flags(tmp, flag);
+      }
+  else
+    for(tmp=columns; tmp!=NULL; tmp=tmp->next)
+      flag=blank_remove_in_list_merge_flags(tmp, flag);
+
+  /* Now that the flags have been set, remove the rows. */
+  for(tmp=columns; tmp!=NULL; tmp=tmp->next)
+    gal_blank_flag_remove(tmp, flag);
+
+  /* For a check.
+  double *d1=columns->array, *d2=columns->next->array;
+  for(i=0;i<columns->size;++i)
+    printf("%f, %f\n", d2[i], d1[i]);
+  printf("\n");
+  */
+
+  /* Return the flag. */
+  return flag;
+}
diff --git a/lib/box.c b/lib/box.c
index 1a48f71..7f6353d 100644
--- a/lib/box.c
+++ b/lib/box.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -102,7 +102,7 @@ gal_box_bound_ellipse(double a, double b, double theta_deg, 
long *width)
 
 /* Find the bounding box of an ellipsoid. An ellipsoid is defined by its
    three axises: the first (a) must be the major axis, the other two must
-   be smaller than `a' but no particular relation between them is
+   be smaller than 'a' but no particular relation between them is
    assumed. We will define the orientation of the ellipsoid from its major
    axis and use the "Proper Euler angles" (ZXZ order) to define that
    orientation.
@@ -117,8 +117,8 @@ gal_box_bound_ellipse(double a, double b, double theta_deg, 
long *width)
 
       https://en.wikipedia.org/wiki/Euler_angles
 
-   Defining the general point `p' as (the transpose of `p' with `p^T' and
-   its inverse with `p^-1'):
+   Defining the general point 'p' as (the transpose of 'p' with 'p^T' and
+   its inverse with 'p^-1'):
 
            | x |
        p = | y |
@@ -138,7 +138,7 @@ gal_box_bound_ellipse(double a, double b, double theta_deg, 
long *width)
    vertical vectors into one matrix. The rotation can be written as the
    following combined affine transformation (only in the three main axises
    (since we aren't dealing with translation here). Here, we'll call
-   `sin(alpha)' as `s1', `cos(beta)' as `c2' and `sin(gamma)' as `s3'.
+   'sin(alpha)' as 's1', 'cos(beta)' as 'c2' and 'sin(gamma)' as 's3'.
 
                    Rotate by Euler angles
                 ----------------------------
@@ -146,7 +146,7 @@ gal_box_bound_ellipse(double a, double b, double theta_deg, 
long *width)
     R = | s1   c1  0 | * | 0   c2  -s2 | * | s3   c3   0 |
         | 0    0   1 |   | 0   s2   c2 |   | 0    0    1 |
 
-   Then `M' (rotation and scaling to obtain ellipsoid from sphere) will be:
+   Then 'M' (rotation and scaling to obtain ellipsoid from sphere) will be:
 
             | a |          | 0 |          | 0 |              | A1 B1 C1 |
     A = R * | 0 |, B = R * | b |, C = R * | 0 |    -->   M = | A2 B2 C2 |
@@ -162,27 +162,27 @@ gal_box_bound_ellipse(double a, double b, double 
theta_deg, long *width)
 
      (M^-1 * p)^T * S * (M^-1 * p) = 0  --> p^T * (M^-T * S * M^-1) * p = 0
 
-   Writing Q = M^-T * S * M^-1, we get: `p^T * Q * p = 0'. Now, we define a
-   plane with a horizontal vector `u = [a b c d ]', such that `u.p=0'. For
-   a point on the ellipsoid (at `p') we have: `u^T=p^T * Q'. This is
-   because: `u.p = u^T * p = p^T * Q * p = 0' (as we showed above).
+   Writing Q = M^-T * S * M^-1, we get: 'p^T * Q * p = 0'. Now, we define a
+   plane with a horizontal vector 'u = [a b c d ]', such that 'u.p=0'. For
+   a point on the ellipsoid (at 'p') we have: 'u^T=p^T * Q'. This is
+   because: 'u.p = u^T * p = p^T * Q * p = 0' (as we showed above).
 
    Tangent planes will have the following useful property:
 
         u^T * Q^-1 * u = p^T * Q * Q^-1 * Q * p = p^T * Q * p = 0
 
    Now, the particular plane that is perpendicular to the X axis has the
-   general form: `u = [ 1 0 0 -x ]'. So, defining `R = Q^1', and using the
+   general form: 'u = [ 1 0 0 -x ]'. So, defining 'R = Q^1', and using the
    property above for tangential planes, we can find the X axis position.
 
-   However, getting to `R' from `M' as described above is not easy. So,
+   However, getting to 'R' from 'M' as described above is not easy. So,
    taking the following considerations into account, we can derive the
    final values: [from that webpage] "Several details of the problem can
-   make computing the planes more efficient. The first is that `S' is
-   involutory, meaning `S^-1 = S'. This means that the product `M * S^-1'
-   can be computed implicitly: it is simply `M' with its last column
-   negated. The last column of `R = M * S^-1 * MT' is the same, because the
-   last column of `M^T' is `[ 0 0 0 1 ]'. In particular, `R[4,4]=-1'.
+   make computing the planes more efficient. The first is that 'S' is
+   involutory, meaning 'S^-1 = S'. This means that the product 'M * S^-1'
+   can be computed implicitly: it is simply 'M' with its last column
+   negated. The last column of 'R = M * S^-1 * MT' is the same, because the
+   last column of 'M^T' is '[ 0 0 0 1 ]'. In particular, 'R[4,4]=-1'.
 
    Not all values of RR are used; in fact, only values from the last column
    and the diagonal appear in the formulae. We know the last column
@@ -192,7 +192,7 @@ gal_box_bound_ellipse(double a, double b, double theta_deg, 
long *width)
 
    So the bounding box lengths along each dimension are the
    following. Recall that in homogenous coordinates, the last column is for
-   translation. So in the case of this function all the `M[i,4]' values are
+   translation. So in the case of this function all the 'M[i,4]' values are
    zero.
 
       x = M[1,4] \pm sqrt( M[1,1]^2 + M[1,2]^2 + M[1,3]^2 )
@@ -242,7 +242,7 @@ gal_box_bound_ellipsoid_extent(double *semiaxes, double 
*euler_deg,
 
 
 
-/* Using `gal_box_bound_ellipsoid_extent', find the integer width of a box
+/* Using 'gal_box_bound_ellipsoid_extent', find the integer width of a box
    that contains the ellipsoid. */
 #define PRINT3BY3(C, A){                                                \
     printf("%s: | %-15g%-15g%-15g |\n"                                  \
@@ -419,7 +419,7 @@ gal_box_overlap(long *naxes, long *fpixel_i, long *lpixel_i,
       */
       if(fpixel_i[i]<1)
         {
-          /* Along any dimension, if `lpixel_i' is also smaller than 1,
+          /* Along any dimension, if 'lpixel_i' is also smaller than 1,
              then there is no overlap. */
           if(lpixel_i[i]<1) return 0;
 
@@ -442,7 +442,7 @@ gal_box_overlap(long *naxes, long *fpixel_i, long *lpixel_i,
         cropped image we should only fill upto c-n.*/
       if(lpixel_i[i]>naxes[i])
         {
-          /* Along any dimension, if `fpixel_i' is larger than the image
+          /* Along any dimension, if 'fpixel_i' is larger than the image
              size, there is no overlap. */
           if(fpixel_i[i]>naxes[i]) return 0;
 
diff --git a/lib/checkset.c b/lib/checkset.c
index 1464e1f..e4ddd04 100644
--- a/lib/checkset.c
+++ b/lib/checkset.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -49,7 +49,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /**************************************************************/
 /* The GSL random number generator (RNG) reads values from the
    environment. This function is designed to make the job easier for any
-   program using GSL's RNG. If the user doesn't want to set the */
+   Gnuastro program using GSL's RNG functions. */
 gsl_rng *
 gal_checkset_gsl_rng(uint8_t envseed_bool, const char **name,
                      unsigned long int *seed)
@@ -57,13 +57,13 @@ gal_checkset_gsl_rng(uint8_t envseed_bool, const char 
**name,
   gsl_rng *rng;
 
   /* Let GSL read the environment and convert the type name (as string) to
-     `gsl_rng_type'. After this function, `gsl_rng_default' contains the
-     generator's type and `gsl_rng_default_seed' contains the (possibly)
+     'gsl_rng_type'. After this function, 'gsl_rng_default' contains the
+     generator's type and 'gsl_rng_default_seed' contains the (possibly)
      given seed.*/
   gsl_rng_env_setup();
 
   /* Allocate the random number generator based on the requested type and
-     save its name. If no `GSL_RNG_TYPE' is set, then use a fixed
+     save its name. If no 'GSL_RNG_TYPE' is set, then use a fixed
      generator.*/
   rng=gsl_rng_alloc(secure_getenv("GSL_RNG_TYPE")
                     ? gsl_rng_default
@@ -71,7 +71,7 @@ gal_checkset_gsl_rng(uint8_t envseed_bool, const char **name,
   *name = gsl_rng_name(rng);
 
   /* Initialize the random number generator, depending on the
-     `envseed_bool' argument. */
+     'envseed_bool' argument. */
   *seed = ( envseed_bool
             ? gsl_rng_default_seed
             : gal_timing_time_based_rng_seed() );
@@ -85,6 +85,196 @@ gal_checkset_gsl_rng(uint8_t envseed_bool, const char 
**name,
 
 
 
+/* On the Linux kernel, due to "overcommitting" (which is activated by
+   default), malloc will not return NULL when we allocate more memory than
+   the physically available memory. It is possible to disable overcommiting
+   with root permissions, but I have not been able to find any way to do
+   this as a normal user. So the only way is to look into the
+   '/proc/meminfo' file (constantly filled by the Linux kernel) and read
+   the available memory from that.
+
+   Note that this overcommiting apparently only occurs on Linux. From what
+   I have read, other kernels are much more strict and 'malloc' will indeed
+   return NULL if there isn't any physical RAM to support it. So if the
+   '/proc/meminfo' doesn't exist, we can assume that 'malloc' works as
+   expected, until its inverse is proven. */
+size_t
+gal_checkset_ram_available(int quietmmap)
+{
+  FILE *file;
+  int keyfound=0;
+  size_t *freemem=NULL;
+  size_t linelen=80, out=GAL_BLANK_SIZE_T;
+  char *token, *line, *linecp, *saveptr, delimiters[] = " ";
+  char *meminfo="/proc/meminfo", *keyname="MemAvailable", *units="kB";
+
+  /* If /proc/meminfo exists, read it. Otherwise, don't bother doing
+     anything. */
+  if ((file = fopen(meminfo, "r")))
+    {
+      /* Allocate space to read the line. */
+      errno=0;
+      line=malloc(linelen*sizeof *line);
+      if(line==NULL)
+        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for line",
+              __func__, linelen*sizeof *line);
+
+      /* Read it line-by-line until you find 'MemAvailable'.  */
+      while( getline(&line, &linelen, file) != -1 )
+        if( !strncmp(line, keyname, 12) )
+          {
+            /* Necessary for final check: */
+            keyfound=1;
+
+            /* We need to work on a copied line to avoid messing up the
+               contents of the actual line. */
+            gal_checkset_allocate_copy(line, &linecp);
+
+            /* The first token (which we don't need). */
+            token=strtok_r(linecp, delimiters, &saveptr);
+
+            /* The second token (which is the actual number we want). */
+            token=strtok_r(NULL, delimiters, &saveptr);
+            if(token)
+              {
+                /* Read the token as a number. */
+                if( gal_type_from_string((void **)(&freemem), token,
+                                         GAL_TYPE_SIZE_T) )
+                  error(EXIT_SUCCESS, 0, "WARNING: %s: value of '%s' "
+                        "keyword couldn't be read as an integer. Hence "
+                        "the amount of available RAM couldn't be "
+                        "determined. If a large volume of data is "
+                        "provided, the program may crash. Please contact "
+                        "us at '%s' to fix the problem",
+                        meminfo, keyname, PACKAGE_BUGREPORT);
+                else
+                  {
+                    /* The third token should be the units ('kB'). If it
+                       isn't, there should be an error because we currently
+                       assume kilobytes. */
+                    token=strtok_r(NULL, delimiters, &saveptr);
+                    if(token)
+                      {
+                        /* The units should be 'kB' (for kilobytes). */
+                        if( !strncmp(token, units, 2) )
+                          out=freemem[0]*1000;
+                        else
+                          error(EXIT_SUCCESS, 0, "WARNING: %s: the units of "
+                                "the value of '%s' keyword is (usually 'kB') "
+                                "isn't recognized. Hence the amount of "
+                                "available RAM couldn't be determined. If a "
+                                "large volume of data is provided, the "
+                                "program may crash. Please contact us at "
+                                "'%s' to fix the problem", meminfo, keyname,
+                                PACKAGE_BUGREPORT);
+                      }
+                    else
+                      error(EXIT_SUCCESS, 0, "WARNING: %s: the units of the "
+                            "value of '%s' keyword (usually 'kB') couldn't "
+                            "be read as an integer. Hence the amount of "
+                            "available RAM couldn't be determined. If a "
+                            "large volume of data is provided, the program "
+                            "may crash. Please contact us at '%s' to fix "
+                            "the problem", meminfo, keyname, 
PACKAGE_BUGREPORT);
+                  }
+
+                /* Clean up. */
+                if(freemem) free(freemem);
+              }
+            else
+              error(EXIT_SUCCESS, 0, "WARNING: %s: line with the '%s' "
+                    "keyword didn't have a value. Hence the amount of "
+                    "available RAM couldn't be determined. If a large "
+                    "volume of data is provided, the program may crash. "
+                    "Please contact us at '%s' to fix the problem",
+                    meminfo, keyname, PACKAGE_BUGREPORT);
+
+            /* Clean up. */
+            free(linecp);
+          }
+
+      /* The file existed but a keyname couldn't be found. In this case we
+         should inform the user to be aware that we can't automatically
+         determine the available memory. */
+      if(keyfound==0 && quietmmap==0)
+        error(EXIT_SUCCESS, 0, "WARNING: %s: didn't contain a '%s' keyword "
+              "hence the amount of available RAM couldn't be determined. "
+              "If a large volume of data is provided, the program may "
+              "crash. Please contact us at '%s' to fix the problem",
+              meminfo, keyname, PACKAGE_BUGREPORT);
+
+      /* Close the opened file and free the line. */
+      free(line);
+      fclose(file);
+    }
+
+  /* Return the final value. */
+  return out;
+}
+
+
+
+
+
+int
+gal_checkset_need_mmap(size_t bytesize, size_t minmapsize, int quietmmap)
+{
+  int needmmap=0;
+  size_t availableram;
+  size_t minimumtommap=10000000;
+  size_t mustremainfree=250000000;
+
+  /* In case the given minmapsize is smaller than the default value of
+     'minimumtomap', then correct 'minimumtomap' to be the same as
+     'minmapsize' (the user has to have full control to over-write the
+     default value, but let them know in a warning that this is not
+     good). */
+  if(minmapsize < minimumtommap)
+    {
+      /* Let the user know that this is not a good choice and can cause
+         other problems. */
+      if(!quietmmap)
+        error(EXIT_SUCCESS, 0, "WARNING: it is recommended that minmapsize "
+              "have a value larger than %zu (it is currently %zu), see "
+              "\"Memory management\" section in the Gnuastro book for "
+              "more. To disable this warning, please use the option "
+              "'--quiet-mmap'", minimumtommap, minmapsize);
+
+      /* Set the variable. */
+      minimumtommap=minmapsize;
+    }
+
+  /* Memory mapping is only relevant here if the byte-size of the dataset
+     is larger than 'minimumtommap'. This is primarily because checking the
+     available memory can be expensive. */
+  if( bytesize >= minimumtommap )
+    {
+      /* Find the available RAM space (only relevant for Linux). */
+      availableram=gal_checkset_ram_available(quietmmap);
+
+      /* For a check:
+      printf("check: %zu (bs), %zu (ar), %zu (nu)\n",
+             bytesize, availableram, mustremainfree);
+      */
+
+      /* If the final size is larger than the user's maximum,
+         or is larger than the available memory minus 500Mb (to
+         leave the system some breathing space!), then read the
+         array into disk using memory-mapping (HDD/SSD). */
+      if( bytesize >= minmapsize
+          || availableram < mustremainfree
+          || bytesize > (availableram-mustremainfree) )
+        needmmap=1;
+    }
+
+  /* Return the final choice. */
+  return needmmap;
+}
+
+
+
+
+
 
 
 
@@ -270,7 +460,7 @@ gal_checkset_not_dir_part(char *filename)
   size_t i, l;
   char *out, *tmp=filename;
 
-  /* Find the first `/' to identify the directory */
+  /* Find the first '/' to identify the directory */
   l=strlen(filename);
   for(i=l;i!=0;--i)
     if(filename[i]=='/')
@@ -292,6 +482,88 @@ gal_checkset_not_dir_part(char *filename)
 
 
 
+/* Make an allocated copy of the input string, then remove the suffix from
+   that string. */
+char *
+gal_checkset_suffix_separate(char *name, char **outsuffix)
+{
+  char *c, *out=NULL, *suffix=NULL;
+
+  /* Make a copy of the input. */
+  gal_checkset_allocate_copy(name, &out);
+
+  /* Parse the string from the end and stop when we hit a '.'. */
+  c=out+strlen(out)-1;
+  while(c!=out)
+    {
+      /* As soon as we hit the first '.' take a copy of the string after it
+         and put it in 'suffix'. */
+      if(*c=='.')
+        {
+          gal_checkset_allocate_copy(c, &suffix);
+          *c='\0';
+          break;
+        }
+      --c;
+    }
+
+  /* Put the 'suffix' in the output pointer and return the string with no
+     suffix. */
+  *outsuffix=suffix;
+  return out;
+}
+
+
+
+
+
+/* Given a reference filename, add a format of AAAAA-XXXXXX.CCCC where
+   'AAAAA' is the base name of the 'reference' argument, 'XXXXX' is a
+   random/unique sequence of characters, and 'YYYYY' is the string given to
+   'suffix'. If 'suffix' is NULL, the suffix of 'reference' will be used.*/
+char *
+gal_checkset_make_unique_suffix(char *reference, char *suffix)
+{
+  int tmpnamefile;
+  char *nosuff, *tmpname;
+  char *out=NULL, *insuffix;
+
+  /* Remove the suffix. */
+  nosuff=gal_checkset_suffix_separate(reference, &insuffix);
+
+  /* First generate the input to 'mkstemp' (the 'XXXXXX's will be replaced
+     with a unique set of strings with same number of characters). */
+  if( asprintf(&tmpname, "%s-XXXXXX", nosuff)<0 )
+    error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
+
+  /* Generate the unique name with 'mkstemp', but it will actually open the
+     file (to make sure that the name is not used), so we need to close it
+     afterwards. */
+  tmpnamefile=mkstemp(tmpname);
+  errno=0;
+  if( close(tmpnamefile) != 0 )
+    error(EXIT_FAILURE, errno, "couldn't close temporary file");
+
+  /* Delete the temporarily created file. */
+  remove(tmpname);
+
+  /* Add the suffix. */
+  out = ( suffix
+          ? gal_checkset_malloc_cat(tmpname, suffix)
+          : ( insuffix
+              ? gal_checkset_malloc_cat(tmpname, insuffix)
+              : tmpname ) );
+
+  /* Clean up and return the output. */
+  if(tmpname!=out) free(tmpname);
+  if(insuffix) free(insuffix);
+  free(nosuff);
+  return out;
+}
+
+
+
+
 /* Check if a file exists and report if it doesn't. */
 void
 gal_checkset_check_file(char *filename)
@@ -312,7 +584,7 @@ gal_checkset_check_file(char *filename)
 
 
 
-/* Similar to `gal_checkset_check_file', but will report the result instead
+/* Similar to 'gal_checkset_check_file', but will report the result instead
    of doing it quietly. */
 int
 gal_checkset_check_file_return(char *filename)
@@ -343,14 +615,14 @@ gal_checkset_writable_notexist(char *filename)
   char *dir;
   FILE *tmpfile;
 
-  /* If the filename is `NULL' everything is ok (it doesn't exist)! In some
+  /* If the filename is 'NULL' everything is ok (it doesn't exist)! In some
      cases, a NULL filename is interpretted to mean standard output. */
   if(filename==NULL) return 1;
 
   /* We want to make sure that we can open and write to this file. But
      the user might have asked to not delete the file, so the
      contents should not be changed. Therefore we have to open it with
-     `r+`. */
+     'r+'. */
   errno=0;
   tmpfile=fopen(filename, "r+");
   if (tmpfile)                        /* The file opened. */
@@ -387,10 +659,10 @@ gal_checkset_writable_notexist(char *filename)
 
 
 
-/* Check if a file exists and can be opened. If the `keep' value is
+/* Check if a file exists and can be opened. If the 'keep' value is
    non-zero, then the file will remain untouched, otherwise, it will be
    deleted (since most programs need to make a clean output). When the file
-   is to be deleted and `dontdelete' has a non-zero value, then the file
+   is to be deleted and 'dontdelete' has a non-zero value, then the file
    won't be deleted, but the program will abort with an error, informing
    the user that the output can't be made. */
 void
@@ -399,7 +671,7 @@ gal_checkset_writable_remove(char *filename, int keep, int 
dontdelete)
   char *dir;
   FILE *tmpfile;
 
-  /* If the filename is `NULL' everything is ok (it doesn't exist)! In some
+  /* If the filename is 'NULL' everything is ok (it doesn't exist)! In some
      cases, a NULL filename is interpretted to mean standard output. */
   if(filename==NULL)
     return;
@@ -407,7 +679,7 @@ gal_checkset_writable_remove(char *filename, int keep, int 
dontdelete)
   /* We want to make sure that we can open and write to this file. But
      the user might have asked to not delete the file, so the
      contents should not be changed. Therefore we have to open it with
-     `r+`. */
+     'r+'. */
   errno=0;
   tmpfile=fopen(filename, "r+");
   if (tmpfile)                        /* The file opened. */
@@ -423,8 +695,8 @@ gal_checkset_writable_remove(char *filename, int keep, int 
dontdelete)
           /* Make sure it is ok to delete the file. */
           if(dontdelete)
             error(EXIT_FAILURE, 0, "%s already exists and you have "
-                  "asked to not remove it with the `--dontdelete` "
-                  "(`-D`) option", filename);
+                  "asked to not remove it with the '--dontdelete' "
+                  "('-D') option", filename);
 
           /* Delete the file: */
           errno=0;
@@ -444,7 +716,7 @@ gal_checkset_writable_remove(char *filename, int keep, int 
dontdelete)
       errno=0;
       if( access(dir, W_OK) )
         error(EXIT_FAILURE, errno, "cannot create any file(s) in the "
-              "directory `%s'", dir);
+              "directory '%s'", dir);
 
       /* Clean up. */
       free(dir);
@@ -537,15 +809,15 @@ gal_checkset_automatic_output(struct 
gal_options_common_params *cp,
       l=strlen(inname);
       for(i=l-1;i!=0;--i)
         {
-          /* We don't want to touch anything before a `/' (directory
+          /* We don't want to touch anything before a '/' (directory
              names). We are only concerned with file names here. */
           if(out[i]=='/')
             {
-              /* When `/' is the last input character, then the input is
+              /* When '/' is the last input character, then the input is
                  clearly not a filename, but a directory name. In this
                  case, adding a suffix is meaningless (a suffix belongs to
                  a filename for Gnuastro's tools). So close the string
-                 after the `/' and leave the loop. However, if the `/'
+                 after the '/' and leave the loop. However, if the '/'
                  isn't the last input name charector, there is probably a
                  filename (without a "." suffix), so break from the
                  loop. No further action is required, since we initially
@@ -557,9 +829,9 @@ gal_checkset_automatic_output(struct 
gal_options_common_params *cp,
             }
 
           /* The input file names can be compressed names (for example
-             `.fits.gz'). Currently the only compressed formats
+             '.fits.gz'). Currently the only compressed formats
              (decompressed within CFITSIO) are listed in
-             `gal_fits_name_is_fits' and `gal_fits_suffix_is_fits'.*/
+             'gal_fits_name_is_fits' and 'gal_fits_suffix_is_fits'.*/
           else if(out[i]=='.' && !( ( out[i+1]=='g' && out[i+2]=='z' )
                                     || (out[i+1]=='f' && out[i+2]=='z' )
                                     || out[i+1]=='Z' ) )
@@ -596,8 +868,8 @@ gal_checkset_automatic_output(struct 
gal_options_common_params *cp,
 
 
 /* Check write-ability by trying to make a temporary file. Return 0 if the
-   directory is writable, and `errno' if it isn't. We won't be using
-   `facccessat' because its not available on some systems (macOS 10.9 and
+   directory is writable, and 'errno' if it isn't. We won't be using
+   'facccessat' because its not available on some systems (macOS 10.9 and
    earlier, see https://github.com/conda-forge/staged-recipes/pull/9723
    ). */
 static int
@@ -608,7 +880,7 @@ checkset_directory_writable(char *dirname)
   char *tmpname;
 
   /* Set the template for the temporary file (accounting for the possible
-     extra `/'). */
+     extra '/'). */
   if(dirname[strlen(dirname)-1]=='/')
     tmpname=gal_checkset_malloc_cat(dirname, "gnuastroXXXXXX");
   else
@@ -664,18 +936,18 @@ gal_checkset_check_dir_write_add_slash(char **dirname)
   printf("\n\n%s\n\n", tmpname);
   if( write(file_d, buf, strlen(buf)) == -1 )
     error(EXIT_FAILURE, errno, "%s: writing to this temporary file to "
-          "check the given `%s` directory", tmpname, indir);
+          "check the given '%s' directory", tmpname, indir);
   */
   errno=0;
   if( close(file_d) == -1 )
     error(EXIT_FAILURE, errno, "%s: Closing this temporary file to check "
-          "the given `%s` directory", tmpname, indir);
+          "the given '%s' directory", tmpname, indir);
 
   /* Delete the temporary file: */
   errno=0;
   if(unlink(tmpname)==-1)
     error(EXIT_FAILURE, errno, "%s: removing this temporary file made "
-          "to check the given `%s directory`", tmpname, indir);
+          "to check the given '%s directory'", tmpname, indir);
 
   /* Remove the extra characters that were added for the random name. */
   tmpname[strlen(tmpname)-14]='\0';
@@ -691,7 +963,7 @@ gal_checkset_check_dir_write_add_slash(char **dirname)
 /* If the given directory exists and is writable, then nothing is done and
    this function returns 0. If it doesn't, it will be created. If it fails
    at creating the file, or the file isn't writable it returns a non-zero
-   value: the errno, which can be directly used in `error'. */
+   value: the errno, which can be directly used in 'error'. */
 int
 gal_checkset_mkdir(char *dirname)
 {
@@ -709,6 +981,6 @@ gal_checkset_mkdir(char *dirname)
     /* The directory exists, see if its writable. */
     errnum=checkset_directory_writable(dirname);
 
-  /* Return the final `errno'. */
+  /* Return the final 'errno'. */
   return errnum;
 }
diff --git a/lib/convolve.c b/lib/convolve.c
index 27b0574..889af94 100644
--- a/lib/convolve.c
+++ b/lib/convolve.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -128,8 +128,8 @@ struct spatial_params
 
 
 /* Define the overlap of the kernel and image over this part of the image,
-   the necessary input image parameters are stored in `overlap' (its
-   `array' and `dsize' elements).  */
+   the necessary input image parameters are stored in 'overlap' (its
+   'array' and 'dsize' elements).  */
 static int
 convolve_spatial_overlap(struct per_thread_spatial_prm *pprm, int tocorrect)
 {
@@ -164,9 +164,9 @@ convolve_spatial_overlap(struct per_thread_spatial_prm 
*pprm, int tocorrect)
       dim_full_overlap=1;
 
       /* When the tile is on the edge, some pixels in it can have full
-         overlap. So using the `dim_full_overlap', we will do the same
+         overlap. So using the 'dim_full_overlap', we will do the same
          thing we do for the tiles that don't overlap for them. When
-         `tocorrect!=0', then only pixels that are on the edge of the tile
+         'tocorrect!=0', then only pixels that are on the edge of the tile
          will get to this point, so it must always be checked. */
       if( tocorrect ? 1 : pprm->on_edge )
         {
@@ -181,7 +181,7 @@ convolve_spatial_overlap(struct per_thread_spatial_prm 
*pprm, int tocorrect)
                  With the start: assume that in this dimension, the pixel
                  is at position 2, while the kernel is 11 pixels wide (or 5
                  pixels in half-width). As seen below, the kernel should
-                 start from pixel `5-2=3' in this dimension and the overlap
+                 start from pixel '5-2=3' in this dimension and the overlap
                  size should decrease by the same amount.
 
                     image:            0 1 2 3 4 5 6 7 8 9 ...
@@ -193,7 +193,7 @@ convolve_spatial_overlap(struct per_thread_spatial_prm 
*pprm, int tocorrect)
                  no longer worried about the overlap or kernel starting
                  point, it is the width that we need to decrease it by:
 
-                   97 + 5 - 100 + 1 : The `1' is because we want the pixel
+                   97 + 5 - 100 + 1 : The '1' is because we want the pixel
                                       immediately after the end.
 
                     image:        ... 92 93 94 95 96 97 98 99 | 100 101 102
@@ -210,7 +210,7 @@ convolve_spatial_overlap(struct per_thread_spatial_prm 
*pprm, int tocorrect)
               if(is_start) *od -= *k/2 - *p;
               if(is_end)   *od -= *p + *k/2 - *h + 1;
 
-              /* Put the overlap size into the kernel's overlap `dsize'
+              /* Put the overlap size into the kernel's overlap 'dsize'
                  also and then use it to update the total size of the
                  overlap. */
               *kd++ = *od;
@@ -244,13 +244,13 @@ convolve_spatial_overlap(struct per_thread_spatial_prm 
*pprm, int tocorrect)
   while(++p<pf);
 
 
-  /* Update the `size' element of both overlap datasets. */
+  /* Update the 'size' element of both overlap datasets. */
   pprm->i_overlap->size = pprm->k_overlap->size = size;
 
 
   /* Make correction.
 
-      Normal mode (when `tocorrect==0'): add the host's starting location
+      Normal mode (when 'tocorrect==0'): add the host's starting location
          (necessary when convolution over the host/channel is treated
          independently). In this mode, until now we were working as if the
          the host/channel is the full image so the edges don't get
@@ -259,7 +259,7 @@ convolve_spatial_overlap(struct per_thread_spatial_prm 
*pprm, int tocorrect)
          proper place within the allocated array.
 
       To-correct mode: The boundaries were calculated with respect to the
-         block, so we don't need to correct `overlap_start'. But we need to
+         block, so we don't need to correct 'overlap_start'. But we need to
          correct the pixel position back to its original state (relative to
          the channel). */
   hs=pprm->host_start;
@@ -304,8 +304,8 @@ convolve_spatial_tile(struct per_thread_spatial_prm *pprm)
   size_t j, ndim=block->ndim, csize=tile->dsize[ndim-1];
   gal_data_t *i_overlap=pprm->i_overlap, *k_overlap=pprm->k_overlap;
 
-  /* Variables for scanning a tile (`i_*') and the region around every
-     pixel of a tile (`o_*'). */
+  /* Variables for scanning a tile ('i_*') and the region around every
+     pixel of a tile ('o_*'). */
   size_t start_fastdim;
   size_t i_inc, i_ninc, i_st_en[2];
 
@@ -315,17 +315,17 @@ convolve_spatial_tile(struct per_thread_spatial_prm *pprm)
 
 
   /* Starting pixel for the host of this tile. Note that when we are in
-     `convoverch' mode, `host' refers to the fully allocated block of
+     'convoverch' mode, 'host' refers to the fully allocated block of
      memory. */
   pprm->host=cprm->convoverch ? block : tile->block;
   gal_tile_start_coord(pprm->host, pprm->host_start);
 
 
   /* Set the starting and ending coordinates of this tile (recall that the
-     space for the start and end coordinates is stored in `p->pix'). When
-     `convoverch' is set, we want to convolve over the whole allocated
+     space for the start and end coordinates is stored in 'p->pix'). When
+     'convoverch' is set, we want to convolve over the whole allocated
      block, not just one channel. So in effect, it is the same as
-     `rel_block' in `gal_tile_start_end_coord'. */
+     'rel_block' in 'gal_tile_start_end_coord'. */
   gal_tile_start_end_coord(tile, pprm->pix, cprm->convoverch);
   start_fastdim = pprm->pix[ndim-1];
 
@@ -336,7 +336,7 @@ convolve_spatial_tile(struct per_thread_spatial_prm *pprm)
 
 
   /* If it isn't on the edge and we are correcting an already convolved
-     image (`tocorrect!=NULL'), then this tile can be ignored. */
+     image ('tocorrect!=NULL'), then this tile can be ignored. */
   if(cprm->tocorrect && pprm->on_edge==0) return;
 
 
@@ -346,7 +346,7 @@ convolve_spatial_tile(struct per_thread_spatial_prm *pprm)
   while( i_st_en[0] + i_inc <= i_st_en[1] )
     {
       /* Initialize the value along the fastest dimension (it is not
-         incremented during `gal_tile_block_increment'). */
+         incremented during 'gal_tile_block_increment'). */
       pprm->pix[ndim-1]=start_fastdim;
 
       /* Go over each pixel to convolve. */
@@ -356,7 +356,7 @@ convolve_spatial_tile(struct per_thread_spatial_prm *pprm)
           in_v = i_start + i_inc + j;
 
           /* If the input on this pixel is a NaN, then just set the output
-             to NaN too and go onto the next pixel. `in_v' is the pointer
+             to NaN too and go onto the next pixel. 'in_v' is the pointer
              on this pixel. */
           if( isnan(*in_v) )
             out[ in_v - in ]=NAN;
@@ -428,7 +428,7 @@ convolve_spatial_on_thread(void *inparam)
                                      "dsize");
 
 
-  /* Set all dsize values to 1 (the values within `overlap->dsize' will be
+  /* Set all dsize values to 1 (the values within 'overlap->dsize' will be
      changed during convolution). */
   for(i=0;i<ndim;++i) dsize[i]=1;
 
@@ -467,7 +467,7 @@ convolve_spatial_on_thread(void *inparam)
 
 
   /* Clean up, wait until all other threads finish, then return. In a
-     single thread situation, `tprm->b==NULL'. */
+     single thread situation, 'tprm->b==NULL'. */
   free(pprm->pix);
   free(pprm->host_start);
   free(pprm->kernel_start);
@@ -483,7 +483,7 @@ convolve_spatial_on_thread(void *inparam)
 
 
 /* General spatial convolve function. This function is called by both
-   `gal_convolve_spatial' and */
+   'gal_convolve_spatial' and */
 static gal_data_t *
 gal_convolve_spatial_general(gal_data_t *tiles, gal_data_t *kernel,
                              size_t numthreads, int edgecorrection,
@@ -498,7 +498,7 @@ gal_convolve_spatial_general(gal_data_t *tiles, gal_data_t 
*kernel,
     error(EXIT_FAILURE, 0, "%s: The number of dimensions between the kernel "
           "and input should be the same", __func__);
   if( block->type!=GAL_TYPE_FLOAT32 || kernel->type!=GAL_TYPE_FLOAT32 )
-    error(EXIT_FAILURE, 0, "%s: only accepts `float32' type input and "
+    error(EXIT_FAILURE, 0, "%s: only accepts 'float32' type input and "
           "kernel currently", __func__);
 
   /* It may happen that an input dataset is part of a linked list, but it
@@ -508,8 +508,8 @@ gal_convolve_spatial_general(gal_data_t *tiles, gal_data_t 
*kernel,
   if( tiles->block==NULL && tiles->next && tiles->next->block==NULL )
     error(EXIT_FAILURE, 0, "%s: the input is a linked list but not a "
           "tessellation (a list of tiles). This function is optimized to "
-          "work on a list of tiles. Please (temporarily) set the `next' "
-          "element of the input to `NULL' and call this function again",
+          "work on a list of tiles. Please (temporarily) set the 'next' "
+          "element of the input to 'NULL' and call this function again",
           __func__);
 
 
@@ -543,13 +543,14 @@ gal_convolve_spatial_general(gal_data_t *tiles, 
gal_data_t *kernel,
   errno=0;
   params.pprm=malloc(numthreads * sizeof *params.pprm);
   if(params.pprm==NULL)
-    error(EXIT_FAILURE, 0, "%s: %zu bytes for `params.pprm'",
+    error(EXIT_FAILURE, 0, "%s: %zu bytes for 'params.pprm'",
           __func__, numthreads * sizeof *params.pprm);
 
 
   /* Do the spatial convolution on threads. */
   gal_threads_spin_off(convolve_spatial_on_thread, &params,
-                       gal_list_data_number(tiles), numthreads);
+                       gal_list_data_number(tiles), numthreads,
+                       tiles->minmapsize, tiles->quietmmap);
 
 
   /* Clean up and return the output array. */
@@ -565,12 +566,12 @@ gal_convolve_spatial_general(gal_data_t *tiles, 
gal_data_t *kernel,
    convolution can be greatly sped up if it is done on separate tiles over
    the image (on multiple threads). So as input, you can either give tile
    values or one full array. Just note that if you give a single array as
-   input, the `next' element has to be `NULL'.*/
+   input, the 'next' element has to be 'NULL'.*/
 gal_data_t *
 gal_convolve_spatial(gal_data_t *tiles, gal_data_t *kernel,
                      size_t numthreads, int edgecorrection, int convoverch)
 {
-  /* When there isn't any tile structure, `convoverch' must be set to
+  /* When there isn't any tile structure, 'convoverch' must be set to
      one. Recall that the input can be a single full dataset also. */
   if(tiles->block==NULL) convoverch=1;
 
@@ -584,7 +585,7 @@ gal_convolve_spatial(gal_data_t *tiles, gal_data_t *kernel,
 
 
 /* Correct the edges of channels in an already convolved image when it was
-   initially convolved with `gal_convolve_spatial' with `convoverch==0'. In
+   initially convolved with 'gal_convolve_spatial' with 'convoverch==0'. In
    that case, strong boundaries exist on the tile edges. So if you later
    need to remove those boundaries, you can call this function, it will
    only do convolution on the tiles that are near the edge, not the full
@@ -598,12 +599,12 @@ gal_convolve_spatial_correct_ch_edge(gal_data_t *tiles, 
gal_data_t *kernel,
 
   /* Some small sanity checks. */
   if( gal_dimension_is_different(block, tocorrect) )
-    error(EXIT_FAILURE, 0, "%s: the `tocorrect' dataset has to have the "
-          "same dimensions/size as the block of the `tiles' input", __func__);
+    error(EXIT_FAILURE, 0, "%s: the 'tocorrect' dataset has to have the "
+          "same dimensions/size as the block of the 'tiles' input", __func__);
   if( block->type != tocorrect->type )
-    error(EXIT_FAILURE, 0, "%s: the `tocorrect' dataset has to have the same "
-          "type as the block of the `tiles' input. The given types are `%s' "
-          "and `%s' respectively", __func__,
+    error(EXIT_FAILURE, 0, "%s: the 'tocorrect' dataset has to have the same "
+          "type as the block of the 'tiles' input. The given types are '%s' "
+          "and '%s' respectively", __func__,
           gal_type_name(tocorrect->type, 1), gal_type_name(block->type, 1));
 
   /* Call the general function, which will do the correction. */
diff --git a/lib/cosmology.c b/lib/cosmology.c
index b8d243b..4291fbd 100644
--- a/lib/cosmology.c
+++ b/lib/cosmology.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -310,3 +310,27 @@ gal_cosmology_to_absolute_mag(double z, double H0, double 
o_lambda_0,
                                            o_radiation_0);
   return dm-2.5*log10(1.0+z);
 }
+
+
+
+
+
+/* Velocity at given redshift in units of km/s. */
+double
+gal_cosmology_velocity_from_z(double z)
+{
+  double c=GSL_CONST_MKSA_SPEED_OF_LIGHT;
+  return c * ( (1+z)*(1+z) - 1 ) / ( (1+z)*(1+z) + 1 ) / 1000;
+}
+
+
+
+
+
+/* Redshift at given velocity (in units of km/s). */
+double
+gal_cosmology_z_from_velocity(double v)
+{
+  double c=GSL_CONST_MKSA_SPEED_OF_LIGHT/1000;
+  return sqrt( (c+v)/(c-v) ) - 1;
+}
diff --git a/lib/data.c b/lib/data.c
index fd112cc..f8eb11b 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -96,15 +96,15 @@ gal_data_alloc(void *array, uint8_t type, size_t ndim, 
size_t *dsize,
 
    Some notes:
 
-   - The `status' value is the only element that cannot be set by this
+   - The 'status' value is the only element that cannot be set by this
      function, it is initialized to zero.
 
-   - If no `array' is given, a blank array of the given size will be
+   - If no 'array' is given, a blank array of the given size will be
      allocated. If it is given the array pointer will be directly put here,
      so do not free it independently any more. If you want a separate copy
-     of a dataset, you should use `gal_data_copy', not this function.
+     of a dataset, you should use 'gal_data_copy', not this function.
 
-   - Space for the `name', `unit', and `comment' strings within the data
+   - Space for the 'name', 'unit', and 'comment' strings within the data
      structure are allocated here. So you can safely use literal strings,
      or statically allocated ones, or simply the strings from other data
      structures (and not have to worry about which one to free later).
@@ -141,8 +141,8 @@ gal_data_initialize(gal_data_t *data, void *array, uint8_t 
type,
 
 
   /* Allocate space for the dsize array, only if the data are to have any
-     dimensions. Note that in our convention, a number has a `ndim=1' and
-     `dsize[0]=1', A 1D array also has `ndim=1', but `dsize[0]>1'. */
+     dimensions. Note that in our convention, a number has a 'ndim=1' and
+     'dsize[0]=1', A 1D array also has 'ndim=1', but 'dsize[0]>1'. */
   if(ndim)
     {
       /* Allocate dsize. */
@@ -153,12 +153,14 @@ gal_data_initialize(gal_data_t *data, void *array, 
uint8_t type,
               __func__, ndim*sizeof *data->dsize);
 
 
-      /* Fill in the `dsize' array and in the meantime set `size': */
+      /* Fill in the 'dsize' array and in the meantime set 'size': */
       data->size=1;
       for(i=0;i<ndim;++i)
         {
-          /* Size along a dimension cannot be negative. */
-          if(dsize[i] == 0)
+          /* Size along a dimension cannot be 0 if we are in a
+             multi-dimensional dataset. In a single-dimensional dataset, we
+             can have an empty dataset. */
+          if(ndim>1 && dsize[i] == 0)
             error(EXIT_FAILURE, 0, "%s: dsize[%zu]==0. The size of a "
                   "dimension cannot be zero", __func__, i);
 
@@ -188,19 +190,11 @@ gal_data_initialize(gal_data_t *data, void *array, 
uint8_t type,
         data->array=array;
       else
         {
+          /* If a size wasn't given, just set a NULL pointer. */
           if(data->size)
-            {
-              if( gal_type_sizeof(type)*data->size > minmapsize )
-                /* Allocate the space into disk (HDD/SSD). */
-                data->array=gal_pointer_allocate_mmap(data->type, data->size,
-                                                      clear, &data->mmapname,
-                                                      quietmmap);
-              else
-                /* Allocate the space in RAM. */
-                data->array = gal_pointer_allocate(data->type, data->size,
-                                                   clear, __func__,
-                                                   "data->array");
-            }
+            data->array=gal_pointer_allocate_ram_or_mmap(data->type,
+                                 data->size, clear, minmapsize,
+                                 &data->mmapname, quietmmap, __func__, "");
           else data->array=NULL; /* The given size was zero! */
         }
     }
@@ -217,17 +211,17 @@ gal_data_initialize(gal_data_t *data, void *array, 
uint8_t type,
 
 
 /* Free the allocated contents of a data structure, not the structure
-   itsself. The reason that this function is separate from `gal_data_free'
+   itsself. The reason that this function is separate from 'gal_data_free'
    is that the data structure might be allocated as an array (statically
-   like `gal_data_t da[20]', or dynamically like `gal_data_t *da;
+   like 'gal_data_t da[20]', or dynamically like 'gal_data_t *da;
    da=malloc(20*sizeof *da);'). In both cases, a loop will be necessary to
    delete the allocated contents of each element of the data structure
    array, but not the structure its self. After that loop, if the array of
    data structures was statically allocated, you don't have to do
    anything. If it was dynamically allocated, we just have to run
-   `free(da)'.
+   'free(da)'.
 
-   Since we aren't freeing the `gal_data_t' its-self, after the allocated
+   Since we aren't freeing the 'gal_data_t' its-self, after the allocated
    space for each pointer is freed, the pointer is set to NULL for safety
    (to avoid possible re-calls).
 */
@@ -239,14 +233,15 @@ gal_data_free_contents(gal_data_t *data)
 
   if(data==NULL)
     error(EXIT_FAILURE, 0, "%s: the input data structure to "
-          "`gal_data_free_contents' was a NULL pointer", __func__);
+          "'gal_data_free_contents' was a NULL pointer", __func__);
 
   /* Free all the possible allocations. */
   if(data->name)    { free(data->name);    data->name    = NULL; }
   if(data->unit)    { free(data->unit);    data->unit    = NULL; }
   if(data->dsize)   { free(data->dsize);   data->dsize   = NULL; }
-  if(data->wcs)     { wcsfree(data->wcs);  data->wcs     = NULL; }
   if(data->comment) { free(data->comment); data->comment = NULL; }
+  if(data->wcs)
+    { wcsfree(data->wcs); free(data->wcs); data->wcs     = NULL; }
 
   /* If the data type is string, then each element in the array is actually
      a pointer to the array of characters, so free them before freeing the
@@ -257,24 +252,14 @@ gal_data_free_contents(gal_data_t *data)
       for(i=0;i<data->size;++i) if(strarr[i]) free(strarr[i]);
     }
 
-  /* Free the array. */
-  if(data->mmapname)
+  /* Free the array (if it was separately allocated: not part of a block),
+     then set the 'array' to NULL. */
+  if(data->array && data->block==NULL)
     {
-      /* Delete the file keeping the array. */
-      remove(data->mmapname);
-
-      /* Inform the user. */
-      if(!data->quietmmap)
-        error(EXIT_SUCCESS, 0, "%s: deleted", data->mmapname);
-
-      /* Free the file name space. */
-      free(data->mmapname);
-
-      /* Set the name pointer to NULL since it has been freed. */
-      data->mmapname=NULL;
+      if(data->mmapname)
+        gal_pointer_mmap_free(&data->mmapname, data->quietmmap);
+      else free(data->array);
     }
-  else
-    if(data->array && data->block==NULL) free(data->array);
   data->array=NULL;
 }
 
@@ -327,14 +312,14 @@ gal_data_array_calloc(size_t size)
   errno=0;
   out=malloc(size*sizeof *out);
   if(out==NULL)
-    error(EXIT_FAILURE, errno, "%s: %zu bytes for `out'", __func__,
+    error(EXIT_FAILURE, errno, "%s: %zu bytes for 'out'", __func__,
           size*sizeof *out);
 
 
   /* Set the pointers to NULL if they didn't exist and the non-pointers to
      impossible integers (so the caller knows the array is only
-     allocated. `minmapsize' should be set when allocating the array and
-     should be set when you run `gal_data_initialize'. */
+     allocated. 'minmapsize' should be set when allocating the array and
+     should be set when you run 'gal_data_initialize'. */
   for(i=0;i<size;++i)
     {
       out[i].array      = NULL;
@@ -391,6 +376,55 @@ gal_data_array_free(gal_data_t *dataarr, size_t size, int 
free_array)
 
 
 
+/* Create an array of gal_data_t pointers and initializes them. */
+gal_data_t **
+gal_data_array_ptr_calloc(size_t size)
+{
+  size_t i;
+  gal_data_t **out;
+
+  /* Allocate the array to keep the pointers. */
+  errno=0;
+  out=malloc(size*sizeof *out);
+  if(out==NULL)
+    error(EXIT_FAILURE, errno, "%s: %zu bytes for 'out'", __func__,
+          size*sizeof *out);
+
+  /* Initialize all the pointers to NULL and return. */
+  for(i=0;i<size;++i) out[i]=NULL;
+  return out;
+}
+
+
+
+
+
+/* Assuming that we have an array of pointers to data structures, this
+   function frees them. */
+void
+gal_data_array_ptr_free(gal_data_t **dataptr, size_t size, int free_array)
+{
+  size_t i;
+  for(i=0;i<size;++i)
+    {
+      /* If the user doesn't want to free the array, it must be because
+         they are keeping its pointer somewhere else (that their own
+         responsability!), so we can just set it to NULL for the
+         'gal_data_free' to not free it. */
+      if(free_array==0)
+        dataptr[i]->array=NULL;
+
+      /* Free this data structure. */
+      gal_data_free(dataptr[i]);
+    }
+
+  /* Free the 'gal_data_t **'. */
+  free(dataptr);
+}
+
+
+
+
 
 
 
@@ -409,14 +443,14 @@ gal_data_array_free(gal_data_t *dataarr, size_t size, int 
free_array)
 /*************************************************************
  **************            Copying             ***************
  *************************************************************/
-/* Only to be used in `data_copy_from_string'. */
+/* Only to be used in 'data_copy_from_string'. */
 static void
 data_copy_to_string_not_parsed(char *string, void *to, uint8_t type)
 {
   if( strcmp(string, GAL_BLANK_STRING) )
     gal_blank_write(to, type);
   else
-    error(EXIT_FAILURE, 0, "%s: `%s' couldn't be parsed as `%s' type",
+    error(EXIT_FAILURE, 0, "%s: '%s' couldn't be parsed as '%s' type",
           __func__, string, gal_type_name(type, 1));
 }
 
@@ -424,7 +458,7 @@ data_copy_to_string_not_parsed(char *string, void *to, 
uint8_t type)
 
 
 
-/* The `from' array is an array of strings. We want to keep it as
+/* The 'from' array is an array of strings. We want to keep it as
    numbers. Note that the case where both input and output structures are
    string was */
 static void
@@ -436,9 +470,9 @@ data_copy_from_string(gal_data_t *from, gal_data_t *to)
 
   /* Sanity check. */
   if(from->type!=GAL_TYPE_STRING)
-    error(EXIT_FAILURE, 0, "%s: `from' must have a string type.", __func__);
+    error(EXIT_FAILURE, 0, "%s: 'from' must have a string type.", __func__);
   if(from->block)
-    error(EXIT_FAILURE, 0, "%s: tiles not currently supported (`block' "
+    error(EXIT_FAILURE, 0, "%s: tiles not currently supported ('block' "
           "element must be NULL). Please contact us at %s so we can "
           "implement this feature", __func__, PACKAGE_BUGREPORT);
 
@@ -517,7 +551,7 @@ data_copy_from_string(gal_data_t *from, gal_data_t *to)
   }
 
 /* Convert any given type into a string by printing it into the elements of
-   the already allocated `to->array'. */
+   the already allocated 'to->array'. */
 static void
 data_copy_to_string(gal_data_t *from, gal_data_t *to)
 {
@@ -527,10 +561,10 @@ data_copy_to_string(gal_data_t *from, gal_data_t *to)
 
   /* Sanity check */
   if(to->type!=GAL_TYPE_STRING)
-    error(EXIT_FAILURE, 0, "%s: `to' must have a string type", __func__);
+    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 "
+          "('block' element must be NULL). Please contact us at %s so we "
           "can implement this feature", __func__, PACKAGE_BUGREPORT);
 
   /* Do the copying */
@@ -580,7 +614,7 @@ data_copy_to_string(gal_data_t *from, gal_data_t *to)
       break;
 
     default:
-      error(EXIT_FAILURE, 0, "%s: type %d not recognized for `from->type'",
+      error(EXIT_FAILURE, 0, "%s: type %d not recognized for 'from->type'",
             __func__, from->type);
     }
 }
@@ -612,7 +646,7 @@ data_copy_to_string(gal_data_t *from, gal_data_t *to)
     /* Parse over the input and copy it. */                             \
     while( s_e_ind[0] + increment <= s_e_ind[1] )                       \
       {                                                                 \
-        /* If we are on a tile, reset `i' and  `f' for each round. */   \
+        /* If we are on a tile, reset 'i' and  'f' for each round. */   \
         if(in!=iblock)                                                  \
           f = ( i = ist + increment ) + contig_len;                     \
                                                                         \
@@ -629,7 +663,7 @@ data_copy_to_string(gal_data_t *from, gal_data_t *to)
             /* If the blank is a NaN value (only for floating point  */ \
             /* types), it will fail any comparison, so we'll exploit */ \
             /* this property in such cases. For other cases, a       */ \
-            /* `*i==ib' is enough.                                   */ \
+            /* '*i==ib' is enough.                                   */ \
             if(ib==ib) do *o++ = *i==ib ? ob : *i; while(++i<f);        \
             else       do *o++ = *i!=*i ? ob : *i; while(++i<f);        \
           }                                                             \
@@ -673,14 +707,14 @@ data_copy_to_string(gal_data_t *from, gal_data_t *to)
                                                                         \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: type code %d not recognized for "     \
-            "`in->type'", "COPY_OT_SET", in->type);                     \
+            "'in->type'", "COPY_OT_SET", in->type);                     \
     }
 
 
 
 
 
-/* Wrapper for `gal_data_copy_to_new_type', but will copy to the same type
+/* Wrapper for 'gal_data_copy_to_new_type', but will copy to the same type
    as the input. Recall that if the input is a tile (a part of the input,
    which is not-contiguous if it has more than one dimension), then the
    output will have only the elements that cover the tile.*/
@@ -740,10 +774,10 @@ gal_data_copy_to_new_type_free(gal_data_t *in, uint8_t 
newtype)
         gal_data_free(in);
       else
         fprintf(stderr, "#####\nWarning from "
-                "`gal_data_copy_to_new_type_free'\n#####\n The input "
+                "'gal_data_copy_to_new_type_free'\n#####\n The input "
                 "dataset is a tile, not a contiguous (fully allocated) "
                 "patch of memory. So it has not been freed. Please use "
-                "`gal_data_copy_to_new_type' to avoid this warning.\n"
+                "'gal_data_copy_to_new_type' to avoid this warning.\n"
                 "#####");
       return out;
     }
@@ -753,21 +787,21 @@ 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' (except
-   for `block') will be fully copied into `out' also. `out->size' will be
+/* Copy a given dataset ('in') into an already allocated dataset 'out' (the
+   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:
+   When 'in->size != out->size' this function will behave as follows:
 
-      `out->size < in->size': it won't re-allocate the necessary space, it
+      'out->size < in->size': it won't re-allocate the necessary space, it
           will abort with an error, so please check before calling this
           function.
 
-      `out->size > in->size': it will update `out->size' and `out->dsize'
+      'out->size > in->size': it will update 'out->size' and 'out->dsize'
           to be the same as the input. So if you want to re-use a
           pre-allocated space with varying input sizes, be sure to reset
-          `out->size' before every call to this function. */
+          'out->size' before every call to this function. */
 void
 gal_data_copy_to_allocated(gal_data_t *in, gal_data_t *out)
 {
@@ -801,32 +835,34 @@ gal_data_copy_to_allocated(gal_data_t *in, gal_data_t 
*out)
   gal_checkset_allocate_copy(in->comment, &out->comment);
 
   /* Do the copying. */
-  switch(out->type)
-    {
-    case GAL_TYPE_UINT8:   COPY_OT_SET( uint8_t  );      break;
-    case GAL_TYPE_INT8:    COPY_OT_SET( int8_t   );      break;
-    case GAL_TYPE_UINT16:  COPY_OT_SET( uint16_t );      break;
-    case GAL_TYPE_INT16:   COPY_OT_SET( int16_t  );      break;
-    case GAL_TYPE_UINT32:  COPY_OT_SET( uint32_t );      break;
-    case GAL_TYPE_INT32:   COPY_OT_SET( int32_t  );      break;
-    case GAL_TYPE_UINT64:  COPY_OT_SET( uint64_t );      break;
-    case GAL_TYPE_INT64:   COPY_OT_SET( int64_t  );      break;
-    case GAL_TYPE_FLOAT32: COPY_OT_SET( float    );      break;
-    case GAL_TYPE_FLOAT64: COPY_OT_SET( double   );      break;
-    case GAL_TYPE_STRING:  data_copy_to_string(in, out); break;
-
-    case GAL_TYPE_BIT:
-    case GAL_TYPE_STRLL:
-    case GAL_TYPE_COMPLEX32:
-    case GAL_TYPE_COMPLEX64:
-      error(EXIT_FAILURE, 0, "%s: copying to %s type not yet supported",
-            __func__, gal_type_name(out->type, 1));
-      break;
-
-    default:
-      error(EXIT_FAILURE, 0, "%s: type %d not recognized for `out->type'",
-            __func__, out->type);
-    }
+  if(in->array)
+    switch(out->type)
+      {
+      case GAL_TYPE_UINT8:   COPY_OT_SET( uint8_t  );      break;
+      case GAL_TYPE_INT8:    COPY_OT_SET( int8_t   );      break;
+      case GAL_TYPE_UINT16:  COPY_OT_SET( uint16_t );      break;
+      case GAL_TYPE_INT16:   COPY_OT_SET( int16_t  );      break;
+      case GAL_TYPE_UINT32:  COPY_OT_SET( uint32_t );      break;
+      case GAL_TYPE_INT32:   COPY_OT_SET( int32_t  );      break;
+      case GAL_TYPE_UINT64:  COPY_OT_SET( uint64_t );      break;
+      case GAL_TYPE_INT64:   COPY_OT_SET( int64_t  );      break;
+      case GAL_TYPE_FLOAT32: COPY_OT_SET( float    );      break;
+      case GAL_TYPE_FLOAT64: COPY_OT_SET( double   );      break;
+      case GAL_TYPE_STRING:  data_copy_to_string(in, out); break;
+
+      case GAL_TYPE_BIT:
+      case GAL_TYPE_STRLL:
+      case GAL_TYPE_COMPLEX32:
+      case GAL_TYPE_COMPLEX64:
+        error(EXIT_FAILURE, 0, "%s: copying to %s type not yet supported",
+              __func__, gal_type_name(out->type, 1));
+        break;
+
+      default:
+        error(EXIT_FAILURE, 0, "%s: type %d not recognized for 'out->type'",
+              __func__, out->type);
+      }
+  else out->array=NULL;
 
   /* Correct the sizes of the output to be the same as the input. If it is
      equal, there is no problem, if not, the size information will be
@@ -840,8 +876,8 @@ gal_data_copy_to_allocated(gal_data_t *in, gal_data_t *out)
 
 
 
-/* Just a wrapper around `gal_type_from_string_auto', to return a
-   `gal_data_t' dataset hosting the allocated number. */
+/* Just a wrapper around 'gal_type_from_string_auto', to return a
+   'gal_data_t' dataset hosting the allocated number. */
 gal_data_t *
 gal_data_copy_string_to_number(char *string)
 {
diff --git a/lib/dimension.c b/lib/dimension.c
index 47a98f8..033b972 100644
--- a/lib/dimension.c
+++ b/lib/dimension.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -78,7 +78,7 @@ gal_dimension_is_different(gal_data_t *first, gal_data_t 
*second)
 
 
 /* Calculate the values necessary to increment/decrement along each
-   dimension of a dataset with size `dsize'. */
+   dimension of a dataset with size 'dsize'. */
 size_t *
 gal_dimension_increment(size_t ndim, size_t *dsize)
 {
@@ -187,10 +187,10 @@ gal_dimension_coord_to_index(size_t ndim, size_t *dsize, 
size_t *coord)
 
 
 
-/* You know the index (`ind') of a point/tile in an n-dimensional (`ndim')
-   array which has `dsize[i]' elements along dimension `i'. You want to
+/* You know the index ('ind') of a point/tile in an n-dimensional ('ndim')
+   array which has 'dsize[i]' elements along dimension 'i'. You want to
    know the coordinates of that point along each dimension. The output is
-   not actually returned, it must be allocated (`ndim' elements) before
+   not actually returned, it must be allocated ('ndim' elements) before
    calling this function. This function will just fill it. The reason for
    this is that this function will often be called with a loop and a single
    allocated space would be enough for the whole loop. */
@@ -285,6 +285,60 @@ gal_dimension_dist_radial(size_t *a, size_t *b, size_t 
ndim)
 
 
 
+/* Elliptical distance of a point from a given center. */
+#define DEGREESTORADIANS   M_PI/180.0
+float
+gal_dimension_dist_elliptical(double *center, double *pa_deg, double *q,
+                              size_t ndim, double *point)
+{
+  double Xr, Yr, Zr;        /* Rotated x, y, z. */
+  double q1=q[0], q2;
+  double c1=cos( pa_deg[0] * DEGREESTORADIANS ), c2, c3;
+  double s1= sin( pa_deg[0] * DEGREESTORADIANS ), s2, s3;
+  double x=center[0]-point[0], y=center[1]-point[1], z;
+
+  /* Find the distance depending on the dimension. */
+  switch(ndim)
+    {
+    case 2:
+      /* The parenthesis aren't necessary, but help in readability and
+         avoiding human induced bugs. */
+      Xr = x * ( c1       )     +   y * ( s1 );
+      Yr = x * ( -1.0f*s1 )     +   y * ( c1 );
+      return sqrt( Xr*Xr + Yr*Yr/q1/q1 );
+      break;
+
+    case 3:
+      /* Define the necessary parameters. */
+      q2=q[1];
+      z=center[2]-point[2];
+      c2 = cos( pa_deg[1] * DEGREESTORADIANS );
+      s2 = sin( pa_deg[1] * DEGREESTORADIANS );
+      c3 = cos( pa_deg[2] * DEGREESTORADIANS );
+      s3 = sin( pa_deg[2] * DEGREESTORADIANS );
+
+      /* Calculate the distance. */
+      Xr = x*(  c3*c1   - s3*c2*s1 ) + y*( c3*s1   + s3*c2*c1) + z*( s3*s2 );
+      Yr = x*( -1*s3*c1 - c3*c2*s1 ) + y*(-1*s3*s1 + c3*c2*c1) + z*( c3*s2 );
+      Zr = x*(  s1*s2              ) + y*(-1*s2*c1           ) + z*( c2    );
+      return sqrt( Xr*Xr + Yr*Yr/q1/q1 + Zr*Zr/q2/q2 );
+      break;
+
+    default:
+      error(EXIT_FAILURE, 0, "%s: currently only 2 and 3 dimensional "
+            "distances are supported, you have asked for an %zu-dimensional "
+            "dataset", __func__, ndim);
+
+    }
+
+  /* Control should never reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to address the "
+        "problem. Control should not reach the end of this function",
+        __func__, PACKAGE_BUGREPORT);
+  return NAN;
+}
+
+
 
 
 
@@ -335,7 +389,7 @@ dimension_collapse_sanity_check(gal_data_t *in, gal_data_t 
*weight,
 
   /* If there is no blank value, there is no point in calculating the
      number of points in each collapsed dataset (when necessary). In that
-     case, `cnum!=0'. */
+     case, 'cnum!=0'. */
   if(hasblank==0)
     *cnum=in->dsize[c_dim];
 
@@ -411,7 +465,7 @@ dimension_collapse_sizes(gal_data_t *in, size_t c_dim, 
size_t *outndim,
 #define COLLAPSE_CHECKBLANK(OIND,IIND) {                                \
     if(hasblank)                                                        \
       {                                                                 \
-        if(B==B) /* An integer type: blank can be checked with `=='. */ \
+        if(B==B) /* An integer type: blank can be checked with '=='. */ \
           {                                                             \
             if( inarr[IIND] != B )           COLLAPSE_WRITE(OIND,IIND); \
           }                                                             \
@@ -548,7 +602,7 @@ gal_dimension_collapse_sum(gal_data_t *in, size_t c_dim, 
gal_data_t *weight)
             __func__, in->type);
     }
 
-  /* If `num' is zero on any element, set its sum to NaN. */
+  /* If 'num' is zero on any element, set its sum to NaN. */
   if(num)
     {
       ii = num->array;
@@ -557,8 +611,8 @@ gal_dimension_collapse_sum(gal_data_t *in, size_t c_dim, 
gal_data_t *weight)
     }
 
   /* Remove the respective dimension in the WCS structure also (if any
-     exists). Note that `sum->ndim' has already been changed. So we'll use
-     `in->wcs'. */
+     exists). Note that 'sum->ndim' has already been changed. So we'll use
+     'in->wcs'. */
   gal_wcs_remove_dimension(sum->wcs, in->ndim-c_dim);
 
   /* Clean up and return. */
@@ -642,7 +696,7 @@ gal_dimension_collapse_mean(gal_data_t *in, size_t c_dim,
             __func__, in->type);
     }
 
-  /* If `num' is zero on any element, set its sum to NaN. */
+  /* If 'num' is zero on any element, set its sum to NaN. */
   if(num)
     {
       ii = num->array;
@@ -724,8 +778,8 @@ gal_dimension_collapse_number(gal_data_t *in, size_t c_dim)
     }
 
   /* Remove the respective dimension in the WCS structure also (if any
-     exists). Note that `sum->ndim' has already been changed. So we'll use
-     `in->wcs'. */
+     exists). Note that 'sum->ndim' has already been changed. So we'll use
+     'in->wcs'. */
   gal_wcs_remove_dimension(num->wcs, in->ndim-c_dim);
 
   /* Return. */
@@ -786,8 +840,8 @@ gal_dimension_collapse_minmax(gal_data_t *in, size_t c_dim, 
int max1_min0)
     }
 
   /* Remove the respective dimension in the WCS structure also (if any
-     exists). Note that `sum->ndim' has already been changed. So we'll use
-     `in->wcs'. */
+     exists). Note that 'sum->ndim' has already been changed. So we'll use
+     'in->wcs'. */
   gal_wcs_remove_dimension(minmax->wcs, in->ndim-c_dim);
 
   /* Clean up and return. */
@@ -832,7 +886,7 @@ gal_dimension_remove_extra(size_t ndim, size_t *dsize, 
struct wcsprm *wcs)
         /* Shift all subsequent dimensions to replace this one. */
         for(j=i;j<ndim-1;++j) dsize[j]=dsize[j+1];
 
-        /* Decrement the `i' and the total number of dimension. */
+        /* Decrement the 'i' and the total number of dimension. */
         --i;
         --ndim;
       }
diff --git a/lib/eps.c b/lib/eps.c
index 9e07402..4b940b0 100644
--- a/lib/eps.c
+++ b/lib/eps.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -266,7 +266,7 @@ eps_write_ascii85(gal_data_t *write, FILE *fp, size_t 
numbytes)
                 anint=( arr[j]*256*256*256 + arr[j+1]*256*256
                         + arr[j+2]*256     + arr[j+3]         );
 
-              /* If all four bytes are zero, then just print `z'. */
+              /* If all four bytes are zero, then just print 'z'. */
               if(anint==0) fprintf(fp, "z");
               else
                 {
@@ -387,8 +387,8 @@ gal_eps_write(gal_data_t *in, char *filename, float 
widthincm,
     error(EXIT_FAILURE, 0, "%s: only 1, 3, and 4 color channels are "
           "acceptable, input is a list of %zu data sets", __func__, numch);
   if(in->type!=GAL_TYPE_UINT8)
-    error(EXIT_FAILURE, 0, "%s: input has a `%s' type, but JPEG images can "
-          "only have a `uint8' type", __func__, gal_type_name(in->type, 1));
+    error(EXIT_FAILURE, 0, "%s: input has a '%s' type, but JPEG images can "
+          "only have a 'uint8' type", __func__, gal_type_name(in->type, 1));
 
 
   /* Read the time to write in the output. */
diff --git a/lib/fits.c b/lib/fits.c
index 82af7e0..10a0bac 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -94,7 +94,7 @@ gal_fits_io_error(int status, char *message)
  **************           FITS names           ***************
  *************************************************************/
 /* IMPORTANT NOTE: if other compression suffixes are add to this function,
-   include them in `gal_checkset_automatic_output', so the compression
+   include them in 'gal_checkset_automatic_output', so the compression
    suffix can be skipped when the user doesn't specify an output
    filename.*/
 int
@@ -123,7 +123,7 @@ gal_fits_name_is_fits(char *name)
 
 
 /* IMPORTANT NOTE: if other compression suffixes are add to this function,
-   include them in `gal_checkset_automatic_output', so the compression
+   include them in 'gal_checkset_automatic_output', so the compression
    suffix can be skipped when the user doesn't specify an output
    filename.*/
 int
@@ -151,7 +151,7 @@ gal_fits_suffix_is_fits(char *suffix)
 
 
 
-/* If the name is a FITS name, then put a `(hdu: ...)' after it and return
+/* If the name is a FITS name, then put a '(hdu: ...)' after it and return
    the string. If it isn't a FITS file, just print the name. Note that the
    space is allocated. */
 char *
@@ -318,7 +318,7 @@ gal_fits_type_to_datatype(uint8_t type)
     case GAL_TYPE_STRING:           return TSTRING;
 
     /* Types that depend on the host system. The C standard says that the
-       `short', `int' and `long' types are ATLEAST 2, 2, 4 bytes, so to be
+       'short', 'int' and 'long' types are ATLEAST 2, 2, 4 bytes, so to be
        safe, we will check all of them for the 32-bit types.*/
     case GAL_TYPE_UINT16:
       w=2;
@@ -332,9 +332,9 @@ gal_fits_type_to_datatype(uint8_t type)
       else if( sizeof(int)      == w )   return TINT;
       break;
 
-    /* On 32-bit systems, the length of `int' and `long' are both
+    /* On 32-bit systems, the length of 'int' and 'long' are both
        32-bits. But CFITSIO's LONG type is preferred because it is designed
-       to be 32-bit. Its `INT' type is not clearly defined and caused
+       to be 32-bit. Its 'INT' type is not clearly defined and caused
        problems when reading keywords.*/
     case GAL_TYPE_UINT32:
       w=4;
@@ -371,12 +371,12 @@ gal_fits_type_to_datatype(uint8_t type)
   /* If control reaches, here, there was a problem with the host types. */
   if(w)
     error(EXIT_FAILURE, 0, "%s: this system doesn't have a %d byte integer "
-          "type, so type `%s' cannot be written to FITS", __func__, w,
+          "type, so type '%s' cannot be written to FITS", __func__, w,
           gal_type_name(type, 1));
   else
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we can "
           "fix the problem. Control must not have reached the end for the "
-          "given type `%s'", __func__, PACKAGE_BUGREPORT,
+          "given type '%s'", __func__, PACKAGE_BUGREPORT,
           gal_type_name(type, 1));
   return -1;
 }
@@ -499,14 +499,14 @@ fits_type_correct(int *type, double bscale, char 
*bzero_str)
   int tofloat=1;
   char *tailptr, *bzero_u64="9223372036854775808";
 
-  /* If bzero_str is given and `bscale=1.0' the case might be that we are
+  /* If bzero_str is given and 'bscale=1.0' the case might be that we are
      dealing with an integer dataset that just needs a different sign. */
   if(bzero_str && bscale==1.0f)
     {
-      /* Read the `bzero' string as a `double' number. */
+      /* Read the 'bzero' string as a 'double' number. */
       bzero  = strtod(bzero_str, &tailptr);
       if(tailptr==bzero_str)
-        error(EXIT_FAILURE, 0, "%s: BZERO value `%s' couldn't be "
+        error(EXIT_FAILURE, 0, "%s: BZERO value '%s' couldn't be "
               "parsed as a number", __func__, bzero_str);
 
       /* Work based on type. For the default conversions defined by the
@@ -526,13 +526,13 @@ fits_type_correct(int *type, double bscale, char 
*bzero_str)
           if(bzero == 2147483648LU) { *type = GAL_TYPE_UINT32; tofloat=0; }
           break;
 
-        /* The `bzero' value for unsigned 64-bit integers has 19 decimal
-           digits, but a 64-bit floating point (`double' type) can only
+        /* The 'bzero' value for unsigned 64-bit integers has 19 decimal
+           digits, but a 64-bit floating point ('double' type) can only
            safely store 15 decimal digits. As a result, the safest way to
-           check the `bzero' value for this type is to compare it as a
+           check the 'bzero' value for this type is to compare it as a
            string. But all integers nearby (for example
-           `9223372036854775807') get rounded to this same value (when
-           stored as `double'). So we will also check the parsed number and
+           '9223372036854775807') get rounded to this same value (when
+           stored as 'double'). So we will also check the parsed number and
            if it equals this number, a warning will be printed. */
         case GAL_TYPE_INT64:
           if( !strcmp(bzero_str, bzero_u64) )
@@ -541,8 +541,8 @@ fits_type_correct(int *type, double bscale, char *bzero_str)
             if( bzero == 9223372036854775808LLU )
               {
                 fprintf(stderr, "\nWARNING in %s: the BZERO header keyword "
-                        "value (`%s') is very close (but not exactly equal) "
-                        "to `%s'. The latter value in the FITS standard is "
+                        "value ('%s') is very close (but not exactly equal) "
+                        "to '%s'. The latter value in the FITS standard is "
                         "used to identify that the dataset should be read as "
                         "unsigned 64-bit integers instead of signed 64-bit "
                         "integers. Depending on your version of CFITSIO, "
@@ -552,7 +552,7 @@ fits_type_correct(int *type, double bscale, char *bzero_str)
               }
           break;
 
-          /* For the other types (when `BSCALE=1.0f'), currently no
+          /* For the other types (when 'BSCALE=1.0f'), currently no
              correction is necessary, maybe later we can check if the
              scales are integers and set the integer output type to the
              smallest type that can allow the scaled values. */
@@ -649,6 +649,49 @@ gal_fits_hdu_num(char *filename)
 
 
 
+/* Calculate the datasum of the given HDU in the given file. */
+unsigned long
+gal_fits_hdu_datasum(char *filename, char *hdu)
+{
+  int status=0;
+  fitsfile *fptr;
+  unsigned long datasum;
+
+  /* Read the desired extension (necessary for reading the rest). */
+  fptr=gal_fits_hdu_open(filename, hdu, READONLY);
+
+  /* Calculate the datasum. */
+  datasum=gal_fits_hdu_datasum_ptr(fptr);
+
+  /* Close the file and return. */
+  fits_close_file(fptr, &status);
+  gal_fits_io_error(status, "closing file");
+  return datasum;
+}
+
+
+
+
+
+/* Calculate the FITS standard datasum for the opened FITS pointer. */
+unsigned long
+gal_fits_hdu_datasum_ptr(fitsfile *fptr)
+{
+  int status=0;
+  unsigned long datasum, hdusum;
+
+  /* Calculate the checksum and datasum of the opened HDU. */
+  fits_get_chksum(fptr, &datasum, &hdusum, &status);
+  gal_fits_io_error(status, "estimating datasum");
+
+  /* Return the datasum. */
+  return datasum;
+}
+
+
+
+
+
 /* Given the filename and HDU, this function will return the CFITSIO code
    for the type of data it contains (table, or image). The CFITSIO codes
    are:
@@ -679,7 +722,31 @@ gal_fits_hdu_format(char *filename, char *hdu)
 
 
 
-/* Open a given HDU and return the FITS pointer. `iomode' determines how
+/* Return 1 if the HDU appears to be a HEALpix grid. */
+int
+gal_fits_hdu_is_healpix(fitsfile *fptr)
+{
+  long value;
+  int hdutype, status=0;
+
+  /* An ASCII table can't be a healpix table. */
+  if (fits_get_hdu_type(fptr, &hdutype, &status) )
+    gal_fits_io_error(status, NULL);
+  if(hdutype==ASCII_TBL) return 0;
+
+  /* If all these keywords are present, then 'status' will be 0
+     afterwards. */
+  fits_read_key_lng(fptr, "NSIDE",    &value, 0x0, &status);
+  fits_read_key_lng(fptr, "FIRSTPIX", &value, 0x0, &status);
+  fits_read_key_lng(fptr, "LASTPIX",  &value, 0x0, &status);
+  return !status;
+}
+
+
+
+
+
+/* Open a given HDU and return the FITS pointer. 'iomode' determines how
    the FITS file will be opened: only to read or to read and write. You
    should use the macros given by the CFITSIO header:
 
@@ -701,28 +768,36 @@ gal_fits_hdu_open(char *filename, char *hdu, int iomode)
     {
       switch(status)
         {
-        /* Since the default HDU is `1', when the file only has one
+        /* Since the default HDU is '1', when the file only has one
            extension, this error is common, so we will put a special
            notice. */
         case END_OF_FILE:
           if( hdu[0]=='1' && hdu[1]=='\0' )
-            error(EXIT_FAILURE, 0, "%s has only one extension/HDU but you "
-                  "have asked for the second HDU (hdu number 1 in CFITSIO)."
-                  "\n\n"
-                  "To fix the problem please add `--hdu=0' (or `-h0') to "
-                  "your command when calling Gnuastro's programs. For "
-                  "library users, please give a value of \"0\" to the HDU "
-                  "argument.\n\n"
+            error(EXIT_FAILURE, 0, "%s: only has one HDU.\n\n"
+                  "You should tell Gnuastro's command-line "
+                  "programs to look for data in the primary HDU with the "
+                  "'--hdu=0' option (or '-h0'). For library users, you can "
+                  "put \"0\" (a string literal) for the function's HDU "
+                  "argument. For more, see the FOOTNOTE below.\n\n"
+                  "Pro TIP: if your desired HDU has a name (value to "
+                  "'EXTNAME' keyword), it is best to just use that name "
+                  "with '--hdu' instead of relying on a counter. You can "
+                  "see the list of HDUs in a FITS file (with their data "
+                  "format, type, size and possibly HDU name) using "
+                  "Gnuastro's 'astfits' program, for example:\n\n"
+                  "    astfits %s\n\n"
                   "FOOTNOTE -- When writing a new FITS file, Gnuastro leaves "
-                  "the first HDU blank (with no data) and writes the "
-                  "outputs in the second HDU. In this way the keywords of "
-                  "the the first HDU can be used as meta data of the whole "
-                  "file (which may contain many extensions). This is the "
-                  "recommended way in the FITS standard. As a result, "
-                  "Gnuastro's default HDU to read an extension in a FITS "
-                  "file is the second. This error is commonly caused when "
-                  "the FITS file wasn't created according to this "
-                  "convention.", filename);
+                  "the pimary HDU only for metadata. The output datasets "
+                  "(tables, images or cubes) are written after the primary "
+                  "HDU. In this way the keywords of the the first HDU can be "
+                  "used as metadata of the whole file (which may contain many "
+                  "extensions, this is stipulated in the FITS standard). "
+                  "Usually the primary HDU keywords contains the option names "
+                  "and values that the program was run with. Because of this, "
+                  "Gnuastro's default HDU to read data in a FITS file is the "
+                  "second (or '--hdu=1'). This error is commonly caused when "
+                  "the FITS file wasn't created by Gnuastro or by a program "
+                  "respecting this convention.", filename, filename);
           break;
 
         /* Generic error below is fine for this case */
@@ -736,12 +811,12 @@ gal_fits_hdu_open(char *filename, char *hdu, int iomode)
                             "the given file");
         }
 
-      error(EXIT_FAILURE, 0, "%s: extension/HDU `%s' doesn't exist. Please "
+      error(EXIT_FAILURE, 0, "%s: extension/HDU '%s' doesn't exist. Please "
             "run the following command to see the extensions/HDUs in "
-            "`%s':\n\n"
+            "'%s':\n\n"
             "    $ astfits %s\n\n"
             "The respective HDU number (or name, when present) may be used "
-            "with the `--hdu' option in Gnuastro's programs (or the `hdu' "
+            "with the '--hdu' option in Gnuastro's programs (or the 'hdu' "
             "argument in Gnuastro's libraries) to open the respective HDU.",
             filename, hdu, filename, filename);
     }
@@ -786,8 +861,21 @@ gal_fits_hdu_open_format(char *filename, char *hdu, int 
img0_tab1)
   else
     {
       if(hdutype!=IMAGE_HDU)
-        error(EXIT_FAILURE, 0, "%s (hdu: %s): not an image",
-              filename, hdu);
+        {
+          /* Let the user know. */
+          if( gal_fits_hdu_is_healpix(fptr) )
+            error(EXIT_FAILURE, 0, "%s (hdu: %s): appears to be a HEALPix 
table "
+                  "(which is a 2D dataset on a spherical surface: stored as "
+                  "a 1D table). You can use the 'HPXcvt' command-line utility "
+                  "to convert it to a 2D image that can easily be used by "
+                  "other programs. 'HPXcvt' is built and installed as part of "
+                  "WCSLIB (which is a mandatory dependency of Gnuastro, so "
+                  "you should already have it), run 'man HPXcvt' for more",
+                  filename, hdu);
+          else
+            error(EXIT_FAILURE, 0, "%s (hdu: %s): not an image",
+                  filename, hdu);
+        }
     }
 
   /* Clean up and return. */
@@ -813,7 +901,6 @@ gal_fits_hdu_open_format(char *filename, char *hdu, int 
img0_tab1)
 
 
 
-
 /**************************************************************/
 /**********            Header keywords             ************/
 /**************************************************************/
@@ -856,7 +943,7 @@ gal_fits_key_img_blank(uint8_t type)
             "data type", __func__, type);
     }
 
-  /* If `gal_blank_alloc_write' wasn't used (copy!=NULL), then allocate the
+  /* If 'gal_blank_alloc_write' wasn't used (copy!=NULL), then allocate the
      necessary space and fill it in. Note that the width of the signed and
      unsigned values doesn't differ, so we can use the actual input
      type. */
@@ -894,8 +981,8 @@ gal_fits_key_clean_str_value(char *string)
     if(string[end]!=' ')
       break;
 
-  /* Shift all the characters after the first one (which is a `'' back by
-     one and put the string ending characters on the `end'th element. */
+  /* Shift all the characters after the first one (which is a ''' back by
+     one and put the string ending characters on the 'end'th element. */
   cf=(c=string)+end; do *c=*(c+1); while(++c<cf);
   *cf='\0';
 }
@@ -903,34 +990,54 @@ gal_fits_key_clean_str_value(char *string)
 
 
 
-/* Fill the `tm' structure (defined in `time.h') with the values derived
+/* Fill the 'tm' structure (defined in 'time.h') with the values derived
    from a FITS format date-string and return the (optional) sub-second
-   information as a double.*/
+   information as a double.
+
+   The basic FITS string is defined under the 'DATE' keyword in the FITS
+   standard. For the more complete format which includes timezones, see the
+   W3 standard: https://www.w3.org/TR/NOTE-datetime */
 char *
 gal_fits_key_date_to_struct_tm(char *fitsdate, struct tm *tp)
 {
-  char *c=NULL, *cf;
-  int hasT=0, hassq=0, usesdash=0, usesslash=0;
+  int hasT=0, hassq=0, usesdash=0, usesslash=0, hasZ=0;
+  char *C, *cc, *c=NULL, *cf, *subsec=NULL, *nosubsec=fitsdate;
 
-  /* Initialize the `tm' structure to all-zero elements. In particular, The
-     FITS standard times are written in UTC, so, the time zone (`tm_zone'
+  /* Initialize the 'tm' structure to all-zero elements. In particular, The
+     FITS standard times are written in UTC, so, the time zone ('tm_zone'
      element, which specifies number of seconds to shift for the time zone)
-     has to be zero. The day-light saving flag (`isdst' element) also has
+     has to be zero. The day-light saving flag ('isdst' element) also has
      to be set to zero. */
   tp->tm_sec=tp->tm_min=tp->tm_hour=tp->tm_mday=tp->tm_mon=tp->tm_year=0;
-  tp->tm_wday=tp->tm_yday=tp->tm_isdst=tp->tm_gmtoff;
+  tp->tm_wday=tp->tm_yday=tp->tm_isdst=tp->tm_gmtoff=0;
   tp->tm_zone=NULL;
 
-  /* According to the FITS standard the `T' in the middle of the date and
+  /* According to the FITS standard the 'T' in the middle of the date and
      time of day is optional (the time is not mandatory). */
   cf=(c=fitsdate)+strlen(fitsdate);
   do
     switch(*c)
       {
-      case 'T':  hasT=1;      break; /* With `T' HH:MM:SS are defined.    */
+      case 'T':  hasT=1;      break; /* With 'T' HH:MM:SS are defined.    */
       case '-':  usesdash=1;  break; /* Day definition: YYYY-MM-DD.       */
       case '/':  usesslash=1; break; /* Day definition(old): DD/MM/YY.    */
       case '\'': hassq=1;     break; /* Wholly Wrapped in a single-quote. */
+      case 'Z':  hasZ=1;      break; /* When ends in 'Z', means UTC. See  */
+                                   /* https://www.w3.org/TR/NOTE-datetime */
+
+      /* In case we have sub-seconds in the string, we need to remove it
+         because 'strptime' doesn't recognize sub-second resolution.*/
+      case '.':
+        /* Allocate space (by copying the remaining full string and adding
+           a '\0' where necessary. */
+        gal_checkset_allocate_copy(c, &subsec);
+        gal_checkset_allocate_copy(fitsdate, &nosubsec);
+
+        /* Parse the sub-second part and remove it from the copy. */
+        cc=nosubsec+(c-fitsdate);
+        for(C=subsec+1;*C!='\0';C++)
+          if(!isdigit(*C)) {*cc++=*C; *C='\0';}
+        *cc='\0';
       }
   while(++c<cf);
 
@@ -939,32 +1046,43 @@ gal_fits_key_date_to_struct_tm(char *fitsdate, struct tm 
*tp)
         ? NULL
         : ( usesdash
             ? ( hasT
-                ? strptime(fitsdate, hassq?"'%FT%T'":"%FT%T", tp)
-                : strptime(fitsdate, hassq?"'%F'"   :"%F"   , tp))
+                ? ( hasZ
+                    ? strptime(nosubsec, hassq?"'%FT%TZ'":"%FT%TZ", tp)
+                    : strptime(nosubsec, hassq?"'%FT%T'":"%FT%T", tp) )
+                : strptime(nosubsec, hassq?"'%F'"   :"%F"   , tp))
             : ( hasT
-                ? strptime(fitsdate, hassq?"'%d/%m/%yT%T'":"%d/%m/%yT%T", tp)
-                : strptime(fitsdate, hassq?"'%d/%m/%y'"   :"%d/%m/%y"   , tp)
+                ? ( hasZ
+                    ? strptime(nosubsec, 
hassq?"'%d/%m/%yT%TZ'":"%d/%m/%yT%TZ", tp)
+                    : strptime(nosubsec, hassq?"'%d/%m/%yT%T'":"%d/%m/%yT%T", 
tp))
+                : strptime(nosubsec, hassq?"'%d/%m/%y'"   :"%d/%m/%y"   , tp)
                 )
             )
         );
 
-  /* The value might have sub-seconds. In that case, `c' will point to a
-     `.' and we'll have to parse it as double. */
+  /* The value might have sub-seconds. In that case, 'c' will point to a
+     '.' and we'll have to parse it as double. */
   if( c==NULL || (*c!='.' && *c!='\0') )
-    error(EXIT_FAILURE, 0, "`%s' isn't in the FITS date format.\n\n"
+    error(EXIT_FAILURE, 0, "'%s' isn't in the FITS date format.\n\n"
           "According to the FITS standard, the date must be in one of "
           "these formats:\n"
           "   YYYY-MM-DD\n"
           "   YYYY-MM-DDThh:mm:ss\n"
-          "   DD/MM/YY               (Note the `YY', see *)\n"
-          "   DD/MM/YYThh:mm:ss      (Note the `YY', see *)\n\n"
-          "[*]: Gnuastro's FITS library (this program), interprets the "
+          "   YYYY-MM-DDThh:mm:ssZ   (Note the 'Z',  see *) \n"
+          "   DD/MM/YY               (Note the 'YY', see ^)\n"
+          "   DD/MM/YYThh:mm:ss\n"
+          "   DD/MM/YYThh:mm:ssZ\n\n"
+          "[*]: The 'Z' is interpreted as being in the UTC Timezone.\n"
+          "[^]: Gnuastro's FITS library (this program), interprets the "
           "older (two character for year) format, year values 68 to 99 as "
           "the years 1969 to 1999 and values 0 to 68 as the years 2000 to "
           "2068.", fitsdate);
 
+  /* If the subseconds were removed (and a new string was allocated), free
+     that extra new string. */
+  if(nosubsec!=fitsdate) free(nosubsec);
+
   /* Return the subsecond value. */
-  return c;
+  return subsec;
 }
 
 
@@ -975,7 +1093,7 @@ gal_fits_key_date_to_struct_tm(char *fitsdate, struct tm 
*tp)
    the keywords) into number of seconds since 1970/01/01, 00:00:00. Very
    useful to avoid calendar issues like number of days in a different
    months or leap years and etc. The remainder of the format string
-   (sub-seconds) will be put into the two pointer arguments: `subsec' is in
+   (sub-seconds) will be put into the two pointer arguments: 'subsec' is in
    double-precision floating point format and  */
 size_t
 gal_fits_key_date_to_seconds(char *fitsdate, char **subsecstr,
@@ -984,32 +1102,59 @@ gal_fits_key_date_to_seconds(char *fitsdate, char 
**subsecstr,
   time_t t;
   char *tmp;
   struct tm tp;
-  void *outptr=subsec;
+  size_t seconds;
+  void *subsecptr=subsec;
+
+  /* If the string is blank, return a blank value. */
+  if( strcmp(fitsdate, GAL_BLANK_STRING)==0 )
+    {
+      if(subsec) *subsec=NAN;
+      if(subsecstr) *subsecstr=NULL;
+      return GAL_BLANK_SIZE_T;
+    }
 
-  /* Fill in the `tp' elements with values read from the string. */
+  /* Fill in the 'tp' elements with values read from the string. */
   tmp=gal_fits_key_date_to_struct_tm(fitsdate, &tp);
 
-  /* If the user cared about the remainder (sub-second string), then set it
-     and convert it to a double type. */
+  /* If the user wanted a possible sub-second string/value, then
+     'subsecstr!=NULL'. */
   if(subsecstr)
     {
-      /* Set the output pointer. */
+      /* Set the output pointer. Note that it may be NULL if there was no
+         sub-second string, but that is fine (and desired because the user
+         can use this to check if there was a sub-string or not). */
       *subsecstr=tmp;
 
-      /* Convert the remainder string to double-precision floating point
-         (if the given pointer isn't NULL). */
-      if(subsec)
-        if( gal_type_from_string(&outptr, tmp, GAL_TYPE_FLOAT64) )
-          error(EXIT_FAILURE, 0, "%s: the sub-second portion of `%s' (or "
-                "`%s') couldn't be read as a number", __func__, fitsdate,
-                tmp);
+      /* If there was a sub-second string, then also read it as a
+         double-precision floating point. */
+      if(tmp)
+        {
+          if(subsec)
+            if( gal_type_from_string(&subsecptr, tmp, GAL_TYPE_FLOAT64) )
+              error(EXIT_FAILURE, 0, "%s: the sub-second portion of '%s' (or "
+                    "'%s') couldn't be read as a number", __func__, fitsdate,
+                    tmp);
+        }
+      else { if(subsec) *subsec=NAN; }
     }
 
-  /* Convert the `tm' structure to `time_t'. */
+  /* Convert the contents of the 'tm' structure to 'time_t' (a positive
+     integer) with 'mktime'. Note that by design, the system's timezone is
+     included in the returned value of 'mktime' (leading to situations like
+     bug #57995). But it writes the given time's timezone (number of
+     seconds ahead of UTC) in the 'tm_gmtoff' element of its input.
+
+     IMPORTANT NOTE: the timezone that is calculated by 'mktime' (in
+     'tp.tm_gmtoff') belongs to the time that is already within 'tp' (this
+     is exactly what we want!). So for example when daylight saving is
+     activated at run-time, but at the time inside 'tp', there was no
+     daylight saving, the value of 'tp.tm_gmtoff' will be different from
+     the 'timezone' global variable. */
   t=mktime(&tp);
 
-  /* Return the value and set the output pointer. */
-  return (size_t)t;
+  /* Calculate the seconds and return it. */
+  seconds = (t == (time_t)(-1)) ? GAL_BLANK_SIZE_T : (t+tp.tm_gmtoff);
+  return seconds;
 }
 
 
@@ -1017,9 +1162,9 @@ gal_fits_key_date_to_seconds(char *fitsdate, char 
**subsecstr,
 
 
 /* Read the keyword values from a FITS pointer. The input should be a
-   linked list of `gal_data_t'. Before calling this function, you just have
-   to set the `name' and desired `type' values of each element in the list
-   to the keyword you want it to keep the value of. The given `name' value
+   linked list of 'gal_data_t'. Before calling this function, you just have
+   to set the 'name' and desired 'type' values of each element in the list
+   to the keyword you want it to keep the value of. The given 'name' value
    will be directly passed to CFITSIO to read the desired keyword. This
    function will allocate space to keep the value. Here is one example of
    using this function:
@@ -1036,31 +1181,31 @@ gal_fits_key_date_to_seconds(char *fitsdate, char 
**subsecstr,
 
       gal_data_array_free(keysll, N, 1);
 
-   If the `array' pointer of each keyword's dataset is not NULL, then it is
+   If the 'array' pointer of each keyword's dataset is not NULL, then it is
    assumed that the space has already been allocated. If it is NULL, then
    space will be allocated internally here.
 
    Strings need special consideration: the reason is that generally,
-   `gal_data_t' needs to also allow for array of strings (as it supports
+   'gal_data_t' needs to also allow for array of strings (as it supports
    arrays of integers for example). Hence two allocations will be done here
-   (one if `array!=NULL') and `keysll[i].array' must be interpretted as
-   `char **': one allocation for the pointer, one for the actual
+   (one if 'array!=NULL') and 'keysll[i].array' must be interpretted as
+   'char **': one allocation for the pointer, one for the actual
    characters. You don't have to worry about the freeing,
-   `gal_data_array_free' will free both allocations. So to read a string,
+   'gal_data_array_free' will free both allocations. So to read a string,
    one easy way would be the following:
 
       char *str, **strarray;
       strarr = keysll[i].array;
       str    = strarray[0];
 
-   If CFITSIO is unable to read a keyword for any reason the `status'
-   element of the respective `gal_data_t' will be non-zero. You can check
-   the successful reading of the keyword from the `status' value in each
-   keyword's `gal_data_t'. If it is zero, then the keyword was found and
+   If CFITSIO is unable to read a keyword for any reason the 'status'
+   element of the respective 'gal_data_t' will be non-zero. You can check
+   the successful reading of the keyword from the 'status' value in each
+   keyword's 'gal_data_t'. If it is zero, then the keyword was found and
    succesfully read. Otherwise, it a CFITSIO status value. You can use
-   CFITSIO's error reporting tools or `gal_fits_io_error' for reporting the
+   CFITSIO's error reporting tools or 'gal_fits_io_error' for reporting the
    reason. A tip: when the keyword doesn't exist, then CFITSIO's status
-   value will be `KEY_NO_EXIST'.
+   value will be 'KEY_NO_EXIST'.
 
    CFITSIO will start searching for the keywords from the last place in the
    header that it searched for a keyword. So it is much more efficient if
@@ -1071,9 +1216,11 @@ void
 gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t *keysll,
                            int readcomment, int readunit)
 {
-  void *valueptr;
-  char **strarray;
+  uint8_t numtype;
   gal_data_t *tmp;
+  char **strarray;
+  int typewasinvalid;
+  void *numptr, *valueptr;
 
   /* Get the desired keywords. */
   for(tmp=keysll;tmp!=NULL;tmp=tmp->next)
@@ -1090,7 +1237,16 @@ gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t 
*keysll,
                                           "tmp->dsize");
         tmp->ndim=tmp->size=tmp->dsize[0]=1;
 
-        /* When the type is a string, `tmp->array' is an array of pointers
+        /* If no type has been given, temporarily set it to a string, we
+           will then deduce the type afterwards. */
+        typewasinvalid=0;
+        if(tmp->type==GAL_TYPE_INVALID)
+          {
+            typewasinvalid=1;
+            tmp->type=GAL_TYPE_STRING;
+          }
+
+        /* When the type is a string, 'tmp->array' is an array of pointers
            to a separately allocated piece of memory. So we have to
            allocate that space here. If its not a string, then the
            allocated space above is enough to keep the value.*/
@@ -1131,7 +1287,7 @@ gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t 
*keysll,
 
         /* Allocate space for the keyword unit if necessary. Note that
            since there is no precise CFITSIO length for units, we will use
-           the `FLEN_COMMENT' length for units too (theoretically, the unit
+           the 'FLEN_COMMENT' length for units too (theoretically, the unit
            might take the full remaining area in the keyword). Also note
            that the unit is only optional, so it needs a separate CFITSIO
            function call which is done here.*/
@@ -1155,12 +1311,26 @@ gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t 
*keysll,
         fits_read_key(fptr, gal_fits_type_to_datatype(tmp->type),
                       tmp->name, valueptr, tmp->comment, &tmp->status);
 
-        /* If the comment was empty, free the space and set it to zero. */
+        /* Correct the type if no type was requested and the key has been
+           successfully read. */
+        if(tmp->status==0 && typewasinvalid)
+          {
+            /* If the string can be parsed as a number, then number will be
+               allocated and placed in 'numptr', otherwise, 'numptr' will
+               be NULL. */
+            numptr=gal_type_string_to_number(valueptr, &numtype);
+            if(numptr)
+              {
+                free(valueptr);
+                free(tmp->array);
+                tmp->array=numptr;
+                tmp->type=numtype;
+              }
+          }
+
+        /* If the comment was empty, free the space and set it to NULL. */
         if(tmp->comment && tmp->comment[0]=='\0')
           {free(tmp->comment); tmp->comment=NULL;}
-
-        /* Strings need to be cleaned (CFITSIO puts `'' around them with
-           some (possiblly) extra space on the two ends of the string. */
       }
 }
 
@@ -1168,8 +1338,8 @@ gal_fits_key_read_from_ptr(fitsfile *fptr, gal_data_t 
*keysll,
 
 
 
-/* Same as `gal_fits_read_keywords_fptr', but accepts the filename and HDU
-   as input instead of an already opened CFITSIO `fitsfile' pointer. */
+/* Same as 'gal_fits_read_keywords_fptr', but accepts the filename and HDU
+   as input instead of an already opened CFITSIO 'fitsfile' pointer. */
 void
 gal_fits_key_read(char *filename, char *hdu, gal_data_t *keysll,
                   int readcomment, int readunit)
@@ -1216,7 +1386,7 @@ gal_fits_key_read(char *filename, char *hdu, gal_data_t 
*keysll,
 void
 gal_fits_key_list_add(gal_fits_list_key_t **list, uint8_t type,
                       char *keyname, int kfree, void *value, int vfree,
-                      char *comment, int cfree, char *unit)
+                      char *comment, int cfree, char *unit, int ufree)
 {
   gal_fits_list_key_t *newnode;
 
@@ -1225,6 +1395,10 @@ gal_fits_key_list_add(gal_fits_list_key_t **list, 
uint8_t type,
   newnode=malloc(sizeof *newnode);
   if(newnode==NULL)
     error(EXIT_FAILURE, errno, "%s: allocating new node", __func__);
+
+  /* Write the given values into the key structure. */
+  newnode->title=NULL;
+  newnode->fullcomment=NULL;
   newnode->type=type;
   newnode->keyname=keyname;
   newnode->value=value;
@@ -1233,7 +1407,9 @@ gal_fits_key_list_add(gal_fits_list_key_t **list, uint8_t 
type,
   newnode->kfree=kfree;                /* Free pointers after using them. */
   newnode->vfree=vfree;
   newnode->cfree=cfree;
+  newnode->ufree=ufree;
 
+  /* Set the next pointer. */
   newnode->next=*list;
   *list=newnode;
 }
@@ -1245,15 +1421,19 @@ gal_fits_key_list_add(gal_fits_list_key_t **list, 
uint8_t type,
 void
 gal_fits_key_list_add_end(gal_fits_list_key_t **list, uint8_t type,
                           char *keyname, int kfree, void *value, int vfree,
-                          char *comment, int cfree, char *unit)
+                          char *comment, int cfree, char *unit, int ufree)
 {
-  gal_fits_list_key_t *newnode, *tmp;
+  gal_fits_list_key_t *tmp, *newnode;
 
   /* Allocate space for the new node and fill it in. */
   errno=0;
   newnode=malloc(sizeof *newnode);
   if(newnode==NULL)
     error(EXIT_FAILURE, errno, "%s: allocation of new node", __func__);
+
+  /* Write the given values into the key structure. */
+  newnode->title=NULL;
+  newnode->fullcomment=NULL;
   newnode->type=type;
   newnode->keyname=keyname;
   newnode->value=value;
@@ -1262,7 +1442,135 @@ gal_fits_key_list_add_end(gal_fits_list_key_t **list, 
uint8_t type,
   newnode->kfree=kfree;            /* Free pointers after using them. */
   newnode->vfree=vfree;
   newnode->cfree=cfree;
+  newnode->ufree=ufree;
+
+  /* Set the next pointer. */
+  if(*list)         /* List is already full, add this node to the end */
+    {
+      /* After this line, tmp points to the last node. */
+      tmp=*list; while(tmp->next!=NULL) tmp=tmp->next;
+      tmp->next=newnode;
+      newnode->next=NULL;
+    }
+  else                 /* List is empty */
+    {
+      newnode->next=NULL;
+      *list=newnode;
+    }
+}
+
+
+
+
+/* Only set this key to be a title. */
+void
+gal_fits_key_list_title_add(gal_fits_list_key_t **list, char *title, int tfree)
+{
+  gal_fits_list_key_t *newnode;
+
+  /* Allocate space (and initialize to 0/NULL) for the new node and fill it
+     in. */
+  errno=0;
+  newnode=calloc(1, sizeof *newnode);
+  if(newnode==NULL)
+    error(EXIT_FAILURE, errno, "%s: allocating new node", __func__);
+
+  /* Set the arguments. */
+  newnode->title=title;
+  newnode->tfree=tfree;
+
+  /* Set the next pointer. */
+  newnode->next=*list;
+  *list=newnode;
+}
+
+
+
+
+
+/* Put the title keyword in the end. */
+void
+gal_fits_key_list_title_add_end(gal_fits_list_key_t **list, char *title,
+                                int tfree)
+{
+  gal_fits_list_key_t *tmp, *newnode;
 
+  /* Allocate space (and initialize to 0/NULL) for the new node and fill it
+     in. */
+  errno=0;
+  newnode=calloc(1, sizeof *newnode);
+  if(newnode==NULL)
+    error(EXIT_FAILURE, errno, "%s: allocating new node", __func__);
+
+  /* Set the arguments. */
+  newnode->title=title;
+  newnode->tfree=tfree;
+
+  /* Set the next pointer. */
+  if(*list)         /* List is already full, add this node to the end */
+    {
+      /* After this line, tmp points to the last node. */
+      tmp=*list; while(tmp->next!=NULL) tmp=tmp->next;
+      tmp->next=newnode;
+      newnode->next=NULL;
+    }
+  else                 /* List is empty */
+    {
+      newnode->next=*list;
+      *list=newnode;
+    }
+}
+
+
+
+
+
+/* Only set this key to be a full comment */
+void
+gal_fits_key_list_fullcomment_add(gal_fits_list_key_t **list,
+                                  char *fullcomment, int fcfree)
+{
+  gal_fits_list_key_t *newnode;
+
+  /* Allocate space (and initialize to 0/NULL) for the new node and fill it
+     in. */
+  errno=0;
+  newnode=calloc(1, sizeof *newnode);
+  if(newnode==NULL)
+    error(EXIT_FAILURE, errno, "%s: allocating new node", __func__);
+
+  /* Set the arguments. */
+  newnode->fullcomment=fullcomment;
+  newnode->fcfree=fcfree;
+
+  /* Set the next pointer. */
+  newnode->next=*list;
+  *list=newnode;
+}
+
+
+
+
+
+/* Put the title keyword in the end. */
+void
+gal_fits_key_list_fullcomment_add_end(gal_fits_list_key_t **list,
+                                      char *fullcomment, int fcfree)
+{
+  gal_fits_list_key_t *tmp, *newnode;
+
+  /* Allocate space (and initialize to 0/NULL) for the new node and fill it
+     in. */
+  errno=0;
+  newnode=calloc(1, sizeof *newnode);
+  if(newnode==NULL)
+    error(EXIT_FAILURE, errno, "%s: allocating new node", __func__);
+
+  /* Set the arguments. */
+  newnode->fullcomment=fullcomment;
+  newnode->fcfree=fcfree;
+
+  /* Set the next pointer. */
   if(*list)         /* List is already full, add this node to the end */
     {
       /* After this line, tmp points to the last node. */
@@ -1289,7 +1597,7 @@ gal_fits_key_list_reverse(gal_fits_list_key_t **list)
   /* Only do the reversal if there is more than one element. */
   if(in && in->next)
     {
-      /* Fill the `reversed' list. */
+      /* Fill the 'reversed' list. */
       while(in!=NULL)
         {
           tmp=in->next;
@@ -1316,12 +1624,12 @@ gal_fits_key_write_title_in_ptr(char *title, fitsfile 
*fptr)
   int status=0;
   char *cp, *cpf, blankrec[80], titlerec[80];
 
-  /* Just in case title is `NULL'. */
+  /* Just in case title is 'NULL'. */
   if(title)
     {
       /* A small sanity check. */
       if( strlen(title) + strlen(GAL_FITS_KEY_TITLE_START) > 78 )
-        fprintf(stderr, "%s: FITS keyword title `%s' is too long to be fully "
+        fprintf(stderr, "%s: FITS keyword title '%s' is too long to be fully "
                 "included in the keyword record (80 characters, where the "
                 "title is prefixed with %zu space characters)",
                 __func__, title, strlen(GAL_FITS_KEY_TITLE_START));
@@ -1372,7 +1680,7 @@ gal_fits_key_write_filename(char *keynamebase, char 
*filename,
       errno=0;
       keyname=malloc(FLEN_KEYWORD);
       if(keyname==NULL)
-        error(EXIT_FAILURE, errno, "%s: %d bytes for `keyname'", __func__,
+        error(EXIT_FAILURE, errno, "%s: %d bytes for 'keyname'", __func__,
               FLEN_KEYWORD);
       if(len<maxlength)
         strcpy(keyname, keynamebase);
@@ -1382,7 +1690,7 @@ gal_fits_key_write_filename(char *keynamebase, char 
*filename,
       /* Set the keyword value: */
       errno=0;
       thislen=strlen(&filename[i]);
-      value=malloc(maxlength);
+      value=malloc(maxlength+1);
       if(value==NULL)
         error(EXIT_FAILURE, errno, "%s: allocating %zu bytes", __func__,
               thislen);
@@ -1396,10 +1704,10 @@ gal_fits_key_write_filename(char *keynamebase, char 
*filename,
         {
           if(top1end0)
             gal_fits_key_list_add(list, GAL_TYPE_STRING, keyname, 1,
-                                  value, 1, NULL, 0, NULL);
+                                  value, 1, NULL, 0, NULL, 0);
           else
             gal_fits_key_list_add_end(list, GAL_TYPE_STRING, keyname, 1,
-                                      value, 1, NULL, 0, NULL);
+                                      value, 1, NULL, 0, NULL, 0);
           break;
         }
       else
@@ -1423,7 +1731,7 @@ gal_fits_key_write_filename(char *keynamebase, char 
*filename,
               strcpy(keyname, keynamebase);
 
               /* Let the user know that  */
-              error(0,0, "%s: WARNING: `%s' is too long to fit "
+              error(0,0, "%s: WARNING: '%s' is too long to fit "
                     "into a FITS keyword value (max of %zu characters), "
                     "it will be truncated", __func__, filename,
                     maxlength);
@@ -1432,10 +1740,10 @@ gal_fits_key_write_filename(char *keynamebase, char 
*filename,
           /* Convert the last useful character and save the file name.*/
           if(top1end0)
             gal_fits_key_list_add(list, GAL_TYPE_STRING, keyname, 1,
-                                  value, 1, NULL, 0, NULL);
+                                  value, 1, NULL, 0, NULL, 0);
           else
             gal_fits_key_list_add_end(list, GAL_TYPE_STRING, keyname, 1,
-                                      value, 1, NULL, 0, NULL);
+                                      value, 1, NULL, 0, NULL, 0);
           i+=j+1;
         }
     }
@@ -1445,9 +1753,95 @@ gal_fits_key_write_filename(char *keynamebase, char 
*filename,
 
 
 
+/* A bug was found in WCSLIB that has existed since WCSLIB 5.9 (released on
+   2015/07/21) and will be fixed in the version after WCSLIB 7.3.1
+   (released in 2020). However, it will take time for many user package
+   managers to update their WCSLIB version. So we need to check if that bug
+   has occurred here and fix it manually.
+
+   In short the bug was originally seen on a dataset with this input CDELT
+   values:
+
+     CDELT1  =            0.0007778 / [deg]
+     CDELT2  =            0.0007778 / [deg]
+     CDELT3  =        30000.0000000 / [Hz]
+
+   The values are read into the 'wcsprm' structure properly, but upon
+   writing into the keyword string structure, the 'CDELT1' and 'CDELT2'
+   values are printed as 0. Mark Calabretta (creator of WCSLIB) described
+   the issue as follows:
+
+        """wcshdo() tries to find a single sprintf() floating point format
+        to use for groups of keywords, such as CDELTj as a group, or
+        CRPIXi, PCi_j, and CRVALj, each as separate groups.  It aims to
+        present the keyvalues in human-readable form, i.e. with decimal
+        points lined up, no unnecessary trailing zeroes, and avoiding
+        exponential ('E') format where its use is not warranted.
+
+        The problem here arose because of the large range of CDELT values
+        formatted using 'f' format, but not being so large that it would
+        force wcshdo() to revert to 'E' format.  There is also the
+        troubling possibility that in less extreme cases, precision of the
+        CDELT (or other) values could be lost without being noticed."""
+
+   To implement the check we will follow this logic: large dimensional
+   differences will not commonly happen in 2D datasets, so we will only
+   attempt the check in 3D datasets. We'll read each written CDELT value
+   with CFITSIO and if its zero, we'll correct it. */
+static void
+fits_bug_wrapper_cdelt_zero(fitsfile *fptr, struct wcsprm *wcs, char *keystr)
+{
+  char *keyname;
+  double keyvalue;
+  size_t dim=GAL_BLANK_SIZE_T;
+  int status=0, datatype=TDOUBLE;
+
+  /* Only do this check when we have more than two dimensions. */
+  if(wcs->naxis>2 && !strncmp(keystr, "CDELT", 5))
+    {
+      /* Find the dimension number and keyword string. This can later be
+         improved/generalized by actually parsing the keyword name to
+         extract the dimension, but I didn't have time to implement it in
+         the first implementation. It will rarely be necessary to go beyond
+         the third dimension, so this has almost no extra burden on the
+         computer's processing. */
+      if(      !strncmp(keystr, "CDELT1", 6) ) { keyname="CDELT1"; dim=0; }
+      else if( !strncmp(keystr, "CDELT2", 6) ) { keyname="CDELT2"; dim=1; }
+      else if( !strncmp(keystr, "CDELT3", 6) ) { keyname="CDELT3"; dim=2; }
+      else if( !strncmp(keystr, "CDELT4", 6) ) { keyname="CDELT4"; dim=3; }
+      else if( !strncmp(keystr, "CDELT5", 6) ) { keyname="CDELT5"; dim=4; }
+      else if( !strncmp(keystr, "CDELT6", 6) ) { keyname="CDELT6"; dim=5; }
+      else if( !strncmp(keystr, "CDELT7", 6) ) { keyname="CDELT7"; dim=6; }
+      else if( !strncmp(keystr, "CDELT8", 6) ) { keyname="CDELT8"; dim=7; }
+      else if( !strncmp(keystr, "CDELT9", 6) ) { keyname="CDELT9"; dim=8; }
+      else
+        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+              "the problem. There appears to be more than 9 dimensions in "
+              "the input dataset", __func__, PACKAGE_BUGREPORT);
+
+      /* Read the keyword value. */
+      fits_read_key(fptr, datatype, keyname, &keyvalue, NULL, &status);
+      gal_fits_io_error(status, NULL);
+
+      /* If the written value is not the same by more than 10 decimals,
+         re-write the value. */
+      if( fabs( wcs->cdelt[dim] - keyvalue ) > 1e-10  )
+        {
+          fits_update_key(fptr, datatype, keyname, &wcs->cdelt[dim],
+                          NULL, &status);
+          gal_fits_io_error(status, NULL);
+        }
+    }
+}
+
+
+
+
+
 /* Write the WCS header string into a FITS files*/
 void
-gal_fits_key_write_wcsstr(fitsfile *fptr, char *wcsstr, int nkeyrec)
+gal_fits_key_write_wcsstr(fitsfile *fptr, struct wcsprm *wcs,
+                          char *wcsstr, int nkeyrec)
 {
   size_t i;
   int status=0;
@@ -1463,13 +1857,16 @@ gal_fits_key_write_wcsstr(fitsfile *fptr, char *wcsstr, 
int nkeyrec)
       keystart=&wcsstr[i*80];
 
       /* Write it if it isn't blank (first character is a space), or not a
-         comment (first 7 characters equal to `COMMENT'). The reason is
-         that WCSLIB adds a blank line and a `COMMENT' keyword saying its
+         comment (first 7 characters equal to 'COMMENT'). The reason is
+         that WCSLIB adds a blank line and a 'COMMENT' keyword saying its
          own version. But Gnuastro writes the version of WCSLIB as a
          separate keyword along with all other important software, so it is
          redundant and just makes the keywrods hard to read by eye.*/
       if( keystart[0]!=' ' && strncmp(keystart, "COMMENT", 7) )
-        fits_write_record(fptr, keystart, &status);
+        {
+          fits_write_record(fptr, keystart, &status);
+          if(wcs) fits_bug_wrapper_cdelt_zero(fptr, wcs, keystart);
+        }
     }
   gal_fits_io_error(status, NULL);
 }
@@ -1514,29 +1911,46 @@ gal_fits_key_write_in_ptr(gal_fits_list_key_t 
**keylist, fitsfile *fptr)
   tmp=*keylist;
   while(tmp!=NULL)
     {
-      /* Write the basic key value and comments. */
-      if(tmp->value)
+      /* If a title is requested, only put a title. */
+      if(tmp->title)
         {
-          if( fits_update_key(fptr, gal_fits_type_to_datatype(tmp->type),
-                              tmp->keyname, tmp->value, tmp->comment,
-                              &status) )
-            gal_fits_io_error(status, NULL);
+          gal_fits_key_write_title_in_ptr(tmp->title, fptr);
+          if(tmp->tfree) free(tmp->title);
         }
-      else
+      else if (tmp->fullcomment)
         {
-          if(fits_update_key_null(fptr, tmp->keyname, tmp->comment, &status))
+          if( fits_write_comment(fptr, tmp->fullcomment, &status) )
             gal_fits_io_error(status, NULL);
+          if(tmp->fcfree) free(tmp->fullcomment);
         }
+      else
+        {
+          /* Write the basic key value and comments. */
+          if(tmp->value)
+            {
+              if( fits_update_key(fptr, gal_fits_type_to_datatype(tmp->type),
+                                  tmp->keyname, tmp->value, tmp->comment,
+                                  &status) )
+                gal_fits_io_error(status, NULL);
+            }
+          else
+            {
+              if(fits_update_key_null(fptr, tmp->keyname, tmp->comment,
+                                      &status))
+                gal_fits_io_error(status, NULL);
+            }
 
-      /* Write the units if it was given. */
-      if( tmp->unit
-          && fits_write_key_unit(fptr, tmp->keyname, tmp->unit, &status) )
-        gal_fits_io_error(status, NULL);
+          /* Write the units if it was given. */
+          if( tmp->unit
+              && fits_write_key_unit(fptr, tmp->keyname, tmp->unit, &status) )
+            gal_fits_io_error(status, NULL);
 
-      /* Free the value pointer if desired: */
-      if(tmp->kfree) free(tmp->keyname);
-      if(tmp->vfree) free(tmp->value);
-      if(tmp->cfree) free(tmp->comment);
+          /* Free the value pointer if desired: */
+          if(tmp->kfree) free(tmp->keyname);
+          if(tmp->vfree) free(tmp->value);
+          if(tmp->cfree) free(tmp->comment);
+          if(tmp->ufree) free(tmp->unit);
+        }
 
       /* Keep the pointer to the next keyword and free the allocated
          space for this keyword.*/
@@ -1603,7 +2017,7 @@ gal_fits_key_write_version_in_ptr(gal_fits_list_key_t 
**keylist, char *title,
       gal_fits_key_write_in_ptr(keylist, fptr);
     }
 
-  /* Print `Versions and date' title. */
+  /* Print 'Versions and date' title. */
   gal_fits_key_write_title_in_ptr("Versions and date", fptr);
 
   /* Set the version of CFITSIO as a string. */
@@ -1706,11 +2120,11 @@ gal_fits_key_write_config(gal_fits_list_key_t 
**keylist, char *title,
  ***********            Array functions            ***********
  *************************************************************/
 
-/* Note that the FITS standard defines any array as an `image',
+/* Note that the FITS standard defines any array as an 'image',
    irrespective of how many dimensions it has. This function will return
    the Gnuastro-type, the number of dimensions and size along each
    dimension of the image along with its name and units if necessary (not
-   NULL). Note that `*dsize' will be allocated here, so it must not point
+   NULL). Note that '*dsize' will be allocated here, so it must not point
    to any already allocated space. */
 void
 gal_fits_img_info(fitsfile *fptr, int *type, size_t *ndim, size_t **dsize,
@@ -1737,7 +2151,7 @@ gal_fits_img_info(fitsfile *fptr, int *type, size_t 
*ndim, size_t **dsize,
   /* Define the names of the possibly existing important keywords about the
      dataset. We are defining these in the opposite order to be read by
      CFITSIO. The way Gnuastro writes the FITS keywords, the output will
-     first have `BZERO', then `BSCALE', then `EXTNAME', then, `BUNIT'.*/
+     first have 'BZERO', then 'BSCALE', then 'EXTNAME', then, 'BUNIT'.*/
   gal_list_data_add_alloc(&keysll, NULL, GAL_TYPE_STRING, 1, &dsize_key,
                           NULL, 0, -1, 1, "BUNIT", NULL, NULL);
   gal_list_data_add_alloc(&keysll, NULL, GAL_TYPE_STRING, 1, &dsize_key,
@@ -1785,8 +2199,8 @@ gal_fits_img_info(fitsfile *fptr, int *type, size_t 
*ndim, size_t **dsize,
     (*dsize)[i]=naxes[*ndim-1-i];
 
 
-  /* Clean up. Note that bzero_str, gets freed by `gal_data_free' (which is
-     called by `gal_list_data_free'. */
+  /* Clean up. Note that bzero_str, gets freed by 'gal_data_free' (which is
+     called by 'gal_list_data_free'. */
   gal_list_data_free(keysll);
 }
 
@@ -1843,16 +2257,16 @@ gal_fits_img_read(char *filename, char *hdu, size_t 
minmapsize,
     error(EXIT_FAILURE, 0, "%s (hdu: %s) has 0 dimensions! The most common "
           "cause for this is a wrongly specified HDU. In some FITS images, "
           "the first HDU doesn't have any data, the data is in subsequent "
-          "extensions. So probably reading the second HDU (with `--hdu=1' "
-          "or `-h1') will solve the problem (following CFITSIO's "
+          "extensions. So probably reading the second HDU (with '--hdu=1' "
+          "or '-h1') will solve the problem (following CFITSIO's "
           "convention, currently HDU counting starts from 0)." , filename,
           hdu);
 
 
   /* Set the fpixel array (first pixel in all dimensions). Note that the
-     `long' type will not be larger than 64-bits, so, we'll just assume it
+     'long' type will not be larger than 64-bits, so, we'll just assume it
      is 64-bits for space allocation. On 32-bit systems, this won't be a
-     problem, the space will be written/read as 32-bit `long' any way,
+     problem, the space will be written/read as 32-bit 'long' any way,
      we'll just have a few empty bytes that will be freed anyway at the end
      of this function. */
   fpixel=gal_pointer_allocate(GAL_TYPE_INT64, ndim, 0, __func__, "fpixel");
@@ -1982,23 +2396,25 @@ gal_fits_img_write_to_ptr(gal_data_t *input, char 
*filename)
 {
   void *blank;
   int64_t *i64;
+  char *u64key;
   fitsfile *fptr;
   uint64_t *u64, *u64f;
   long fpixel=1, *naxes;
-  char *wcsstr, *u64key;
   size_t i, ndim=input->ndim;
-  int nkeyrec, hasblank, status=0, datatype=0;
+  int hasblank, status=0, datatype=0;
   gal_data_t *i64data, *towrite, *block=gal_tile_block(input);
 
   /* Small sanity check. */
   if( gal_fits_name_is_fits(filename)==0 )
     error(EXIT_FAILURE, 0, "%s: not a FITS suffix", filename);
 
+
   /* If the input is a tile (isn't a contiguous region of memory), then
      copy it into a contiguous region. */
   towrite = input==block ? input : gal_data_copy(input);
   hasblank=gal_blank_present(towrite, 0);
 
+
   /* Allocate the naxis area. */
   naxes=gal_pointer_allocate( ( sizeof(long)==8
                                 ? GAL_TYPE_INT64
@@ -2010,13 +2426,13 @@ gal_fits_img_write_to_ptr(gal_data_t *input, char 
*filename)
   fptr=gal_fits_open_to_write(filename);
 
 
-  /* Fill the `naxes' array (in opposite order, and `long' type): */
+  /* Fill the 'naxes' array (in opposite order, and 'long' type): */
   for(i=0;i<ndim;++i) naxes[ndim-1-i]=towrite->dsize[i];
 
 
   /* Create the FITS file. Unfortunately CFITSIO doesn't have a macro for
      UINT64, TLONGLONG is only for (signed) INT64. So if the dataset has
-     that type, we'll have to convert it to `INT64' and in the mean-time
+     that type, we'll have to convert it to 'INT64' and in the mean-time
      shift its zero, we will then have to write the BZERO and BSCALE
      keywords accordingly. */
   if(block->type==GAL_TYPE_UINT64)
@@ -2044,7 +2460,6 @@ gal_fits_img_write_to_ptr(gal_data_t *input, char 
*filename)
       fits_create_img(fptr, LONGLONG_IMG, ndim, naxes, &status);
       gal_fits_io_error(status, NULL);
 
-
       /* Write the image into the file. */
       fits_write_img(fptr, datatype, fpixel, i64data->size, i64data->array,
                      &status);
@@ -2122,22 +2537,11 @@ gal_fits_img_write_to_ptr(gal_data_t *input, char 
*filename)
   if(towrite->comment)
     fits_write_comment(fptr, towrite->comment, &status);
 
+
   /* If a WCS structure is present, write it in */
   if(towrite->wcs)
-    {
-      /* Decompose the `PCi_j' matrix and `CDELTi' vector. */
-      gal_wcs_decompose_pc_cdelt(towrite->wcs);
-
-      /* Convert the WCS information to text. */
-      status=wcshdo(WCSHDO_safe, towrite->wcs, &nkeyrec, &wcsstr);
-      if(status)
-        error(0, 0, "%s: WARNING: WCSLIB error, no WCS in output.\n"
-              "wcshdu ERROR %d: %s", __func__, status,
-              wcs_errmsg[status]);
-      else
-        gal_fits_key_write_wcsstr(fptr, wcsstr, nkeyrec);
-      status=0;
-    }
+    gal_wcs_write_in_fitsptr(fptr, towrite->wcs);
+
 
   /* Report any errors if we had any */
   free(naxes);
@@ -2224,7 +2628,7 @@ gal_fits_img_write_corr_wcs_str(gal_data_t *input, char 
*filename,
   fptr=gal_fits_img_write_to_ptr(input, filename);
 
   /* Write the WCS headers into the FITS file. */
-  gal_fits_key_write_wcsstr(fptr, wcsstr, nkeyrec);
+  gal_fits_key_write_wcsstr(fptr, NULL, wcsstr, nkeyrec);
 
   /* Update the CRPIX keywords. Note that we don't want to change the
      values in the WCS information of gal_data_t. Because, it often happens
@@ -2306,8 +2710,8 @@ gal_fits_tab_format(fitsfile *fitsptr)
       else if(!strcmp(value, "BINTABLE"))
         return GAL_TABLE_FORMAT_BFITS;
       else
-        error(EXIT_FAILURE, 0, "%s: the `XTENSION' keyword of this FITS "
-              "table (`%s') doesn't have a standard value", __func__, value);
+        error(EXIT_FAILURE, 0, "%s: the 'XTENSION' keyword of this FITS "
+              "table ('%s') doesn't have a standard value", __func__, value);
     }
   else
     {
@@ -2328,9 +2732,9 @@ gal_fits_tab_format(fitsfile *fitsptr)
 
 
 
-/* The general format of the TDISPn keywords in FITS is like this: `Tw.p',
-   where `T' specifies the general format, `w' is the width to be given to
-   this column and `p' is the precision. For integer types, percision is
+/* The general format of the TDISPn keywords in FITS is like this: 'Tw.p',
+   where 'T' specifies the general format, 'w' is the width to be given to
+   this column and 'p' is the precision. For integer types, percision is
    actually the minimum number of integers, for floats, it is the number of
    decimal digits beyond the decimal point. */
 static void
@@ -2376,7 +2780,7 @@ set_display_format(char *tdisp, gal_data_t *data, char 
*filename, char *hdu,
       break;
 
     default:
-      error(EXIT_FAILURE, 0, "%s (hdu: %s): Format character `%c' in the "
+      error(EXIT_FAILURE, 0, "%s (hdu: %s): Format character '%c' in the "
             "value (%s) of the keyword %s not recognized in %s", filename, hdu,
             tdisp[0], tdisp, keyname, __func__);
     }
@@ -2389,8 +2793,8 @@ set_display_format(char *tdisp, gal_data_t *data, char 
*filename, char *hdu,
     case '.':      /* Width is set, go onto finding the precision. */
       data->disp_precision = strtol(&tailptr[1], &tailptr, 0);
       if(*tailptr!='\0')
-        error(EXIT_FAILURE, 0, "%s (hdu: %s): The value `%s' of the "
-              "`%s' keyword could not recognized (it doesn't finish after "
+        error(EXIT_FAILURE, 0, "%s (hdu: %s): The value '%s' of the "
+              "'%s' keyword could not recognized (it doesn't finish after "
               "the precision) in %s", filename, hdu, tdisp, keyname, __func__);
       break;
 
@@ -2401,8 +2805,8 @@ set_display_format(char *tdisp, gal_data_t *data, char 
*filename, char *hdu,
       break;
 
     default:
-      error(EXIT_FAILURE, 0, "%s (hdu: %s): The value `%s' of the "
-            "`%s' keyword could not recognized (it doesn't have a `.', or "
+      error(EXIT_FAILURE, 0, "%s (hdu: %s): The value '%s' of the "
+            "'%s' keyword could not recognized (it doesn't have a '.', or "
             "finish, after the width) in %s", filename, hdu, tdisp,
             keyname, __func__);
     }
@@ -2415,7 +2819,7 @@ set_display_format(char *tdisp, gal_data_t *data, char 
*filename, char *hdu,
 
 /* The FITS standard for binary tables (not ASCII tables) does not allow
    unsigned types for short, int and long types, or signed char! So it has
-   `TSCALn' and `TZEROn' to scale the signed types to an unsigned type. It
+   'TSCALn' and 'TZEROn' to scale the signed types to an unsigned type. It
    does this internally, but since we need to define our data type and
    allocate space for it before actually reading the array, it is necessary
    to do this setting here.  */
@@ -2461,7 +2865,7 @@ fits_correct_bin_table_int_types(gal_data_t *allcols, int 
tfields,
 
 
 
-/* See the descriptions of `gal_table_info'. */
+/* See the descriptions of 'gal_table_info'. */
 gal_data_t *
 gal_fits_tab_info(char *filename, char *hdu, size_t *numcols,
                   size_t *numrows, int *tableformat)
@@ -2476,6 +2880,11 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
   int status=0, datatype, *tscal;
   char keyname[FLEN_KEYWORD]="XXXXXXXXXXXXX", value[FLEN_VALUE];
 
+  /* Necessary when a keyword can't be written immediately as it is read in
+     the FITS header and it actually depends on other data before. */
+  gal_list_str_t *tmp_n, *later_name=NULL;
+  gal_list_str_t *tmp_v, *later_value=NULL;
+  gal_list_sizet_t *tmp_i, *later_index=NULL;
 
   /* Open the FITS file and get the basic information. */
   fptr=gal_fits_hdu_open_format(filename, hdu, 1);
@@ -2489,7 +2898,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
   allcols=gal_data_array_calloc(tfields);
 
 
-  /* See comments of `fits_correct_bin_table_int_types'. Here we are
+  /* See comments of 'fits_correct_bin_table_int_types'. Here we are
      allocating the space to keep these values. */
   errno=0;
   tscal=calloc(tfields, sizeof *tscal);
@@ -2514,17 +2923,17 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
 
       /* For string valued keywords, CFITSIO's function above, keeps the
          single quotes around the value string, one before and one
-         after. `gal_fits_key_clean_str_value' will remove these single
+         after. 'gal_fits_key_clean_str_value' will remove these single
          quotes and any possible trailing space within the allocated
          space.*/
       if(value[0]=='\'') gal_fits_key_clean_str_value(value);
 
       /* COLUMN DATA TYPE. According the the FITS standard, the value of
-         TFORM is most generally in this format: `rTa'. `T' is actually a
-         code of the datatype. `r' is the `repeat' counter and `a' is
+         TFORM is most generally in this format: 'rTa'. 'T' is actually a
+         code of the datatype. 'r' is the 'repeat' counter and 'a' is
          depreciated. Currently we can only read repeat==1 cases. When no
          number exists before the defined capital letter, it defaults to 1,
-         but if a number exists (for example `5D'), then the repeat is 5
+         but if a number exists (for example '5D'), then the repeat is 5
          (there are actually five values in each column). Note that
          value[0] is a single quote.*/
       if(strncmp(keyname, "TFORM", 5)==0)
@@ -2556,7 +2965,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
                       repeat=strtol(value+1, &tailptr, 0);
                       if(*tailptr!='\0')
                         error(EXIT_FAILURE, 0, "%s (hdu: %s): the value to "
-                              "keyword `%s' (`%s') is not in `Aw' format "
+                              "keyword '%s' ('%s') is not in 'Aw' format "
                               "(for strings) as required by the FITS "
                               "standard in %s", filename, hdu, keyname, value,
                               __func__);
@@ -2575,7 +2984,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
               tscal[index]=strtol(value, &tailptr, 0);
               if(*tailptr!='\0')
                 error(EXIT_FAILURE, 0, "%s (hdu: %s): value to %s keyword "
-                      "(`%s') couldn't be read as a number in %s", filename,
+                      "('%s') couldn't be read as a number in %s", filename,
                       hdu, keyname, value, __func__);
             }
         }
@@ -2589,7 +2998,7 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
               tzero[index]=strtoll(value, &tailptr, 0);
               if(*tailptr!='\0')
                 error(EXIT_FAILURE, 0, "%s (hdu: %s): value to %s keyword "
-                      "(`%s') couldn't be read as a number in %s", filename,
+                      "('%s') couldn't be read as a number in %s", filename,
                       hdu, keyname, value, __func__);
             }
         }
@@ -2631,13 +3040,25 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
           index = strtoul(&keyname[5], &tailptr, 10) - 1;
           if(index<tfields )
             {
-              if(allcols[index].type<0)
-                fprintf(stderr, "%s (hdu: %s): %s is located before "
-                        "TFORM%zu, so the proper type to read/store the "
-                        "blank value cannot be deduced", filename, hdu,
-                        keyname, index+1);
+              if(allcols[index].type==0)
+                {
+                  gal_list_str_add(&later_name, keyname, 1);
+                  gal_list_str_add(&later_value, value, 1);
+                  gal_list_sizet_add(&later_index, index);
+                }
               else
-                gal_tableintern_read_blank(&allcols[index], value);
+                {
+                  /* Put in the blank value. */
+                  gal_tableintern_read_blank(&allcols[index], value);
+
+                  /* This flag is not relevant for FITS tables. */
+                  if(allcols[index].flag
+                     ==GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING)
+                    {
+                      allcols[index].flag=0;
+                      free(allcols[index].array);
+                    }
+                }
             }
         }
 
@@ -2653,11 +3074,53 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
       /* Column zero. */
     }
 
+
+  /* If any columns should be added later because of missing information,
+     add them here. */
+  if(later_name)
+    {
+      /* Interpret the necessary 'later_' keys. */
+      tmp_i=later_index;
+      tmp_v=later_value;
+      for(tmp_n=later_name; tmp_n!=NULL; tmp_n=tmp_n->next)
+        {
+          /* Go over the known types and do the job. */
+          if(strncmp(tmp_n->v, "TNULL", 5)==0)
+            {
+              /* Put in the blank value. */
+              gal_tableintern_read_blank(&allcols[tmp_i->v], tmp_v->v);
+
+              /* This flag is not relevant for FITS tables. */
+              if(allcols[tmp_i->v].flag
+                 ==GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING)
+                {
+                  allcols[tmp_i->v].flag=0;
+                  free(allcols[tmp_i->v].array);
+                }
+            }
+          else
+            error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+                  "fix the problem. Post-processing of keyword '%s' failed",
+                  __func__, PACKAGE_BUGREPORT, tmp_n->v);
+
+          /* Increment the other two lists too. */
+          tmp_v=tmp_v->next;
+          tmp_i=tmp_i->next;
+        }
+
+      /* Clean up. */
+      gal_list_sizet_free(later_index);
+      gal_list_str_free(later_name, 1);
+      gal_list_str_free(later_value, 1);
+    }
+
+
   /* Correct integer types, then free the allocated arrays. */
   fits_correct_bin_table_int_types(allcols, tfields, tscal, tzero);
   free(tscal);
   free(tzero);
 
+
   /* Close the FITS file and report an error if we had any. */
   fits_close_file(fptr, &status);
   gal_fits_io_error(status, NULL);
@@ -2706,12 +3169,10 @@ fits_tab_read_ascii_float_special(char *filename, char 
*hdu, fitsfile *fptr,
   /* Convert the strings to float. */
   for(i=0;i<numrows;++i)
     {
-      /* Parse the string. */
+      /* Parse the string, if its not readable as a special number (like
+         'inf' or 'nan', then just read it as a NaN. */
       tmp=strtod(strarr[i], &tailptr);
-      if(tailptr==strarr[i])
-        error(EXIT_FAILURE, 0, "%s (hdu %s): couldn't parse row %zu of "
-              "column %zu (with value `%s') as a floating point number",
-              filename, hdu, i+1, colnum, strarr[i]);
+      if(tailptr==strarr[i]) tmp=NAN;
 
       /* Write it into the output dataset. */
       if(out->type==GAL_TYPE_FLOAT32)
@@ -2766,7 +3227,7 @@ gal_fits_tab_read(char *filename, char *hdu, size_t 
numrows,
           /* For a string column, we need an allocated array for each element,
              even in binary values. This value should be stored in the
              disp_width element of the data structure, which is done
-             automatically in `gal_fits_table_info'. */
+             automatically in 'gal_fits_table_info'. */
           if(out->type==GAL_TYPE_STRING)
             {
               strarr=out->array;
@@ -2789,10 +3250,10 @@ gal_fits_tab_read(char *filename, char *hdu, size_t 
numrows,
              types, the FITS standard defines blanks as NaN (same as almost
              any other software like Gnuastro). However if a blank value is
              specified, CFITSIO will convert other special numbers like
-             `inf' to NaN also. We want to be able to distringuish `inf'
+             'inf' to NaN also. We want to be able to distringuish 'inf'
              and NaN here, so for floating point types in binary tables, we
              won't define any blank value. In ASCII tables, CFITSIO doesn't
-             read the `NAN' values (that it has written itself) unless we
+             read the 'NAN' values (that it has written itself) unless we
              specify a blank pointer/value. */
           isfloat = ( out->type==GAL_TYPE_FLOAT32
                       || out->type==GAL_TYPE_FLOAT64 );
@@ -2803,8 +3264,8 @@ gal_fits_tab_read(char *filename, char *hdu, size_t 
numrows,
                         1, 1, out->size, blank, out->array, &anynul, &status);
 
           /* In the ASCII table format, CFITSIO might not be able to read
-             `INF' or `-INF'. In this case, it will set status to `BAD_C2D'
-             or `BAD_C2F'. So, we'll use our own parser for the column
+             'INF' or '-INF'. In this case, it will set status to 'BAD_C2D'
+             or 'BAD_C2F'. So, we'll use our own parser for the column
              values. */
           if( hdutype==ASCII_TBL
               && isfloat
@@ -2826,7 +3287,7 @@ gal_fits_tab_read(char *filename, char *hdu, size_t 
numrows,
       gal_fits_io_error(status, NULL);
     }
 
-  /* There are no rows to read (`numrows==NULL'). Make an empty-sized
+  /* There are no rows to read ('numrows==NULL'). Make an empty-sized
      array. */
   else
     {
@@ -2844,9 +3305,9 @@ gal_fits_tab_read(char *filename, char *hdu, size_t 
numrows,
 
           /* Correct the array and sizes. */
           out->size=0;
+          out->array=NULL;
+          out->dsize[0]=0;
           free(out->array);
-          free(out->dsize);
-          out->dsize=out->array=NULL;
         }
     }
 
@@ -2859,10 +3320,10 @@ gal_fits_tab_read(char *filename, char *hdu, size_t 
numrows,
 
 
 /* This function will allocate new copies for all elements to have the same
-   length as the maximum length and set all trailing elements to `\0' for
+   length as the maximum length and set all trailing elements to '\0' for
    those that are shorter than the length. The return value is the
    allocated space. If the dataset is not a string, the returned value will
-   be -1 (largest number of `size_t'). */
+   be -1 (largest number of 'size_t'). */
 static size_t
 fits_string_fixed_alloc_size(gal_data_t *data)
 {
@@ -2890,8 +3351,8 @@ fits_string_fixed_alloc_size(gal_data_t *data)
         error(EXIT_FAILURE, 0, "%s: %zu bytes for tmp", __func__,
               (maxlen+1)*sizeof *strarr[i]);
 
-      /* Put the old array into the newly allocated space. `tmp' was
-         cleared (all values set to `\0', so we don't need to set the final
+      /* Put the old array into the newly allocated space. 'tmp' was
+         cleared (all values set to '\0', so we don't need to set the final
          one explicity after the copy.*/
       for(j=0;strarr[i][j]!='\0';++j)
         tmp[j]=strarr[i][j];
@@ -2920,7 +3381,7 @@ fits_table_prepare_arrays(gal_data_t *cols, size_t 
numcols, int tableformat,
   char *blank, **tform, **ttype, **tunit;
 
 
-  /* Allocate the arrays to keep the `tform' values */
+  /* Allocate the arrays to keep the 'tform' values */
   errno=0;
   tform=*outtform=malloc(numcols*sizeof *tform);
   if(tform==NULL)
@@ -2941,7 +3402,7 @@ fits_table_prepare_arrays(gal_data_t *cols, size_t 
numcols, int tableformat,
   /* Go over each column and fill in these arrays. */
   for(col=cols; col!=NULL; col=col->next)
     {
-      /* Set the `ttype' and `tunit' values: */
+      /* Set the 'ttype' and 'tunit' values: */
       if( asprintf(&ttype[i], "%s", col->name ? col->name : "")<0 )
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
       if( asprintf(&tunit[i], "%s", col->unit ? col->unit : "")<0 )
@@ -2962,7 +3423,7 @@ fits_table_prepare_arrays(gal_data_t *cols, size_t 
numcols, int tableformat,
             /* We need to check if the blank value needs is larger than the
                expected width or not. Its initial width is set the output
                of the function above, but if the value is larger,
-               `asprintf' (which is used) will make it wider. */
+               'asprintf' (which is used) will make it wider. */
             blank = ( gal_blank_present(col, 0)
                       ? gal_blank_as_string(col->type, col->disp_width)
                       : NULL );
@@ -3039,6 +3500,40 @@ fits_table_prepare_arrays(gal_data_t *cols, size_t 
numcols, int tableformat,
 
 
 
+/* Set the blank value to use for TNULL keyword (necessary in reading and
+   writing).
+
+   The blank value must be the raw value within the FITS file (before
+   applying 'TZERO' OR 'TSCAL'). Therefore, because the following integer
+   types aren't native to the FITS standard, we need to correct TNULL for
+   them after applying TZERO. For example for uin16_t, TZERO is 32768, so
+   TNULL has to be 32767 (the maximum value of the signed integer with the
+   same width). In this way, adding TZERO to the TNULL, will make it the
+   actual NULL value we assume in Gnuastro for uint16_t (the largest
+   possible number). */
+static void *
+fits_blank_for_tnull(uint8_t type)
+{
+  /* Allocate the default blank value. */
+  void *blank=gal_blank_alloc_write(type);
+
+  /* For the non-native FITS type, correct the value. */
+  switch(type)
+    {
+    case GAL_TYPE_INT8:   gal_type_min(GAL_TYPE_UINT8, blank); break;
+    case GAL_TYPE_UINT16: gal_type_max(GAL_TYPE_INT16, blank); break;
+    case GAL_TYPE_UINT32: gal_type_max(GAL_TYPE_INT32, blank); break;
+    case GAL_TYPE_UINT64: gal_type_max(GAL_TYPE_INT64, blank); break;
+    }
+
+  /* Return the final allocated pointer. */
+  return blank;
+}
+
+
+
+
+
 /* Write the TNULLn keywords into the FITS file. Note that this depends on
    the type of the table: for an ASCII table, all the columns need it. For
    a binary table, only the non-floating point ones (even if they don't
@@ -3061,9 +3556,9 @@ fits_write_tnull_tcomm(fitsfile *fptr, gal_data_t *col, 
int tableformat,
         error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
       blank=gal_blank_as_string(col->type, col->disp_width);
 
-      /* When in exponential form (`tform' starting with `E'), CFITSIO
-         writes a NaN value as `NAN', but when in floating point form
-         (`tform' starting with `F'), it writes it as `nan'. So in the
+      /* When in exponential form ('tform' starting with 'E'), CFITSIO
+         writes a NaN value as 'NAN', but when in floating point form
+         ('tform' starting with 'F'), it writes it as 'nan'. So in the
          former case, we need to convert the string to upper case. */
       if(tform[0]=='E' || tform[0]=='e')
         for(c=blank; *c!='\0'; ++c) *c=toupper(*c);
@@ -3078,14 +3573,18 @@ fits_write_tnull_tcomm(fitsfile *fptr, gal_data_t *col, 
int tableformat,
       break;
 
     case GAL_TABLE_FORMAT_BFITS:
+
       /* FITS binary tables don't accept NULL values for floating point or
-         string columns. For floating point is must be NaN and for strings
+         string columns. For floating point it must be NaN, and for strings
          it is a blank string. */
       if( col->type!=GAL_TYPE_FLOAT32
           && col->type!=GAL_TYPE_FLOAT64
           && col->type!=GAL_TYPE_STRING )
         {
-          blank=gal_blank_alloc_write(col->type);
+          /* Allocate the blank value to write into the TNULL keyword. */
+          blank=fits_blank_for_tnull(col->type);
+
+          /* Prepare the name and write the keyword. */
           if( asprintf(&keyname, "TNULL%zu", colnum)<0 )
             error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
           fits_write_key(fptr, gal_fits_type_to_datatype(col->type),
@@ -3121,11 +3620,12 @@ fits_write_tnull_tcomm(fitsfile *fptr, gal_data_t *col, 
int tableformat,
 
 
 
-/* Write the given columns (a linked list of `gal_data_t') into a FITS
+/* Write the given columns (a linked list of 'gal_data_t') into a FITS
    table.*/
 void
 gal_fits_tab_write(gal_data_t *cols, gal_list_str_t *comments,
-                   int tableformat, char *filename, char *extname)
+                   int tableformat, char *filename, char *extname,
+                   struct gal_fits_list_key_t **keylist)
 {
   void *blank;
   fitsfile *fptr;
@@ -3158,7 +3658,7 @@ gal_fits_tab_write(gal_data_t *cols, gal_list_str_t 
*comments,
 
 
   /* Make the FITS file pointer. Note that tableformat was checked in
-     `fits_table_prepare_arrays'. */
+     'fits_table_prepare_arrays'. */
   tbltype = tableformat==GAL_TABLE_FORMAT_AFITS ? ASCII_TBL : BINARY_TBL;
   fits_create_tbl(fptr, tbltype, numrows, numcols, ttype, tform, tunit,
                   extname, &status);
@@ -3175,12 +3675,30 @@ gal_fits_tab_write(gal_data_t *cols, gal_list_str_t 
*comments,
       fits_write_tnull_tcomm(fptr, col, tableformat, i+1, tform[i]);
 
       /* Set the blank pointer if its necessary, note that strings don't
-         need a blank pointer in a FITS ASCII table.*/
+         need a blank pointer in a FITS ASCII table. */
       blank = ( gal_blank_present(col, 0)
-                ? gal_blank_alloc_write(col->type) : NULL );
+                ? fits_blank_for_tnull(col->type) : NULL );
       if(tableformat==GAL_TABLE_FORMAT_AFITS && col->type==GAL_TYPE_STRING)
         { if(blank) free(blank); blank=NULL; }
 
+      /* Manually remove the 'blank' pointer for standard FITS table
+         numeric types (types below). We are doing this because as of
+         CFITSIO 3.48, CFITSIO crashes for these types when we define our
+         own blank values within this pointer, and such values actually
+         exist in the column. This is the error message: "Null value for
+         integer table column is not defined (FTPCLU)". Generally, for
+         these native FITS table types 'blank' is redundant because our
+         blank values are actually within their numerical data range. */
+      switch(col->type)
+        {
+        case GAL_TYPE_UINT8:
+        case GAL_TYPE_INT16:
+        case GAL_TYPE_INT32:
+        case GAL_TYPE_INT64:
+          free(blank); blank=NULL;
+          break;
+        }
+
       /* Write the full column into the table. */
       fits_write_colnull(fptr, gal_fits_type_to_datatype(col->type),
                          i+1, 1, 1, col->size, col->array, blank, &status);
@@ -3191,6 +3709,9 @@ gal_fits_tab_write(gal_data_t *cols, gal_list_str_t 
*comments,
       ++i;
     }
 
+  /* Write the requested keywords. */
+  if(keylist)
+    gal_fits_key_write_in_ptr(keylist, fptr);
 
   /* Write the comments if there were any. */
   for(strt=comments; strt!=NULL; strt=strt->next)
@@ -3202,7 +3723,7 @@ gal_fits_tab_write(gal_data_t *cols, gal_list_str_t 
*comments,
 
 
   /* Clean up and close the FITS file. Note that each element in the
-     `ttype' and `tunit' arrays just points to the respective string in the
+     'ttype' and 'tunit' arrays just points to the respective string in the
      column data structure, the space for each element of the array wasn't
      allocated.*/
   for(i=0;i<numcols;++i)
diff --git a/lib/git.c b/lib/git.c
index 57d29e3..eb58c04 100644
--- a/lib/git.c
+++ b/lib/git.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
diff --git a/lib/gnuastro-internal/README b/lib/gnuastro-internal/README
index a4e26f2..f8e9bf8 100644
--- a/lib/gnuastro-internal/README
+++ b/lib/gnuastro-internal/README
@@ -1,10 +1,10 @@
 GNU Astronomy Utilities internal header files
 ---------------------------------------------
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
-The `.h' files in this directory are headers to Gnuastro's internal
+The '.h' files in this directory are headers to Gnuastro's internal
 libraries: libraries that are only available to Gnuastro's programs. These
 are functions that are mainly to do with running a program, for example the
 common options to all the programs, functions to manage options and
@@ -17,9 +17,8 @@ enough).
 
 Copyright
 ---------
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
 version published by the Free Software Foundation; with no Invariant
-Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
\ No newline at end of file
+Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
diff --git a/lib/gnuastro-internal/arithmetic-and.h 
b/lib/gnuastro-internal/arithmetic-and.h
index 44ef3df..f3edbe6 100644
--- a/lib/gnuastro-internal/arithmetic-and.h
+++ b/lib/gnuastro-internal/arithmetic-and.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-binary.h 
b/lib/gnuastro-internal/arithmetic-binary.h
index 010e874..1361446 100644
--- a/lib/gnuastro-internal/arithmetic-binary.h
+++ b/lib/gnuastro-internal/arithmetic-binary.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -84,7 +84,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 
-/* This is for operators like `&&' and `||', where the right operator is
+/* This is for operators like '&&' and '||', where the right operator is
    not necessarily read (and thus incremented). */
 #define BINARY_OP_INCR_OT_RT_LT_SET(OP, OT, LT, RT) {                   \
     LT *la=l->array;                                                    \
@@ -146,7 +146,7 @@ enum arithmetic_binary_outtype_flags
       break;                                                            \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to "   \
-            "address the problem. %d not recognized for `F'",           \
+            "address the problem. %d not recognized for 'F'",           \
             "BINARY_SET_OUT_INT", PACKAGE_BUGREPORT, F);                \
     }
 
@@ -172,7 +172,7 @@ enum arithmetic_binary_outtype_flags
       break;                                                            \
     default:                                                            \
       error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to "   \
-            "address the problem. %d not recognized for `F'",           \
+            "address the problem. %d not recognized for 'F'",           \
             "BINARY_SET_OUT", PACKAGE_BUGREPORT, F);                    \
     }
 
diff --git a/lib/gnuastro-internal/arithmetic-bitand.h 
b/lib/gnuastro-internal/arithmetic-bitand.h
index aa563bc..0e13c9b 100644
--- a/lib/gnuastro-internal/arithmetic-bitand.h
+++ b/lib/gnuastro-internal/arithmetic-bitand.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-bitlsh.h 
b/lib/gnuastro-internal/arithmetic-bitlsh.h
index 86998dc..bc9f58a 100644
--- a/lib/gnuastro-internal/arithmetic-bitlsh.h
+++ b/lib/gnuastro-internal/arithmetic-bitlsh.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-bitor.h 
b/lib/gnuastro-internal/arithmetic-bitor.h
index 9cd5133..3dbe8dd 100644
--- a/lib/gnuastro-internal/arithmetic-bitor.h
+++ b/lib/gnuastro-internal/arithmetic-bitor.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-bitrsh.h 
b/lib/gnuastro-internal/arithmetic-bitrsh.h
index b129458..3a4aef6 100644
--- a/lib/gnuastro-internal/arithmetic-bitrsh.h
+++ b/lib/gnuastro-internal/arithmetic-bitrsh.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-bitxor.h 
b/lib/gnuastro-internal/arithmetic-bitxor.h
index 857a43b..887b7d6 100644
--- a/lib/gnuastro-internal/arithmetic-bitxor.h
+++ b/lib/gnuastro-internal/arithmetic-bitxor.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-divide.h 
b/lib/gnuastro-internal/arithmetic-divide.h
index d521e5a..8803499 100644
--- a/lib/gnuastro-internal/arithmetic-divide.h
+++ b/lib/gnuastro-internal/arithmetic-divide.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-eq.h 
b/lib/gnuastro-internal/arithmetic-eq.h
index 716c041..65f28a7 100644
--- a/lib/gnuastro-internal/arithmetic-eq.h
+++ b/lib/gnuastro-internal/arithmetic-eq.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-ge.h 
b/lib/gnuastro-internal/arithmetic-ge.h
index 9b53546..8af5f43 100644
--- a/lib/gnuastro-internal/arithmetic-ge.h
+++ b/lib/gnuastro-internal/arithmetic-ge.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-gt.h 
b/lib/gnuastro-internal/arithmetic-gt.h
index aaaa6d5..d20cc98 100644
--- a/lib/gnuastro-internal/arithmetic-gt.h
+++ b/lib/gnuastro-internal/arithmetic-gt.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-internal.h 
b/lib/gnuastro-internal/arithmetic-internal.h
index f9d2bdd..ec2f568 100644
--- a/lib/gnuastro-internal/arithmetic-internal.h
+++ b/lib/gnuastro-internal/arithmetic-internal.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -28,13 +28,13 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/data.h>
 
 
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   `GAL_CONFIG_ARITH_CHAR' and the rest of the types that we needed in the
-   arithmetic function) is kept in `config.h'. When building a user's
-   programs, this information is kept in `gnuastro/config.h'. Note that all
-   `.c' files must start with the inclusion of `config.h' and that
-   `gnuastro/config.h' is only created at installation time (not present
+   'GAL_CONFIG_ARITH_CHAR' and the rest of the types that we needed in the
+   arithmetic function) is kept in 'config.h'. When building a user's
+   programs, this information is kept in 'gnuastro/config.h'. Note that all
+   '.c' files must start with the inclusion of 'config.h' and that
+   'gnuastro/config.h' is only created at installation time (not present
    during the building of Gnuastro).*/
 #ifndef IN_GNUASTRO_BUILD
 #include <gnuastro/config.h>
diff --git a/lib/gnuastro-internal/arithmetic-le.h 
b/lib/gnuastro-internal/arithmetic-le.h
index 857b6db..675b782 100644
--- a/lib/gnuastro-internal/arithmetic-le.h
+++ b/lib/gnuastro-internal/arithmetic-le.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-lt.h 
b/lib/gnuastro-internal/arithmetic-lt.h
index 3238df2..4c0a538 100644
--- a/lib/gnuastro-internal/arithmetic-lt.h
+++ b/lib/gnuastro-internal/arithmetic-lt.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-minus.h 
b/lib/gnuastro-internal/arithmetic-minus.h
index a7a0a79..28e1d74 100644
--- a/lib/gnuastro-internal/arithmetic-minus.h
+++ b/lib/gnuastro-internal/arithmetic-minus.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-modulo.h 
b/lib/gnuastro-internal/arithmetic-modulo.h
index 2448c60..6a01ba4 100644
--- a/lib/gnuastro-internal/arithmetic-modulo.h
+++ b/lib/gnuastro-internal/arithmetic-modulo.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-multiply.h 
b/lib/gnuastro-internal/arithmetic-multiply.h
index c67a75b..9ed9cd2 100644
--- a/lib/gnuastro-internal/arithmetic-multiply.h
+++ b/lib/gnuastro-internal/arithmetic-multiply.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-ne.h 
b/lib/gnuastro-internal/arithmetic-ne.h
index 9a564f6..60b580f 100644
--- a/lib/gnuastro-internal/arithmetic-ne.h
+++ b/lib/gnuastro-internal/arithmetic-ne.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-or.h 
b/lib/gnuastro-internal/arithmetic-or.h
index 188c69b..3e5f81a 100644
--- a/lib/gnuastro-internal/arithmetic-or.h
+++ b/lib/gnuastro-internal/arithmetic-or.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-plus.h 
b/lib/gnuastro-internal/arithmetic-plus.h
index 339d113..58cfb9a 100644
--- a/lib/gnuastro-internal/arithmetic-plus.h
+++ b/lib/gnuastro-internal/arithmetic-plus.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro-internal/arithmetic-set.h 
b/lib/gnuastro-internal/arithmetic-set.h
new file mode 100644
index 0000000..69ded8b
--- /dev/null
+++ b/lib/gnuastro-internal/arithmetic-set.h
@@ -0,0 +1,51 @@
+/*********************************************************************
+Arithmetic operations on data structures.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2021, 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 __ARITHMETIC_SET_H__
+#define __ARITHMETIC_SET_H__
+
+#define GAL_ARITHMETIC_SET_PREFIX         "set-"
+#define GAL_ARITHMETIC_SET_PREFIX_LENGTH  strlen(GAL_ARITHMETIC_SET_PREFIX)
+
+
+struct gal_arithmetic_set_params
+{
+  void               *tokens;   /* Full list of tokens.            */
+  size_t        tokencounter;   /* Counter of current token.       */
+  gal_data_t          *named;   /* List of named datasets.         */
+  void               *params;   /* Internal parameters in caller.  */
+  gal_data_t  * (*pop)(void *in_prm);  /* Function to use.         */
+  int (*used_later)(void *in_prm, char *name); /* Function to use. */
+};
+
+void
+gal_arithmetic_set_name(struct gal_arithmetic_set_params *p,
+                        char *token);
+
+int
+gal_arithmetic_set_is_name(gal_data_t *named, char *token);
+
+gal_data_t *
+gal_arithmetic_set_copy_named(struct gal_arithmetic_set_params *p,
+                              char *name);
+
+#endif
diff --git a/lib/gnuastro-internal/checkset.h b/lib/gnuastro-internal/checkset.h
index 35e1085..ec3810e 100644
--- a/lib/gnuastro-internal/checkset.h
+++ b/lib/gnuastro-internal/checkset.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -57,6 +57,13 @@ gsl_rng *
 gal_checkset_gsl_rng(uint8_t envseed_bool, const char **name,
                      unsigned long int *seed);
 
+size_t
+gal_checkset_ram_available(int quietmmap);
+
+int
+gal_checkset_need_mmap(size_t bytesize, size_t minmapsize, int quietmmap);
+
+
 
 
 /**************************************************************/
@@ -98,6 +105,12 @@ gal_checkset_dir_part(char *filename);
 char *
 gal_checkset_not_dir_part(char *filename);
 
+char *
+gal_checkset_suffix_separate(char *name, char **suffix);
+
+char *
+gal_checkset_make_unique_suffix(char *reference, char *suffix);
+
 void
 gal_checkset_check_file(char *filename);
 
diff --git a/lib/gnuastro-internal/commonopts.h 
b/lib/gnuastro-internal/commonopts.h
index ff35933..d55f98b 100644
--- a/lib/gnuastro-internal/commonopts.h
+++ b/lib/gnuastro-internal/commonopts.h
@@ -6,7 +6,7 @@ IMPORTANT: This header must only be included the programs, not 
libraries.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -31,7 +31,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    IMPORTANT NOTE:
 
      This header should only be included in the programs, not the
-     libraries, and in particular `options.c'. Because we want each program
+     libraries, and in particular 'options.c'. Because we want each program
      to have its own allocation of the common options structure. If it is
      included in options.c, then it will be shared between all the
      programs. */
@@ -60,7 +60,7 @@ struct argp_option gal_commonopts_options[] =
       GAL_OPTIONS_KEY_SEARCHIN,
       "STR",
       0,
-      "Select column(s): `name', `unit', `comment'.",
+      "Select column(s): 'name', 'unit', 'comment'.",
       GAL_OPTIONS_GROUP_INPUT,
       &cp->searchin,
       GAL_TYPE_STRING,
@@ -254,10 +254,10 @@ struct argp_option gal_commonopts_options[] =
       0,
       "Type of output: e.g., int16, float32, etc...",
       GAL_OPTIONS_GROUP_OUTPUT,
-      &cp->type,                /* Internally, `cp->type' is actually an   */
-      GAL_TYPE_STRING,          /* `uint8_t', but the user gives a string. */
+      &cp->type,                /* Internally, 'cp->type' is actually an   */
+      GAL_TYPE_STRING,          /* 'uint8_t', but the user gives a string. */
       GAL_OPTIONS_RANGE_GT_0,   /* So for the sanity checks to pass, we    */
-      GAL_OPTIONS_NOT_MANDATORY,/* use `GAL_TYPE_STRING' for this option.  */
+      GAL_OPTIONS_NOT_MANDATORY,/* use 'GAL_TYPE_STRING' for this option.  */
       GAL_OPTIONS_NOT_SET,
       gal_options_read_type
     },
@@ -266,7 +266,7 @@ struct argp_option gal_commonopts_options[] =
       GAL_OPTIONS_KEY_TABLEFORMAT,
       "STR",
       0,
-      "Table fmt: `fits-ascii', `fits-binary', `txt'.",
+      "Table fmt: 'fits-ascii', 'fits-binary', 'txt'.",
       GAL_OPTIONS_GROUP_OUTPUT,
       &cp->tableformat,
       GAL_TYPE_STRING,
diff --git a/lib/gnuastro-internal/config.h.in 
b/lib/gnuastro-internal/config.h.in
index 14fa844..1d070e0 100644
--- a/lib/gnuastro-internal/config.h.in
+++ b/lib/gnuastro-internal/config.h.in
@@ -4,7 +4,7 @@ Functions dealing with general aspects of all Gnuastro.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -27,7 +27,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 /* The restrict keyword might not be present on some systems, so we are
-   redefining it here based on the checks in `configure.ac', see there for
+   redefining it here based on the checks in 'configure.ac', see there for
    more comments. */
 #define restrict @RESTRICT_REPLACEMENT@
 
@@ -40,6 +40,9 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #define GAL_CONFIG_HAVE_FITS_IS_REENTRANT   @HAVE_FITS_IS_REENTRANT@
 #define GAL_CONFIG_HAVE_WCSLIB_VERSION      @HAVE_WCSLIB_VERSION@
+#define GAL_CONFIG_HAVE_WCSLIB_DIS_H        @HAVE_WCSLIB_DIS_H@
+#define GAL_CONFIG_HAVE_WCSLIB_MJDREF       @HAVE_WCSLIB_MJDREF@
+#define GAL_CONFIG_HAVE_WCSLIB_OBSFIX       @HAVE_WCSLIB_OBSFIX@
 
 #define GAL_CONFIG_HAVE_PTHREAD_BARRIER     @HAVE_PTHREAD_BARRIER@
 
diff --git a/lib/gnuastro-internal/fixedstringmacros.h 
b/lib/gnuastro-internal/fixedstringmacros.h
index 66228cb..258c3c5 100644
--- a/lib/gnuastro-internal/fixedstringmacros.h
+++ b/lib/gnuastro-internal/fixedstringmacros.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -26,7 +26,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 #define GAL_STRINGS_SHORT_COPYRIGHT                                     \
-  "Copyright (C) 2015-2019, Free Software Foundation, Inc."
+  "Copyright (C) 2015-2021, Free Software Foundation, Inc."
 
 
 #define GAL_STRINGS_SHORT_LICENSE                                       \
@@ -64,7 +64,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* This can be used in the end of error messages related to option
    values. */
 #define GAL_STRINGS_HOW_TO_CHECK_VALUES                                 \
-  " You can check all the input values with the `--printparams' "       \
+  " You can check all the input values with the '--printparams' "       \
   "(-P) option."
 
 
diff --git a/lib/gnuastro-internal/options.h b/lib/gnuastro-internal/options.h
index dd5199c..fdb7478 100644
--- a/lib/gnuastro-internal/options.h
+++ b/lib/gnuastro-internal/options.h
@@ -4,7 +4,7 @@ Function to parse options and configuration file values.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -43,7 +43,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* When printing the option names, values and comments, we want things to
    be clean and readable (all the comments starting on one line for most,
    ideally all, lines). But in some cases, option values can become too
-   long (for example the `--polygon' option in Crop, which takes many
+   long (for example the '--polygon' option in Crop, which takes many
    coordinates). So simply using the maximum option length is going to make
    the whole thing unreadable and we need to have a maximum so this rule
    only applies to them. */
@@ -63,7 +63,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
    We want the Operating mode group of options to be the last and the input
    and output groups to the be the first. So first we set the operating
-   mdoe group code to `-1', and benefit from the definition of the
+   mdoe group code to '-1', and benefit from the definition of the
    Enumerator type in C, so each field afterwards will be one integer
    larger than the previous. The largest common option group is then used
    to build the codes of program-specific option groups. */
@@ -83,7 +83,7 @@ enum options_standard_groups
 
 /* Key values for the common options, the free alphabetical keys are listed
    below. You can use any of the free letters for an option in a
-   program. Note that `-V', which is used by GNU and implemented by Argp,
+   program. Note that '-V', which is used by GNU and implemented by Argp,
    is also removed from this list.
 
    a b c d e f g i j k l m n p r s t u v w x y z
@@ -151,7 +151,7 @@ enum gal_options_range_values
 
 
 
-/* What to do if option isn't given. Note that in each program's `main.c'
+/* What to do if option isn't given. Note that in each program's 'main.c'
    the main program structure is initialized to zero (or NULL for
    pointers). */
 enum gal_options_mandatory_values
@@ -295,6 +295,9 @@ void *
 gal_options_parse_csv_strings(struct argp_option *option, char *arg,
                               char *filename, size_t lineno, void *junk);
 
+void
+gal_options_merge_list_of_csv(gal_list_str_t **list);
+
 void *
 gal_options_parse_sizes_reverse(struct argp_option *option, char *arg,
                                 char *filename, size_t lineno, void *params);
@@ -308,8 +311,16 @@ gal_options_read_sigma_clip(struct argp_option *option, 
char *arg,
                             char *filename, size_t lineno, void *junk);
 
 void *
-gal_options_parse_name_and_values(struct argp_option *option, char *arg,
-                                  char *filename, size_t lineno, void *junk);
+gal_options_parse_name_and_strings(struct argp_option *option, char *arg,
+                                   char *filename, size_t lineno, void *junk);
+
+void *
+gal_options_parse_name_and_float64s(struct argp_option *option, char *arg,
+                                    char *filename, size_t lineno, void *junk);
+
+void *
+gal_options_parse_colon_sep_csv(struct argp_option *option, char *arg,
+                                char *filename, size_t lineno, void *junk);
 
 
 /**********************************************************************/
diff --git a/lib/gnuastro-internal/tableintern.h 
b/lib/gnuastro-internal/tableintern.h
index fdfddee..555282d 100644
--- a/lib/gnuastro-internal/tableintern.h
+++ b/lib/gnuastro-internal/tableintern.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -26,7 +26,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Include other headers if necessary here. Note that other header files
    must be included before the C++ preparations below */
 
-#include <gnuastro/fits.h> /* Includes `gnuastro/data.h' and `fitsio.h' */
+#include <gnuastro/fits.h> /* Includes 'gnuastro/data.h' and 'fitsio.h' */
 #include <gnuastro/list.h>
 
 
@@ -52,6 +52,14 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 
+/* Flags for columns. */
+enum tableintern_flags
+{
+ GAL_TABLEINTERN_FLAG_INVALID,  /* Zero according to FITS standard. */
+ GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING,
+};
+
+
 
 
 /************************************************************************/
diff --git a/lib/gnuastro-internal/tile-internal.h 
b/lib/gnuastro-internal/tile-internal.h
index dfa402b..5518ec7 100644
--- a/lib/gnuastro-internal/tile-internal.h
+++ b/lib/gnuastro-internal/tile-internal.h
@@ -6,7 +6,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -28,13 +28,13 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    must be included before the C++ preparations below */
 
 
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   `GAL_CONFIG_ARITH_CHAR' and the rest of the types that we needed in the
-   arithmetic function) is kept in `config.h'. When building a user's
-   programs, this information is kept in `gnuastro/config.h'. Note that all
-   `.c' files must start with the inclusion of `config.h' and that
-   `gnuastro/config.h' is only created at installation time (not present
+   'GAL_CONFIG_ARITH_CHAR' and the rest of the types that we needed in the
+   arithmetic function) is kept in 'config.h'. When building a user's
+   programs, this information is kept in 'gnuastro/config.h'. Note that all
+   '.c' files must start with the inclusion of 'config.h' and that
+   'gnuastro/config.h' is only created at installation time (not present
    during the building of Gnuastro).*/
 #ifndef IN_GNUASTRO_BUILD
 #include <gnuastro/config.h>
@@ -66,6 +66,14 @@ gal_tileinternal_no_outlier(gal_data_t *first, gal_data_t 
*second,
                             double *outliersclip, float outliersigma,
                             char *filename);
 
+void
+gal_tileinternal_no_outlier_local(gal_data_t *input, gal_data_t *second,
+                                  gal_data_t *third,
+                                  struct gal_tile_two_layer_params *tl,
+                                  uint8_t metric, size_t numneighbors,
+                                  size_t numthreads, double *outliersclip,
+                                  double outliersigma, char *filename);
+
 __END_C_DECLS    /* From C++ preparations */
 
 #endif           /* __GAL_TILE_INTERNAL_H__ */
diff --git a/lib/gnuastro-internal/timing.h b/lib/gnuastro-internal/timing.h
index 9b36e18..ef5bc11 100644
--- a/lib/gnuastro-internal/timing.h
+++ b/lib/gnuastro-internal/timing.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro/pdf.h b/lib/gnuastro-internal/wcsdistortion.h
similarity index 75%
copy from lib/gnuastro/pdf.h
copy to lib/gnuastro-internal/wcsdistortion.h
index 5ca76c4..4a6c4f6 100644
--- a/lib/gnuastro/pdf.h
+++ b/lib/gnuastro-internal/wcsdistortion.h
@@ -1,11 +1,12 @@
 /*********************************************************************
-pdf -- functions to write PDF files.
+wcsdistortion -- Manipulation of distortions in WCS.
 This is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
-     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+     Sachin Kumar Singh <sachinkumarsingh092@gmail.com>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Copyright (C) 2020-2021, 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,15 +21,11 @@ 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_PDF_H__
-#define __GAL_PDF_H__
-
+#ifndef __GAL_WCSDISTORTION_H__
+#define __GAL_WCSDISTORTION_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 */
 #undef __BEGIN_C_DECLS
@@ -44,8 +41,6 @@ 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 */
 
@@ -53,20 +48,18 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 
-/* Functions */
-int
-gal_pdf_name_is_pdf(char *name);
+/* This library's functions. */
+struct wcsprm *
+gal_wcsdistortion_tpv_to_sip(struct wcsprm *inwcs,
+                             size_t *fitsize);
 
-int
-gal_pdf_suffix_is_pdf(char *name);
+struct wcsprm *
+gal_wcsdistortion_sip_to_tpv(struct wcsprm *inwcs);
 
-void
-gal_pdf_write(gal_data_t *in, char *filename, float widthincm,
-              uint32_t borderwidth, int dontoptimize);
 
 
 
 
 __END_C_DECLS    /* From C++ preparations */
 
-#endif           /* __GAL_TIFF_H__ */
+#endif           /* __GAL_CHECKSET_H__ */
diff --git a/lib/gnuastro.pc.in b/lib/gnuastro.pc.in
index 3223d8f..fe5e515 100644
--- a/lib/gnuastro.pc.in
+++ b/lib/gnuastro.pc.in
@@ -3,7 +3,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019, Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, 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
@@ -27,6 +27,6 @@ Name: Gnuastro
 Description: GNU Astronomy Utilities
 URL: https://www.gnu.org/software/gnuastro/
 Version: @VERSION@
-Requires.private: wcs cfitsio gsl
+Requires.private: wcslib cfitsio gsl @optional_libs@
 Cflags: -I${includedir}
 Libs: -L${libdir} -lgnuastro @LIBS@
diff --git a/lib/gnuastro/README b/lib/gnuastro/README
index 8dec4c3..9cd197e 100644
--- a/lib/gnuastro/README
+++ b/lib/gnuastro/README
@@ -1,7 +1,7 @@
 GNU Astronomy Utilities library header files
 ============================================
 
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
 See the end of the file for license conditions.
 
 This directory contins Gnuastro's installed library header files. Once
@@ -14,7 +14,7 @@ Inside of Gnuastro, the directory is given the same name to 
make Gnuastro's
 internal code similar to what a user would use in their own programs once
 the header files are installed. The headers that are not installed are kept
 in the parent directory of this directory mixed with the actual library
-`.c' source files.
+'.c' source files.
 
 
 
@@ -22,7 +22,7 @@ in the parent directory of this directory mixed with the 
actual library
 
 Copyright
 ---------
-Copyright (C) 2015-2019 Free Software Foundation, Inc.
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document under
 the terms of the GNU Free Documentation License, Version 1.3 or any later
diff --git a/lib/gnuastro/arithmetic.h b/lib/gnuastro/arithmetic.h
index ce529ff..b1719df 100644
--- a/lib/gnuastro/arithmetic.h
+++ b/lib/gnuastro/arithmetic.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -28,13 +28,13 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/data.h>
 
 
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   `GAL_CONFIG_ARITH_CHAR' and the rest of the types that we needed in the
-   arithmetic function) is kept in `config.h'. When building a user's
-   programs, this information is kept in `gnuastro/config.h'. Note that all
-   `.c' files must start with the inclusion of `config.h' and that
-   `gnuastro/config.h' is only created at installation time (not present
+   'GAL_CONFIG_ARITH_CHAR' and the rest of the types that we needed in the
+   arithmetic function) is kept in 'config.h'. When building a user's
+   programs, this information is kept in 'gnuastro/config.h'. Note that all
+   '.c' files must start with the inclusion of 'config.h' and that
+   'gnuastro/config.h' is only created at installation time (not present
    during the building of Gnuastro).*/
 #ifndef IN_GNUASTRO_BUILD
 #include <gnuastro/config.h>
@@ -77,7 +77,7 @@ enum gal_arithmetic_operators
 
   GAL_ARITHMETIC_OP_PLUS,         /*   +     */
   GAL_ARITHMETIC_OP_MINUS,        /*   -     */
-  GAL_ARITHMETIC_OP_MULTIPLY,     /*   *     */
+  GAL_ARITHMETIC_OP_MULTIPLY,     /*   x     */
   GAL_ARITHMETIC_OP_DIVIDE,       /*   /     */
   GAL_ARITHMETIC_OP_MODULO,       /*   %     */
 
@@ -106,6 +106,25 @@ enum gal_arithmetic_operators
   GAL_ARITHMETIC_OP_LOG,          /* log()   */
   GAL_ARITHMETIC_OP_LOG10,        /* log10() */
 
+  GAL_ARITHMETIC_OP_SIN,          /* sine (input in deg).    */
+  GAL_ARITHMETIC_OP_COS,          /* cosine (input in deg).  */
+  GAL_ARITHMETIC_OP_TAN,          /* tangent (input in deg). */
+  GAL_ARITHMETIC_OP_ASIN,         /* Inverse sine (output in deg). */
+  GAL_ARITHMETIC_OP_ACOS,         /* Inverse cosine (output in deg). */
+  GAL_ARITHMETIC_OP_ATAN,         /* Inverse tangent (output in deg). */
+  GAL_ARITHMETIC_OP_ATAN2,        /* Inv. atan (preserves quad, out in deg). */
+  GAL_ARITHMETIC_OP_SINH,         /* Hyperbolic sine. */
+  GAL_ARITHMETIC_OP_COSH,         /* Hyperbolic cosine. */
+  GAL_ARITHMETIC_OP_TANH,         /* Hyperbolic tangent. */
+  GAL_ARITHMETIC_OP_ASINH,        /* Inverse hyperbolic sine. */
+  GAL_ARITHMETIC_OP_ACOSH,        /* Inverse hyperbolic cosine. */
+  GAL_ARITHMETIC_OP_ATANH,        /* Inverse hyperbolic tangent. */
+
+  GAL_ARITHMETIC_OP_RA_TO_DEGREE, /* right ascension to decimal      */
+  GAL_ARITHMETIC_OP_DEC_TO_DEGREE,/* declination to decimal          */
+  GAL_ARITHMETIC_OP_DEGREE_TO_RA, /* right ascension to decimal      */
+  GAL_ARITHMETIC_OP_DEGREE_TO_DEC,/* declination to decimal          */
+
   GAL_ARITHMETIC_OP_MINVAL,       /* Minimum value of array.               */
   GAL_ARITHMETIC_OP_MAXVAL,       /* Maximum value of array.               */
   GAL_ARITHMETIC_OP_NUMBERVAL,    /* Number of (non-blank) elements.       */
@@ -121,11 +140,14 @@ enum gal_arithmetic_operators
   GAL_ARITHMETIC_OP_MEAN,         /* Mean per pixel of multiple arrays.    */
   GAL_ARITHMETIC_OP_STD,          /* STD per pixel of multiple arrays.     */
   GAL_ARITHMETIC_OP_MEDIAN,       /* Median per pixel of multiple arrays.  */
+  GAL_ARITHMETIC_OP_QUANTILE,     /* Quantile per pixel of multiple arrays.*/
   GAL_ARITHMETIC_OP_SIGCLIP_NUMBER,/* Sigma-clipped number of mult. arrays.*/
   GAL_ARITHMETIC_OP_SIGCLIP_MEAN, /* Sigma-clipped mean of multiple arrays.*/
   GAL_ARITHMETIC_OP_SIGCLIP_MEDIAN,/* Sigma-clipped median of mult. arrays.*/
   GAL_ARITHMETIC_OP_SIGCLIP_STD,  /* Sigma-clipped STD of multiple arrays. */
 
+  GAL_ARITHMETIC_OP_SIZE,         /* Size of the dataset along an axis     */
+
   GAL_ARITHMETIC_OP_TO_UINT8,     /* Convert to uint8_t.                   */
   GAL_ARITHMETIC_OP_TO_INT8,      /* Convert to int8_t.                    */
   GAL_ARITHMETIC_OP_TO_UINT16,    /* Convert to uint16_t.                  */
@@ -137,6 +159,8 @@ enum gal_arithmetic_operators
   GAL_ARITHMETIC_OP_TO_FLOAT32,   /* Convert to float32.                   */
   GAL_ARITHMETIC_OP_TO_FLOAT64,   /* Convert to float64.                   */
 
+  GAL_ARITHMETIC_OP_MAKENEW,      /* Build a new dataset, containing zeros.*/
+
   GAL_ARITHMETIC_OP_LAST_CODE,    /* Last code of the library operands.    */
 };
 
diff --git a/lib/gnuastro/array.h b/lib/gnuastro/array.h
index 137d5bc..cd91755 100644
--- a/lib/gnuastro/array.h
+++ b/lib/gnuastro/array.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
diff --git a/lib/gnuastro/binary.h b/lib/gnuastro/binary.h
index 786cfd9..ffc3af2 100644
--- a/lib/gnuastro/binary.h
+++ b/lib/gnuastro/binary.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -47,7 +47,7 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 
-/* The binary functions will be working on a `uint8_t' type dataset with
+/* The binary functions will be working on a 'uint8_t' type dataset with
    values of 1 or 0 (no other pixel will be touched). However, in some
    cases, it is necessary to put temporary values in each element during
    the processing of the functions. So if your input datasets have values
@@ -96,6 +96,10 @@ gal_data_t *
 gal_binary_connected_adjacency_matrix(gal_data_t *adjacency,
                                       size_t *numnewlabs);
 
+gal_data_t *
+gal_binary_connected_adjacency_list(gal_list_sizet_t **listarr,
+                                    size_t number, size_t minmapsize,
+                                    int quietmmap, size_t *numconnected);
 
 /*********************************************************************/
 /*****************            Fill holes          ********************/
diff --git a/lib/gnuastro/blank.h b/lib/gnuastro/blank.h
index 0c52522..211c2b9 100644
--- a/lib/gnuastro/blank.h
+++ b/lib/gnuastro/blank.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -27,18 +27,20 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    must be included before the C++ preparations below */
 #include <gnuastro/data.h>
 
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   `GAL_CONFIG_SIZEOF_SIZE_T' that we need below) is kept in
-   `config.h'. When building a user's programs, this information is kept in
-   `gnuastro/config.h'. Note that all `.c' files in Gnuastro's source must
-   start with the inclusion of `config.h' and that `gnuastro/config.h' is
+   'GAL_CONFIG_SIZEOF_SIZE_T' that we need below) is kept in
+   'config.h'. When building a user's programs, this information is kept in
+   'gnuastro/config.h'. Note that all '.c' files in Gnuastro's source must
+   start with the inclusion of 'config.h' and that 'gnuastro/config.h' is
    only created at installation time (not present during the building of
    Gnuastro). */
 #ifndef IN_GNUASTRO_BUILD
 #include <gnuastro/config.h>
 #endif
 
+#include <gnuastro/list.h>
+
 /* C++ Preparations */
 #undef __BEGIN_C_DECLS
 #undef __END_C_DECLS
@@ -132,8 +134,11 @@ gal_blank_flag_apply(gal_data_t *input, gal_data_t *flag);
 void
 gal_blank_remove(gal_data_t *data);
 
+void
+gal_blank_remove_realloc(gal_data_t *input);
 
-
+gal_data_t *
+gal_blank_remove_rows(gal_data_t *columns, gal_list_sizet_t *column_indexs);
 
 __END_C_DECLS    /* From C++ preparations */
 
diff --git a/lib/gnuastro/box.h b/lib/gnuastro/box.h
index ee79a83..e0d49b5 100644
--- a/lib/gnuastro/box.h
+++ b/lib/gnuastro/box.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro/convolve.h b/lib/gnuastro/convolve.h
index 8e61940..a1a36e4 100644
--- a/lib/gnuastro/convolve.h
+++ b/lib/gnuastro/convolve.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/lib/gnuastro/cosmology.h b/lib/gnuastro/cosmology.h
index b64ed0c..a20f88c 100644
--- a/lib/gnuastro/cosmology.h
+++ b/lib/gnuastro/cosmology.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -88,6 +88,12 @@ double
 gal_cosmology_to_absolute_mag(double z, double H0, double o_lambda_0,
                               double o_matter_0, double o_radiation_0);
 
+double
+gal_cosmology_velocity_from_z(double z);
+
+double
+gal_cosmology_z_from_velocity(double v);
+
 __END_C_DECLS    /* From C++ preparations */
 
 #endif           /* __GAL_COSMOLOGY_H__ */
diff --git a/lib/gnuastro/data.h b/lib/gnuastro/data.h
index 048e05a..fc7e353 100644
--- a/lib/gnuastro/data.h
+++ b/lib/gnuastro/data.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -28,12 +28,12 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <math.h>
 #include <wcslib/wcs.h>
 
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   the `restrict' replacement) is kept in `config.h' (top source
+   the 'restrict' replacement) is kept in 'config.h' (top source
    directory). When building a user's programs, this information is kept in
-   `gnuastro/config.h'. Note that all `.c' files in Gnuastro's source must
-   start with the inclusion of `config.h' and that `gnuastro/config.h' is
+   'gnuastro/config.h'. Note that all '.c' files in Gnuastro's source must
+   start with the inclusion of 'config.h' and that 'gnuastro/config.h' is
    only created at installation time (not present during the building of
    Gnuastro). */
 #ifndef IN_GNUASTRO_BUILD
@@ -66,11 +66,11 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 /* Flag values for the dataset. Note that these are bit-values, so to be
-   more clear, we'll use hexadecimal notation: `0x1' (=1), `0x2' (=2),
-   `0x4' (=4), `0x8' (=8), `0x10' (=16), `0x20' (=32) and so on. */
+   more clear, we'll use hexadecimal notation: '0x1' (=1), '0x2' (=2),
+   '0x4' (=4), '0x8' (=8), '0x10' (=16), '0x20' (=32) and so on. */
 
-/* Number of bytes in the unsigned integer hosting the bit-flags (`flag'
-   element) of `gal_data_t'. */
+/* Number of bytes in the unsigned integer hosting the bit-flags ('flag'
+   element) of 'gal_data_t'. */
 #define GAL_DATA_FLAG_SIZE         1
 
 /* Bit 0: The has-blank flag has been checked, so a flag value of 0 for the
@@ -105,15 +105,15 @@ __BEGIN_C_DECLS  /* From C++ preparations */
    mmap (keep data outside of RAM)
    -------------------------------
 
-     `mmap' is C's facility to keep the data on the HDD/SSD instead of
+     'mmap' is C's facility to keep the data on the HDD/SSD instead of
      inside the RAM. This can be very useful for large data sets which can
      be very memory intensive. Ofcourse, the speed of operation greatly
      decreases when defining not using RAM, but that is worth it because
-     increasing RAM might not be possible. So in `gal_data_t' when the size
+     increasing RAM might not be possible. So in 'gal_data_t' when the size
      of the requested array (in bytes) is larger than a certain minimum
      size (in bytes), Gnuastro won't write the array in memory but on
      non-volatile memory (like HDDs and SSDs) as a file in the
-     `./.gnuastro' directory of the directory it was run from.
+     './.gnuastro' directory of the directory it was run from.
 
          - If mmapname==NULL, then the array is allocated (using malloc, in
            the RAM), otherwise its is mmap'd (is actually a file on the
@@ -123,9 +123,9 @@ __BEGIN_C_DECLS  /* From C++ preparations */
            derivative data structures to follow the same number and decide
            if they should be mmap'd or allocated.
 
-         - `minmapsize' ==  0: array is definitely mmap'd.
+         - 'minmapsize' ==  0: array is definitely mmap'd.
 
-         - `minmapsize' == -1: array is definitely in RAM.
+         - 'minmapsize' == -1: array is definitely in RAM.
 
 
    block (work with only a subset of the data)
@@ -135,8 +135,8 @@ __BEGIN_C_DECLS  /* From C++ preparations */
      or tiles, not necessarily overlapping. In such a way that you can work
      on each independently. One option is to copy that region to a separate
      allocated space, but in many contexts this isn't necessary and infact
-     can be a big burden on CPU/Memory usage. The `block' pointer in
-     `gal_data_t' is defined for situations where allocation is not
+     can be a big burden on CPU/Memory usage. The 'block' pointer in
+     'gal_data_t' is defined for situations where allocation is not
      necessary: you just want to read the data or write to it
      independently, or in coordination with, other regions of the
      dataset. Added with parallel processing, this can greatly improve the
@@ -160,35 +160,35 @@ __BEGIN_C_DECLS  /* From C++ preparations */
                 |*|                       |
                 ---------------------------
 
-     To use `gal_data_t's block concept, you allocate a `gal_data_t *tile'
+     To use 'gal_data_t's block concept, you allocate a 'gal_data_t *tile'
      which is initialized with the pointer to the first element in the
-     sub-array (as its `array' argument). Note that this is not necessarily
+     sub-array (as its 'array' argument). Note that this is not necessarily
      the first element in the larger array. You can set the size of the
      tile along with the initialization as you please. Recall that, when
-     given a non-NULL pointer as `array', `gal_data_initialize' (and thus
-     `gal_data_alloc') do not allocate any space and just uses the given
-     pointer for the new `array' element of the `gal_data_t'. So your
-     `tile' data structure will not be pointing to a separately allocated
+     given a non-NULL pointer as 'array', 'gal_data_initialize' (and thus
+     'gal_data_alloc') do not allocate any space and just uses the given
+     pointer for the new 'array' element of the 'gal_data_t'. So your
+     'tile' data structure will not be pointing to a separately allocated
      space.
 
-     After the allocation is done, you just point `tile->block' to the
-     `gal_data_t' which hosts the larger array. Afterwards, the programs
-     that take in the `sub' array can check the `block' pointer to see how
+     After the allocation is done, you just point 'tile->block' to the
+     'gal_data_t' which hosts the larger array. Afterwards, the programs
+     that take in the 'sub' array can check the 'block' pointer to see how
      to deal with dimensions and increments (strides) while working on the
-     `sub' datastructure. For example to increment along the vertical
-     dimension, the program must look at index i+W (where `W' is the width
+     'sub' datastructure. For example to increment along the vertical
+     dimension, the program must look at index i+W (where 'W' is the width
      of the larger array, not the tile).
 
      Since the block structure is defined as a pointer, arbitrary levels of
      tesselation/griding are possible. Therefore, just like a linked list,
-     it is important to have the `block' pointer of the largest dataset set
+     it is important to have the 'block' pointer of the largest dataset set
      to NULL. Normally, you won't have to worry about this, because
-     `gal_data_initialize' (and thus `gal_data_alloc') will set it to NULL
+     'gal_data_initialize' (and thus 'gal_data_alloc') will set it to NULL
      by default (just remember not to change it). You can then only change
-     the `block' element for the tiles you define over the allocated space.
+     the 'block' element for the tiles you define over the allocated space.
 
      In Gnuastro, there is a separate library for tiling operations called
-     `tile.h', see the functions there for tools to effectively use this
+     'tile.h', see the functions there for tools to effectively use this
      feature. This approach to dealing with parts of a larger block was
      inspired from the way the GNU Scientific Library does it in the
      "Vectors and Matrices" chapter.
@@ -197,7 +197,7 @@ typedef struct gal_data_t
 {
   /* Basic information on array of data. */
   void     *restrict array;  /* Array keeping data elements.               */
-  uint8_t             type;  /* Type of data (see `gnuastro/type.h').      */
+  uint8_t             type;  /* Type of data (see 'gnuastro/type.h').      */
   size_t              ndim;  /* Number of dimensions in the array.         */
   size_t            *dsize;  /* Size of array along each dimension.        */
   size_t              size;  /* Total number of data-elements.             */
@@ -217,13 +217,13 @@ typedef struct gal_data_t
   char            *comment;  /* A more detailed description of the data.   */
 
   /* For printing */
-  int             disp_fmt;  /* See `gal_table_diplay_formats'.            */
+  int             disp_fmt;  /* See 'gal_table_diplay_formats'.            */
   int           disp_width;  /* Width of space to print in ASCII.          */
   int       disp_precision;  /* Precision to print in ASCII.               */
 
   /* Pointers to other data structures. */
   struct gal_data_t  *next;  /* To use it as a linked list if necessary.   */
-  struct gal_data_t *block;  /* `gal_data_t' of hosting block, see above.  */
+  struct gal_data_t *block;  /* 'gal_data_t' of hosting block, see above.  */
 } gal_data_t;
 
 
@@ -263,6 +263,11 @@ gal_data_array_calloc(size_t size);
 void
 gal_data_array_free(gal_data_t *dataarr, size_t num, int free_array);
 
+gal_data_t **
+gal_data_array_ptr_calloc(size_t size);
+
+void
+gal_data_array_ptr_free(gal_data_t **dataptr, size_t size, int free_array);
 
 
 
diff --git a/lib/gnuastro/dimension.h b/lib/gnuastro/dimension.h
index c9bf682..d832cee 100644
--- a/lib/gnuastro/dimension.h
+++ b/lib/gnuastro/dimension.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -95,6 +95,10 @@ gal_dimension_dist_manhattan(size_t *a, size_t *b, size_t 
ndim);
 float
 gal_dimension_dist_radial(size_t *a, size_t *b, size_t ndim);
 
+float
+gal_dimension_dist_elliptical(double *center, double *pa_deg, double *q,
+                              size_t ndim, double *point);
+
 
 
 /************************************************************************/
@@ -141,30 +145,30 @@ gal_dimension_remove_extra(size_t ndim, size_t *dsize, 
struct wcsprm *wcs);
 
    The inputs are:
 
-       `index': The index of the desired point.
+       'index': The index of the desired point.
 
-       `ndim':  The number of dimensions in the dataset.
+       'ndim':  The number of dimensions in the dataset.
 
-       `dsize': The size of the dataset along each dimension (C order).
+       'dsize': The size of the dataset along each dimension (C order).
 
-       `connectivity': The connectivity of the neighbors. This is a single
-                integer with a value between `1' and `ndim' (it is
+       'connectivity': The connectivity of the neighbors. This is a single
+                integer with a value between '1' and 'ndim' (it is
                 irrelevant for a 1D dataset). Below, the case for 3D data
-                is shown. Each `dn' corresponds to `dinc[n]'. See `dinc'
-                below for the `dinc' values.
+                is shown. Each 'dn' corresponds to 'dinc[n]'. See 'dinc'
+                below for the 'dinc' values.
 
-                   `1': At most ONE addition/subtraction. In 1D: only the
+                   '1': At most ONE addition/subtraction. In 1D: only the
                         first line should be used. In 2D, this is 4
                         connectivity. In 3D all the elements that share an
-                        2D-face with the cube at index `i'.
+                        2D-face with the cube at index 'i'.
 
                                        i - d0     i + d0        (1D, 2D & 3D)
                                        i - d1     i + d1        (2D & 3D)
                                        i - d2     i + d2        (3D)
 
-                   `2': At most TWO additions/subtractions. In 2D: 8
+                   '2': At most TWO additions/subtractions. In 2D: 8
                         connectivity. In 3D: all elements that share a 1D
-                        edge with the cube at index `i'.
+                        edge with the cube at index 'i'.
 
                                   i - d0 - d1     i - d0 + d1   (2D & 3D)
                                   i + d0 - d1     i + d0 + d1   (2D & 3D)
@@ -173,16 +177,16 @@ gal_dimension_remove_extra(size_t ndim, size_t *dsize, 
struct wcsprm *wcs);
                                   i - d1 - d2     i - d1 + d2   (3D)
                                   i + d1 - d2     i + d1 + d2   (3D)
 
-                   `3': At most THREE additions/subtractions (only for 3D
+                   '3': At most THREE additions/subtractions (only for 3D
                         and higher dimensions). All cubes that share a 0-D
-                        vertex with the cube at index `i'.
+                        vertex with the cube at index 'i'.
 
                              i - d0 - d1 - d2     i - d0 - d1 + d2
                              i - d0 + d1 - d2     i - d0 + d1 + d2
                              i + d0 - d1 - d2     i + d0 - d1 + d2
                              i + d0 + d1 - d2     i + d0 + d1 + d2
 
-        `dinc': An array keeping the length necessary to increment along
+        'dinc': An array keeping the length necessary to increment along
                 each dimension. You can make this array with the following
                 function:
 
@@ -190,17 +194,17 @@ gal_dimension_remove_extra(size_t ndim, size_t *dsize, 
struct wcsprm *wcs);
 
                 Don't forget to free it afterwards.
 
-        `operation': Any C operation. `nind' is a `size_t' type variable
+        'operation': Any C operation. 'nind' is a 'size_t' type variable
                 that is defined by this macro and will have the index of
-                each neighbor. You can use this `nind' for any processing
-                that you like on the neighbor. Note that `op' will be
+                each neighbor. You can use this 'nind' for any processing
+                that you like on the neighbor. Note that 'op' will be
                 repeated the number of times there is a neighbor.
 
 
    Implementation
    --------------
 
-   To be most efficient (avoid as many `if's as possible), we will start
+   To be most efficient (avoid as many 'if's as possible), we will start
    parsing the neighbors from the fastest dimension. When-ever the element
    is on the edge of the dataset in any dimension, we will store it in a
    bit-wise array (one bit for each dimension, to mark if it is on the edge
@@ -230,8 +234,8 @@ gal_dimension_remove_extra(size_t ndim, size_t *dsize, 
struct wcsprm *wcs);
     gdn_is_end=(uint8_t *)(&gdn_bitstr)+1;                              \
                                                                         \
     /* Start with the slowest dimension and see if it is on the edge */ \
-    /* or not, similar to `gal_dimension_index_to_coord'. In the */     \
-    /* process, also fill the `connectivity==1' neighbors. */           \
+    /* or not, similar to 'gal_dimension_index_to_coord'. In the */     \
+    /* process, also fill the 'connectivity==1' neighbors. */           \
     for(gdn_D=0;gdn_D<ndim;++gdn_D)                                     \
       {                                                                 \
         /* If this dimension is only one element wide, no neighbors. */ \
@@ -265,16 +269,16 @@ gal_dimension_remove_extra(size_t ndim, size_t *dsize, 
struct wcsprm *wcs);
               }                                                         \
           }                                                             \
                                                                         \
-        /* Change `ind' to the remainder of previous dimensions. */     \
+        /* Change 'ind' to the remainder of previous dimensions. */     \
         gdn_ind %= dinc[gdn_D];                                         \
       }                                                                 \
                                                                         \
     /* We now know if the index is on the edge or not. During the */    \
-    /* process above, we also finished the `connectivity==1' case. */   \
+    /* process above, we also finished the 'connectivity==1' case. */   \
     /* So we'll just have to find the rest of the terms. */             \
     if(connectivity>1 && ndim>1)                                        \
       {                                                                 \
-        /* Finalize `is_edge' (bit value 1 for respective dim.). */     \
+        /* Finalize 'is_edge' (bit value 1 for respective dim.). */     \
         gdn_is_edge=(uint8_t *)(&gdn_bitstr)+2;                         \
         *gdn_is_edge = *gdn_is_start | *gdn_is_end;                     \
                                                                         \
diff --git a/lib/gnuastro/eps.h b/lib/gnuastro/eps.h
index 4026bde..2c90584 100644
--- a/lib/gnuastro/eps.h
+++ b/lib/gnuastro/eps.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro/fits.h b/lib/gnuastro/fits.h
index b330baa..aebd667 100644
--- a/lib/gnuastro/fits.h
+++ b/lib/gnuastro/fits.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -23,12 +23,12 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #ifndef __GAL_FITS_H__
 #define __GAL_FITS_H__
 
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   `GAL_CONFIG_HAVE_WCSLIB_VERION' that we need in `fits.c') is kept in
-   `config.h'. When building a user's programs, this information is kept in
-   `gnuastro/config.h'. Note that all `.c' files must start with the
-   inclusion of `config.h' and that `gnuastro/config.h' is only created at
+   'GAL_CONFIG_HAVE_WCSLIB_VERION' that we need in 'fits.c') is kept in
+   'config.h'. When building a user's programs, this information is kept in
+   'gnuastro/config.h'. Note that all '.c' files must start with the
+   inclusion of 'config.h' and that 'gnuastro/config.h' is only created at
    installation time (not present during the building of Gnuastro).*/
 #ifndef IN_GNUASTRO_BUILD
 #include <gnuastro/config.h>
@@ -47,7 +47,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #include <gnuastro/list.h>
 #include <gnuastro/data.h>
-#include <gnuastro/table.h>
+/* gnuastro/table.h is included below. */
 
 /* C++ Preparations */
 #undef __BEGIN_C_DECLS
@@ -77,19 +77,29 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 /* To create a linked list of headers. */
 typedef struct gal_fits_list_key_t
 {
-  int                        kfree;   /* ==1, free keyword name.   */
-  int                        vfree;   /* ==1, free keyword value.  */
-  int                        cfree;   /* ==1, free comment.        */
-  uint8_t                     type;   /* Keyword value type.       */
+  char                      *title;   /* !=NULL, only print title. */
+  char                *fullcomment;   /* Fully COMMENT (no name).  */
   char                    *keyname;   /* Keyword Name.             */
   void                      *value;   /* Keyword value.            */
+  uint8_t                     type;   /* Keyword value type.       */
   char                    *comment;   /* Keyword comment.          */
   char                       *unit;   /* Keyword unit.             */
+  int                        tfree;   /* ==1, free title string.   */
+  int                       fcfree;   /* ==1, free full comment.   */
+  int                        kfree;   /* ==1, free keyword name.   */
+  int                        vfree;   /* ==1, free keyword value.  */
+  int                        cfree;   /* ==1, free comment.        */
+  int                        ufree;   /* ==1, free unit.           */
   struct gal_fits_list_key_t *next;   /* Pointer next keyword.     */
 } gal_fits_list_key_t;
 
 
 
+/* table.h needs 'gal_fits_list_key_t'. */
+#include <gnuastro/table.h>
+
+
+
 
 
 /*************************************************************
@@ -146,9 +156,18 @@ gal_fits_open_to_write(char *filename);
 size_t
 gal_fits_hdu_num(char *filename);
 
+unsigned long
+gal_fits_hdu_datasum(char *filename, char *hdu);
+
+unsigned long
+gal_fits_hdu_datasum_ptr(fitsfile *fptr);
+
 int
 gal_fits_hdu_format(char *filename, char *hdu);
 
+int
+gal_fits_hdu_is_healpix(fitsfile *fptr);
+
 fitsfile *
 gal_fits_hdu_open(char *filename, char *hdu, int iomode);
 
@@ -185,12 +204,28 @@ gal_fits_key_read(char *filename, char *hdu, gal_data_t 
*keysll,
 void
 gal_fits_key_list_add(gal_fits_list_key_t **list, uint8_t type,
                       char *keyname, int kfree, void *value, int vfree,
-                      char *comment, int cfree, char *unit);
+                      char *comment, int cfree, char *unit, int ufree);
 
 void
 gal_fits_key_list_add_end(gal_fits_list_key_t **list, uint8_t type,
                           char *keyname, int kfree, void *value, int vfree,
-                          char *comment, int cfree, char *unit);
+                          char *comment, int cfree, char *unit, int ufree);
+
+void
+gal_fits_key_list_title_add(gal_fits_list_key_t **list, char *title,
+                            int tfree);
+
+void
+gal_fits_key_list_title_add_end(gal_fits_list_key_t **list, char *title,
+                                int tfree);
+
+void
+gal_fits_key_list_fullcomment_add(gal_fits_list_key_t **list,
+                                  char *fullcomment, int fcfree);
+
+void
+gal_fits_key_list_fullcomment_add_end(gal_fits_list_key_t **list,
+                                      char *fullcomment, int fcfree);
 
 void
 gal_fits_key_list_reverse(gal_fits_list_key_t **list);
@@ -203,7 +238,8 @@ gal_fits_key_write_filename(char *keynamebase, char 
*filename,
                             gal_fits_list_key_t **list, int top1end0);
 
 void
-gal_fits_key_write_wcsstr(fitsfile *fptr, char *wcsstr, int nkeyrec);
+gal_fits_key_write_wcsstr(fitsfile *fptr, struct wcsprm *wcs,
+                          char *wcsstr, int nkeyrec);
 
 void
 gal_fits_key_write(gal_fits_list_key_t **keylist, char *title,
@@ -291,7 +327,8 @@ gal_fits_tab_read(char *filename, char *hdu, size_t numrows,
 
 void
 gal_fits_tab_write(gal_data_t *cols, gal_list_str_t *comments,
-                   int tableformat, char *filename, char *extname);
+                   int tableformat, char *filename, char *extname,
+                   struct gal_fits_list_key_t **keywords);
 
 
 
diff --git a/lib/gnuastro/git.h b/lib/gnuastro/git.h
index f3c4f8d..d7f5762 100644
--- a/lib/gnuastro/git.h
+++ b/lib/gnuastro/git.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro/interpolate.h b/lib/gnuastro/interpolate.h
index 5f1ec26..a3bf212 100644
--- a/lib/gnuastro/interpolate.h
+++ b/lib/gnuastro/interpolate.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -48,12 +48,24 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 /* Metrics to use for nearest-neighbor  */
-enum gal_interpolate_close_metric
+enum gal_interpolate_neighbors_metric
 {
- GAL_INTERPOLATE_CLOSE_METRIC_INVALID,
+ GAL_INTERPOLATE_NEIGHBORS_METRIC_INVALID,
 
- GAL_INTERPOLATE_CLOSE_METRIC_RADIAL,
- GAL_INTERPOLATE_CLOSE_METRIC_MANHATTAN,
+ GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL,
+ GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN,
+};
+
+
+
+/* Types of interpolation for nearest-neighbor. */
+enum gal_interpolate_neighbors_func
+{
+ GAL_INTERPOLATE_NEIGHBORS_FUNC_INVALID,
+
+ GAL_INTERPOLATE_NEIGHBORS_FUNC_MIN,
+ GAL_INTERPOLATE_NEIGHBORS_FUNC_MAX,
+ GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN,
 };
 
 
@@ -75,11 +87,11 @@ enum gal_interpolate_1D_types
 
 
 gal_data_t *
-gal_interpolate_close_neighbors(gal_data_t *input,
-                                struct gal_tile_two_layer_params *tl,
-                                uint8_t metric, size_t numneighbors,
-                                size_t numthreads, int onlyblank,
-                                int aslinkedlist);
+gal_interpolate_neighbors(gal_data_t *input,
+                          struct gal_tile_two_layer_params *tl,
+                          uint8_t metric, size_t numneighbors,
+                          size_t numthreads, int onlyblank,
+                          int aslinkedlist, int function);
 
 gsl_spline *
 gal_interpolate_1d_make_gsl_spline(gal_data_t *X, gal_data_t *Y, int type_1d);
diff --git a/lib/gnuastro/jpeg.h b/lib/gnuastro/jpeg.h
index 9ab0cf4..caeb1e7 100644
--- a/lib/gnuastro/jpeg.h
+++ b/lib/gnuastro/jpeg.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro/jpeg.h b/lib/gnuastro/kdtree.h
similarity index 76%
copy from lib/gnuastro/jpeg.h
copy to lib/gnuastro/kdtree.h
index 9ab0cf4..31db486 100644
--- a/lib/gnuastro/jpeg.h
+++ b/lib/gnuastro/kdtree.h
@@ -1,11 +1,12 @@
 /*********************************************************************
-jpeg -- functions to read and write JPEG files.
+kdtree -- Create kd-tree and nearest neighbour searches.
 This is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
-     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+     Sachin Kumar Singh <sachinkumarsingh092@gmail.com>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Copyright (C) 2020-2021, 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,16 +21,14 @@ 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_JPEG_H__
-#define __GAL_JPEG_H__
-
+#ifndef __GAL_KDTREE_H__
+#define __GAL_KDTREE_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 */
 #undef __BEGIN_C_DECLS
 #undef __END_C_DECLS
@@ -44,32 +43,20 @@ 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 */
 
 
 
-
-
-/* Functions */
-int
-gal_jpeg_name_is_jpeg(char *name);
-
-int
-gal_jpeg_suffix_is_jpeg(char *name);
-
 gal_data_t *
-gal_jpeg_read(char *filename, size_t minmapsize, int quietmmap);
-
-void
-gal_jpeg_write(gal_data_t *in, char *filename, uint8_t quality,
-               float widthincm);
+gal_kdtree_create(gal_data_t *coords_raw, size_t *root);
 
+size_t
+gal_kdtree_nearest_neighbour(gal_data_t *coords_raw, gal_data_t *kdtree,
+                             size_t root, double *point, double *least_dist);
 
 
 
 __END_C_DECLS    /* From C++ preparations */
 
-#endif           /* __GAL_TIFF_H__ */
+#endif           /* __GAL_KDTREE_H__ */
diff --git a/lib/gnuastro/label.h b/lib/gnuastro/label.h
index 73299f9..3472c4e 100644
--- a/lib/gnuastro/label.h
+++ b/lib/gnuastro/label.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
diff --git a/lib/gnuastro/list.h b/lib/gnuastro/list.h
index 202cc8b..60a0d74 100644
--- a/lib/gnuastro/list.h
+++ b/lib/gnuastro/list.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro/match.h b/lib/gnuastro/match.h
index 4804f16..aa1a7c6 100644
--- a/lib/gnuastro/match.h
+++ b/lib/gnuastro/match.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/lib/gnuastro/pdf.h b/lib/gnuastro/pdf.h
index 5ca76c4..14fb5fd 100644
--- a/lib/gnuastro/pdf.h
+++ b/lib/gnuastro/pdf.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro/permutation.h b/lib/gnuastro/permutation.h
index ba73d87..3e1032b 100644
--- a/lib/gnuastro/permutation.h
+++ b/lib/gnuastro/permutation.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/lib/gnuastro/pointer.h b/lib/gnuastro/pointer.h
index e3f6b6d..9bb297c 100644
--- a/lib/gnuastro/pointer.h
+++ b/lib/gnuastro/pointer.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -59,10 +59,17 @@ gal_pointer_allocate(uint8_t type, size_t size, int clear,
                      const char *funcname, const char *varname);
 
 void *
-gal_pointer_allocate_mmap(uint8_t type, size_t size, int clear,
+gal_pointer_mmap_allocate(uint8_t type, size_t size, int clear,
                           char **filename, int quiet);
 
+void
+gal_pointer_mmap_free(char **mmapname, int quietmmap);
 
+void *
+gal_pointer_allocate_ram_or_mmap(uint8_t type, size_t size, int clear,
+                                 size_t minmapsize, char **mmapname,
+                                 int quietmmap, const char *funcname,
+                                 const char *varname);
 
 
 
diff --git a/lib/gnuastro/polygon.h b/lib/gnuastro/polygon.h
index 70fe416..bfa7e89 100644
--- a/lib/gnuastro/polygon.h
+++ b/lib/gnuastro/polygon.h
@@ -5,7 +5,8 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+     Sachin Kumar Singh <sachinkumarsingh092@gmail.com>
+Copyright (C) 2015-2021, 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
@@ -58,21 +59,35 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 /**************     Function declarations     ******************/
 /***************************************************************/
 void
-gal_polygon_ordered_corners(double *in, size_t n, size_t *ordinds);
+gal_polygon_vertices_sort_convex(double *in, size_t n, size_t *ordinds);
+
+int
+gal_polygon_is_convex(double *v, size_t n);
 
 double
 gal_polygon_area(double *v, size_t n);
 
 int
-gal_polygon_pin(double *v, double *p, size_t n);
+gal_polygon_is_inside(double *v, double *p, size_t n);
+
+int
+gal_polygon_is_inside_convex(double *v, double *p, size_t n);
 
 int
 gal_polygon_ppropin(double *v, double *p, size_t n);
 
+int
+gal_polygon_is_counterclockwise(double *v, size_t n);
+
+int
+gal_polygon_to_counterclockwise(double *v, size_t n);
+
 void
 gal_polygon_clip(double *s, size_t n, double *c, size_t m,
                  double *o, size_t *numcrn);
 
+void
+gal_polygon_vertices_sort(double *in, size_t n, size_t *ordinds);
 
 __END_C_DECLS    /* From C++ preparations */
 
diff --git a/lib/gnuastro/qsort.h b/lib/gnuastro/qsort.h
index f7bd374..64f16c8 100644
--- a/lib/gnuastro/qsort.h
+++ b/lib/gnuastro/qsort.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/lib/gnuastro/speclines.h b/lib/gnuastro/speclines.h
index add541d..06a85dc 100644
--- a/lib/gnuastro/speclines.h
+++ b/lib/gnuastro/speclines.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2019, Free Software Foundation, Inc.
+Copyright (C) 2019-2021, 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
@@ -46,10 +46,13 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 __BEGIN_C_DECLS  /* From C++ preparations */
 
 
-/* Spectral line internal codes. */
+/* Spectral line internal codes (SORT BY WAVELENGTH). */
 enum gal_speclines_line_codes
 {
+  /* Allowing '0' to be identied as a known-non-line. */
   GAL_SPECLINES_INVALID=0,
+
+  /* Main list of recognized lines. */
   GAL_SPECLINES_SIIRED,
   GAL_SPECLINES_SII,
   GAL_SPECLINES_SIIBLUE,
@@ -57,11 +60,11 @@ enum gal_speclines_line_codes
   GAL_SPECLINES_NII,
   GAL_SPECLINES_HALPHA,
   GAL_SPECLINES_NIIBLUE,
-  GAL_SPECLINES_OIIIRED,
-  GAL_SPECLINES_OIII,
-  GAL_SPECLINES_OIIIBLUE,
+  GAL_SPECLINES_OIIIRED_VIS,
+  GAL_SPECLINES_OIII_VIS,
+  GAL_SPECLINES_OIIIBLUE_VIS,
   GAL_SPECLINES_HBETA,
-  GAL_SPECLINES_HEIIRED,
+  GAL_SPECLINES_HEII_VIS,
   GAL_SPECLINES_HGAMMA,
   GAL_SPECLINES_HDELTA,
   GAL_SPECLINES_HEPSILON,
@@ -76,13 +79,31 @@ enum gal_speclines_line_codes
   GAL_SPECLINES_CIIIRED,
   GAL_SPECLINES_CIII,
   GAL_SPECLINES_CIIIBLUE,
-  GAL_SPECLINES_HEIIBLUE,
+  GAL_SPECLINES_SiIIIRED,
+  GAL_SPECLINES_SiIII,
+  GAL_SPECLINES_SiIIIBLUE,
+  GAL_SPECLINES_OIIIRED_UV,
+  GAL_SPECLINES_OIII_UV,
+  GAL_SPECLINES_OIIIBLUE_UV,
+  GAL_SPECLINES_HEII_UV,
+  GAL_SPECLINES_CIVRED,
+  GAL_SPECLINES_CIV,
+  GAL_SPECLINES_CIVBLUE,
+  GAL_SPECLINES_NV,
   GAL_SPECLINES_LYALPHA,
+  GAL_SPECLINES_LYBETA,
+  GAL_SPECLINES_LYGAMMA,
+  GAL_SPECLINES_LYDELTA,
+  GAL_SPECLINES_LYEPSILON,
   GAL_SPECLINES_LYLIMIT,
+
+  /* This should be the last element (to keep the total number of
+     lines). */
+  GAL_SPECLINES_INVALID_MAX,
 };
 
 
-/* Spectral lines wavelengths in Angstroms. */
+/* Spectral lines wavelengths in Angstroms (SORT BY WAVELENGTH). */
 #define GAL_SPECLINES_ANGSTROM_SIIRED    6731
 #define GAL_SPECLINES_ANGSTROM_SII       6724
 #define GAL_SPECLINES_ANGSTROM_SIIBLUE   6717
@@ -90,11 +111,11 @@ enum gal_speclines_line_codes
 #define GAL_SPECLINES_ANGSTROM_NII       6566
 #define GAL_SPECLINES_ANGSTROM_HALPHA    6562.8
 #define GAL_SPECLINES_ANGSTROM_NIIBLUE   6548
-#define GAL_SPECLINES_ANGSTROM_OIIIRED   5007
-#define GAL_SPECLINES_ANGSTROM_OIII      4983
-#define GAL_SPECLINES_ANGSTROM_OIIIBLUE  4959
+#define GAL_SPECLINES_ANGSTROM_OIIIRED_VIS 5007
+#define GAL_SPECLINES_ANGSTROM_OIII_VIS  4983
+#define GAL_SPECLINES_ANGSTROM_OIIIBLUE_VIS 4959
 #define GAL_SPECLINES_ANGSTROM_HBETA     4861.36
-#define GAL_SPECLINES_ANGSTROM_HEIIRED   4686
+#define GAL_SPECLINES_ANGSTROM_HEII_VIS  4686
 #define GAL_SPECLINES_ANGSTROM_HGAMMA    4340.46
 #define GAL_SPECLINES_ANGSTROM_HDELTA    4101.74
 #define GAL_SPECLINES_ANGSTROM_HEPSILON  3970.07
@@ -109,12 +130,26 @@ enum gal_speclines_line_codes
 #define GAL_SPECLINES_ANGSTROM_CIIIRED   1909
 #define GAL_SPECLINES_ANGSTROM_CIII      1908
 #define GAL_SPECLINES_ANGSTROM_CIIIBLUE  1907
-#define GAL_SPECLINES_ANGSTROM_HEIIBLUE  1640
+#define GAL_SPECLINES_ANGSTROM_SiIIIRED  1892
+#define GAL_SPECLINES_ANGSTROM_SiIII     1887.5
+#define GAL_SPECLINES_ANGSTROM_SiIIIBLUE 1883
+#define GAL_SPECLINES_ANGSTROM_OIIIRED_UV 1666
+#define GAL_SPECLINES_ANGSTROM_OIII_UV   1663.5
+#define GAL_SPECLINES_ANGSTROM_OIIIBLUE_UV 1661
+#define GAL_SPECLINES_ANGSTROM_HEII_UV   1640
+#define GAL_SPECLINES_ANGSTROM_CIVRED    1551
+#define GAL_SPECLINES_ANGSTROM_CIV       1549.5
+#define GAL_SPECLINES_ANGSTROM_CIVBLUE   1548
+#define GAL_SPECLINES_ANGSTROM_NV        1240
 #define GAL_SPECLINES_ANGSTROM_LYALPHA   1215.67
+#define GAL_SPECLINES_ANGSTROM_LYBETA    1025.7
+#define GAL_SPECLINES_ANGSTROM_LYGAMMA   972.54
+#define GAL_SPECLINES_ANGSTROM_LYDELTA   949.74
+#define GAL_SPECLINES_ANGSTROM_LYEPSILON 937.80
 #define GAL_SPECLINES_ANGSTROM_LYLIMIT   912
 
 
-/* Spectral line name strings. */
+/* Spectral line name strings (SORT BY WAVELENGTH). */
 #define GAL_SPECLINES_NAME_SIIRED    "siired"
 #define GAL_SPECLINES_NAME_SII       "sii"
 #define GAL_SPECLINES_NAME_SIIBLUE   "siiblue"
@@ -122,11 +157,11 @@ enum gal_speclines_line_codes
 #define GAL_SPECLINES_NAME_NII       "nii"
 #define GAL_SPECLINES_NAME_HALPHA    "halpha"
 #define GAL_SPECLINES_NAME_NIIBLUE   "niiblue"
-#define GAL_SPECLINES_NAME_OIIIRED   "oiiired"
-#define GAL_SPECLINES_NAME_OIII      "oiii"
-#define GAL_SPECLINES_NAME_OIIIBLUE  "oiiiblue"
+#define GAL_SPECLINES_NAME_OIIIRED_VIS "oiiired-vis"
+#define GAL_SPECLINES_NAME_OIII_VIS  "oiii-vis"
+#define GAL_SPECLINES_NAME_OIIIBLUE_VIS "oiiiblue-vis"
 #define GAL_SPECLINES_NAME_HBETA     "hbeta"
-#define GAL_SPECLINES_NAME_HEIIRED   "heiired"
+#define GAL_SPECLINES_NAME_HEII_VIS  "heii-vis"
 #define GAL_SPECLINES_NAME_HGAMMA    "hgamma"
 #define GAL_SPECLINES_NAME_HDELTA    "hdelta"
 #define GAL_SPECLINES_NAME_HEPSILON  "hepsilon"
@@ -141,8 +176,22 @@ enum gal_speclines_line_codes
 #define GAL_SPECLINES_NAME_CIIIRED   "ciiired"
 #define GAL_SPECLINES_NAME_CIII      "ciii"
 #define GAL_SPECLINES_NAME_CIIIBLUE  "ciiiblue"
-#define GAL_SPECLINES_NAME_HEIIBLUE  "heiiblue"
+#define GAL_SPECLINES_NAME_SiIIIRED  "si_iiired"
+#define GAL_SPECLINES_NAME_SiIII     "si_iii"
+#define GAL_SPECLINES_NAME_SiIIIBLUE "si_iiiblue"
+#define GAL_SPECLINES_NAME_OIIIRED_UV "oiiired-uv"
+#define GAL_SPECLINES_NAME_OIII_UV   "oiii-uv"
+#define GAL_SPECLINES_NAME_OIIIBLUE_UV "oiiiblue-uv"
+#define GAL_SPECLINES_NAME_HEII_UV   "heii-uv"
+#define GAL_SPECLINES_NAME_CIVRED    "civred"
+#define GAL_SPECLINES_NAME_CIV       "civ"
+#define GAL_SPECLINES_NAME_CIVBLUE   "civblue"
+#define GAL_SPECLINES_NAME_NV        "nv"
 #define GAL_SPECLINES_NAME_LYALPHA   "lyalpha"
+#define GAL_SPECLINES_NAME_LYBETA    "lybeta"
+#define GAL_SPECLINES_NAME_LYGAMMA   "lygamma"
+#define GAL_SPECLINES_NAME_LYDELTA   "lydelta"
+#define GAL_SPECLINES_NAME_LYEPSILON "lyepsilon"
 #define GAL_SPECLINES_NAME_LYLIMIT   "lylimit"
 
 
diff --git a/lib/gnuastro/statistics.h b/lib/gnuastro/statistics.h
index 9dcf35a..64fa0cc 100644
--- a/lib/gnuastro/statistics.h
+++ b/lib/gnuastro/statistics.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -108,6 +108,9 @@ gal_data_t *
 gal_statistics_quantile_function(gal_data_t *input, gal_data_t *value,
                                  int inplace);
 
+gal_data_t *
+gal_statistics_unique(gal_data_t *input, int inplace);
+
 
 
 
@@ -160,6 +163,9 @@ gal_statistics_histogram(gal_data_t *data, gal_data_t *bins,
                          int normalize, int maxhistone);
 
 gal_data_t *
+gal_statistics_histogram2d(gal_data_t *input, gal_data_t *bins);
+
+gal_data_t *
 gal_statistics_cfp(gal_data_t *data, gal_data_t *bins, int normalize);
 
 
@@ -174,9 +180,10 @@ gal_statistics_sigma_clip(gal_data_t *input, float multip, 
float param,
                           int inplace, int quiet);
 
 gal_data_t *
-gal_statistics_outlier_positive(gal_data_t *input, size_t window_size,
-                                float sigma, float sigclip_multip,
-                                float sigclip_param, int inplace, int quiet);
+gal_statistics_outlier_bydistance(int pos1_neg0, gal_data_t *input,
+                                  size_t window_size, float sigma,
+                                  float sigclip_multip, float sigclip_param,
+                                  int inplace, int quiet);
 
 gal_data_t *
 gal_statistics_outlier_flat_cfp(gal_data_t *input, size_t numprev,
diff --git a/lib/gnuastro/table.h b/lib/gnuastro/table.h
index f0808f3..02af21c 100644
--- a/lib/gnuastro/table.h
+++ b/lib/gnuastro/table.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -54,7 +54,7 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 
-/* `printf' default formattings. Note that the string type shouldn't have
+/* 'printf' default formattings. Note that the string type shouldn't have
    any precision and for the width,  */
 #define GAL_TABLE_DEF_WIDTH_STR       6
 #define GAL_TABLE_DEF_WIDTH_INT       6
@@ -72,7 +72,7 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 /* Particular display formats for different types: Integers or floating
    point numbers can be printed in various display formats. The values here
-   are stored in `gal_data_t' when necessary to help in printing of the
+   are stored in 'gal_data_t' when necessary to help in printing of the
    data.*/
 enum gal_table_diplay_formats
 {
@@ -156,9 +156,9 @@ gal_table_comments_add_intro(gal_list_str_t **comments,
                              char *program_string, time_t *rawtime);
 
 void
-gal_table_write(gal_data_t *cols, gal_list_str_t *comments,
-                int tableformat, char *filename, char *extname,
-                uint8_t colinfoinstdout);
+gal_table_write(gal_data_t *cols, struct gal_fits_list_key_t **keylist,
+                gal_list_str_t *comments, int tableformat, char *filename,
+                char *extname, uint8_t colinfoinstdout);
 
 void
 gal_table_write_log(gal_data_t *logll, char *program_string,
diff --git a/lib/gnuastro/threads.h b/lib/gnuastro/threads.h
index 605c264..e5c3520 100644
--- a/lib/gnuastro/threads.h
+++ b/lib/gnuastro/threads.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -28,12 +28,12 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <pthread.h>
 #include <gnuastro/blank.h>
 
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   `GAL_CONFIG_HAVE_PTHREAD_BARRIER' that we need below) is kept in
-   `config.h'. When building a user's programs, this information is kept in
-   `gnuastro/config.h'. Note that all `.c' files must start with the
-   inclusion of `config.h' and that `gnuastro/config.h' is only created at
+   'GAL_CONFIG_HAVE_PTHREAD_BARRIER' that we need below) is kept in
+   'config.h'. When building a user's programs, this information is kept in
+   'gnuastro/config.h'. Note that all '.c' files must start with the
+   inclusion of 'config.h' and that 'gnuastro/config.h' is only created at
    installation time (not present during the building of Gnuastro).*/
 #ifndef IN_GNUASTRO_BUILD
 #include <gnuastro/config.h>
@@ -65,7 +65,7 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 /*****************************************************************/
 #if GAL_CONFIG_HAVE_PTHREAD_BARRIER == 0
 
-/* Integer number of nano-seconds that `pthread_barrier_destroy' should
+/* Integer number of nano-seconds that 'pthread_barrier_destroy' should
    wait for a check to see if all barriers have been reached. */
 #define GAL_THREADS_BARRIER_DESTROY_NANOSECS 1000
 
@@ -102,8 +102,9 @@ pthread_barrier_destroy(pthread_barrier_t *b);
 size_t
 gal_threads_number();
 
-void
+char *
 gal_threads_dist_in_threads(size_t numactions, size_t numthreads,
+                            size_t minmapsize, int quietmmap,
                             size_t **outthrds, size_t *outthrdcols);
 
 void
@@ -126,7 +127,8 @@ struct gal_threads_params
 
 void
 gal_threads_spin_off(void *(*worker)(void *), void *caller_params,
-                     size_t numactions, size_t numthreads);
+                     size_t numactions, size_t numthreads,
+                     size_t minmapsize, int quietmmap);
 
 
 __END_C_DECLS    /* From C++ preparations */
diff --git a/lib/gnuastro/tiff.h b/lib/gnuastro/tiff.h
index aba3379..5b4e8a6 100644
--- a/lib/gnuastro/tiff.h
+++ b/lib/gnuastro/tiff.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
diff --git a/lib/gnuastro/tile.h b/lib/gnuastro/tile.h
index 59fe387..b6044ce 100644
--- a/lib/gnuastro/tile.h
+++ b/lib/gnuastro/tile.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -125,11 +125,11 @@ struct gal_tile_two_layer_params
   size_t         *numtilesinch; /* Tile no. in each dim. on one channel.  */
   char          *tilecheckname; /* Name of file to check tiles.           */
   size_t          *permutation; /* Tile pos. in memory --> pos. overall.  */
-  size_t           *firsttsize; /* See `gal_tile_full_regular_first'.     */
+  size_t           *firsttsize; /* See 'gal_tile_full_regular_first'.     */
 
   /* Actual tile and channel data structures. */
-  gal_data_t            *tiles; /* Tiles array (also linked with `next'). */
-  gal_data_t         *channels; /* Channels array (linked with `next').   */
+  gal_data_t            *tiles; /* Tiles array (also linked with 'next'). */
+  gal_data_t         *channels; /* Channels array (linked with 'next').   */
 };
 
 
@@ -179,11 +179,11 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
 /***********************************************************************/
 /* Useful when the input and other types are already known. We want this to
    be self-sufficient (and be possible to call it independent of
-   `GAL_TILE_PARSE_OPERATE'), so some variables (basic definitions) that
-   are already defined in `GAL_TILE_PARSE_OPERATE' re-defined here. */
+   'GAL_TILE_PARSE_OPERATE'), so some variables (basic definitions) that
+   are already defined in 'GAL_TILE_PARSE_OPERATE' re-defined here. */
 #define GAL_TILE_PO_OISET(IT, OT, IN, OTHER, PARSE_OTHER, CHECK_BLANK, OP) { \
     IT *i=IN->array;                                                    \
-    gal_data_t *tpo_other=OTHER; /* `OTHER' may be NULL. */             \
+    gal_data_t *tpo_other=OTHER; /* 'OTHER' may be NULL. */             \
     gal_data_t *tpo_oblock = OTHER ? gal_tile_block(OTHER) : NULL;      \
                                                                         \
     size_t tpo_s_e_i_junk[2]={0,0};                                     \
@@ -197,19 +197,19 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
     size_t tpo_s_e_i[2]={0,tpo_iblock->size-1}; /* -1: this is INCLUSIVE */ \
                                                                         \
                                                                         \
-    /* A small sanity check: if `OTHER' is given, and it is a block, */ \
-    /* then it must have the same size as `IN's block. On the other  */ \
-    /* hand, when `OTHER' is a tile, its must have `IN's size.       */ \
+    /* A small sanity check: if 'OTHER' is given, and it is a block, */ \
+    /* then it must have the same size as 'IN's block. On the other  */ \
+    /* hand, when 'OTHER' is a tile, its must have 'IN's size.       */ \
     if( tpo_parse_other )                                               \
       {                                                                 \
-        if( OTHER==tpo_oblock )    /* `OTHER' is a block. */            \
+        if( OTHER==tpo_oblock )    /* 'OTHER' is a block. */            \
           {                                                             \
             if( gal_dimension_is_different(tpo_iblock, tpo_oblock) )    \
               {                                                         \
-                /* `error' function, is a GNU extension, see above. */  \
+                /* 'error' function, is a GNU extension, see above. */  \
                 fprintf(stderr, "GAL_TILE_PO_OISET: when "              \
-                        "`PARSE_OTHER' is non-zero, the allocated "     \
-                        "block size of `IN' and `OTHER' must be "       \
+                        "'PARSE_OTHER' is non-zero, the allocated "     \
+                        "block size of 'IN' and 'OTHER' must be "       \
                         "equal, but they are not: %zu and %zu "         \
                         "elements respectively\n", tpo_iblock->size,    \
                         tpo_oblock->size);                              \
@@ -219,12 +219,12 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
         else                                                            \
           if( gal_dimension_is_different(IN, OTHER) )                   \
             {                                                           \
-              /* The `error' function, is a GNU extension and this */   \
+              /* The 'error' function, is a GNU extension and this */   \
               /* is a header, not a library which the user has to  */   \
               /* compile every time (on their own system).         */   \
               fprintf(stderr, "GAL_TILE_PO_OISET: when "                \
-                      "`PARSE_OTHER' is non-zero, the sizes of `IN' "   \
-                      "and `OTHER' must be equal (in all "              \
+                      "'PARSE_OTHER' is non-zero, the sizes of 'IN' "   \
+                      "and 'OTHER' must be equal (in all "              \
                       "dimensions), but they are not: %zu and %zu "     \
                       "elements respectively\n", IN->size,              \
                       tpo_other->size);                                 \
@@ -233,16 +233,16 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
       }                                                                 \
                                                                         \
                                                                         \
-    /* Write the blank value for the input type into `b'. */            \
+    /* Write the blank value for the input type into 'b'. */            \
     gal_blank_write(&b, tpo_iblock->type);                              \
                                                                         \
                                                                         \
     /* If this is a tile, not a full block, then we need to set the  */ \
-    /* starting pointers (`tpo_st' and `tpo_ost'). The latter needs  */ \
+    /* starting pointers ('tpo_st' and 'tpo_ost'). The latter needs  */ \
     /* special attention: if it is a block, then we will use the     */ \
-    /* the same starting element as the input tile. If `OTHER' is a  */ \
+    /* the same starting element as the input tile. If 'OTHER' is a  */ \
     /* tile, then use its own starting position (recall that we have */ \
-    /* already made sure that `IN' and `OTHER' have the same size.   */ \
+    /* already made sure that 'IN' and 'OTHER' have the same size.   */ \
     if(IN!=tpo_iblock)                                                  \
       {                                                                 \
         tpo_st = gal_tile_start_end_ind_inclusive(IN, tpo_iblock,       \
@@ -260,7 +260,7 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
     /* Go over contiguous patches of memory. */                         \
     while( tpo_s_e_i[0] + tpo_i_increment <= tpo_s_e_i[1] )             \
       {                                                                 \
-        /* If we are on a tile, reset `i' and `o'. */                   \
+        /* If we are on a tile, reset 'i' and 'o'. */                   \
         if(IN!=tpo_iblock)                                              \
           {                                                             \
             tpo_f = ( ( i = tpo_st + tpo_i_increment )                  \
@@ -270,9 +270,9 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
                                                                         \
         /* Do the operation depending the nature of the blank value. */ \
         /* Recall that for integer types, the blank value must be    */ \
-        /* checked with `=='. But for floats, the blank value can be */ \
+        /* checked with '=='. But for floats, the blank value can be */ \
         /* a NaN. Recall that a NAN will fail any comparison         */ \
-        /* including `=='. So when the blank value is not equal to   */ \
+        /* including '=='. So when the blank value is not equal to   */ \
         /* itself, then it is floating point and is a NAN. In that   */ \
         /* case, the only way to check if a data element is blank or */ \
         /* not is to see if the value of each element is equal to    */ \
@@ -288,9 +288,9 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
           do    {           {OP;} if(tpo_parse_other) ++o;} while(++i<tpo_f);\
                                                                         \
         /* Set the incrementation. On a fully allocated iblock (when */ \
-        /* `IN==tpo_iblock'), we have already gone through the whole */ \
+        /* 'IN==tpo_iblock'), we have already gone through the whole */ \
         /* array, so we'll set the incrementation to the size of the */ \
-        /* whole block. This will stop the `while' loop above. On a  */ \
+        /* whole block. This will stop the 'while' loop above. On a  */ \
         /* tile, we need to increment to the next contiguous patch   */ \
         /* of memory to continue parsing this tile. */                  \
         tpo_i_increment += ( IN==tpo_iblock                             \
@@ -301,8 +301,8 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
                                                         NULL) );        \
                                                                         \
         /* Similarly, increment the other array if necessary. Like   */ \
-        /* the above, when `OTHER' is a full block, we'll just use   */ \
-        /* the same increment as `IN'. Otherwise, when `OTHER' is a  */ \
+        /* the above, when 'OTHER' is a full block, we'll just use   */ \
+        /* the same increment as 'IN'. Otherwise, when 'OTHER' is a  */ \
         /* tile, calculate its increment based on its own block.     */ \
         if(tpo_parse_other)                                             \
           {                                                             \
@@ -315,7 +315,7 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
           }                                                             \
       }                                                                 \
                                                                         \
-    /* This is done in case the caller doesn't need `o' to avoid */     \
+    /* This is done in case the caller doesn't need 'o' to avoid */     \
     /* compiler warnings. */                                            \
     o = o ? o+0 : NULL;                                                 \
   }
@@ -355,7 +355,7 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
       GAL_TILE_PO_OISET(double,   OT,IN,OTHER,PARSE_OTHER,CHECK_BLANK,OP); \
       break;                                                            \
     default:                                                            \
-      { /* `error' function might not be available for the user. */     \
+      { /* 'error' function might not be available for the user. */     \
         fprintf(stderr, "GAL_TILE_PO_OSET: type code %d not recognized",\
                 tpo_iblock->type);                                      \
         exit(EXIT_FAILURE);                                             \
@@ -365,7 +365,7 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
 
 
 /* Parse over a region of memory (can be an n-dimensional tile or a fully
-   allocated block of memory) and do a certain operation. If `OTHER' is not
+   allocated block of memory) and do a certain operation. If 'OTHER' is not
    NULL, this macro will also parse it at the same time . Note that OTHER
    must either have only one element (for the whole input) or have exactly
    the same number of elements as the input (one value for one
@@ -412,13 +412,13 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl);
         default:                                                        \
           {                                                             \
             fprintf(stderr, "type code %d not recognized in "           \
-                    "`GAL_TILE_PARSE_OPERATE'", tpo_oblock->type);      \
+                    "'GAL_TILE_PARSE_OPERATE'", tpo_oblock->type);      \
             exit(EXIT_FAILURE);                                         \
           }                                                             \
         }                                                               \
     else                                                                \
-      /* When `OTHER==NULL', its type is irrelevant, we'll just use */  \
-      /*`int' as a place holder. */                                     \
+      /* When 'OTHER==NULL', its type is irrelevant, we'll just use */  \
+      /*'int' as a place holder. */                                     \
       GAL_TILE_PO_OSET(int,         IN,OTHER,PARSE_OTHER,CHECK_BLANK,OP); \
   }
 
diff --git a/lib/gnuastro/txt.h b/lib/gnuastro/txt.h
index 6e7f38b..2a3a715 100644
--- a/lib/gnuastro/txt.h
+++ b/lib/gnuastro/txt.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -27,6 +27,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    must be included before the C++ preparations below */
 
 #include <gnuastro/list.h>
+#include <gnuastro/fits.h>
 
 
 
@@ -78,6 +79,9 @@ enum gal_txt_line_status_enums
 int
 gal_txt_line_stat(char *line);
 
+char *
+gal_txt_trim_space(char *str);
+
 gal_data_t *
 gal_txt_table_info(char *filename, gal_list_str_t *lines, size_t *numcols,
                    size_t *numrows);
@@ -99,7 +103,8 @@ gal_list_str_t *
 gal_txt_stdin_read(long timeout_microsec);
 
 void
-gal_txt_write(gal_data_t *input, gal_list_str_t *comment, char *filename,
+gal_txt_write(gal_data_t *input, struct gal_fits_list_key_t **keylist,
+              gal_list_str_t *comment, char *filename,
               uint8_t colinfoinstdout);
 
 
diff --git a/lib/gnuastro/type.h b/lib/gnuastro/type.h
index e43661b..2090023 100644
--- a/lib/gnuastro/type.h
+++ b/lib/gnuastro/type.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -28,12 +28,12 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #include <gsl/gsl_complex.h>
 
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   `GAL_CONFIG_SIZEOF_SIZE_T' that we need below) is kept in
-   `config.h'. When building a user's programs, this information is kept in
-   `gnuastro/config.h'. Note that all `.c' files in Gnuastro's source must
-   start with the inclusion of `config.h' and that `gnuastro/config.h' is
+   'GAL_CONFIG_SIZEOF_SIZE_T' that we need below) is kept in
+   'config.h'. When building a user's programs, this information is kept in
+   'gnuastro/config.h'. Note that all '.c' files in Gnuastro's source must
+   start with the inclusion of 'config.h' and that 'gnuastro/config.h' is
    only created at installation time (not present during the building of
    Gnuastro). */
 #ifndef IN_GNUASTRO_BUILD
@@ -90,13 +90,13 @@ enum gal_types
 
 
 
-/* Define system specific types. For example `size_t' is 4 and 8 bytes on
+/* Define system specific types. For example 'size_t' is 4 and 8 bytes on
    32 and 64 bit systems respectively. In both cases, the standard defines
-   `size_t' to be unsigned. A similar case exists for `long', but it is
-   signed. During `./configure' the sizeof `size_t' and `long' were found
+   'size_t' to be unsigned. A similar case exists for 'long', but it is
+   signed. During './configure' the sizeof 'size_t' and 'long' were found
    and are used to define an alias for these system specific types.
 
-   Note: we are not using `else'. This is done because by any chance, if
+   Note: we are not using 'else'. This is done because by any chance, if
    the length of these types is not what is expected (4 or 8), then the
    aliases are not defined and the compiler will crash. */
 #if GAL_CONFIG_SIZEOF_SIZE_T == 4
diff --git a/lib/gnuastro-internal/arithmetic-internal.h b/lib/gnuastro/units.h
similarity index 59%
copy from lib/gnuastro-internal/arithmetic-internal.h
copy to lib/gnuastro/units.h
index f9d2bdd..0543e67 100644
--- a/lib/gnuastro-internal/arithmetic-internal.h
+++ b/lib/gnuastro/units.h
@@ -1,11 +1,12 @@
 /*********************************************************************
-Arithmetic -- Preform arithmetic operations on datasets.
+Units -- Convert data from one unit to other.
 This is part of GNU Astronomy Utilities (Gnuastro) package.
 
 Original author:
-     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+     Kartik Ohri <kartikohri13@gmail.com>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Copyright (C) 2020-2021, 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,22 +21,17 @@ 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_ARITHMETIC_INTERNAL_H__
-#define __GAL_ARITHMETIC_INTERNAL_H__
-
-/* Include other headers if necessary here. Note that other header files
-   must be included before the C++ preparations below */
-#include <gnuastro/data.h>
+#ifndef __GAL_UNITS_H__
+#define __GAL_UNITS_H__
 
-
-/* When we are within Gnuastro's building process, `IN_GNUASTRO_BUILD' is
+/* When we are within Gnuastro's building process, 'IN_GNUASTRO_BUILD' is
    defined. In the build process, installation information (in particular
-   `GAL_CONFIG_ARITH_CHAR' and the rest of the types that we needed in the
-   arithmetic function) is kept in `config.h'. When building a user's
-   programs, this information is kept in `gnuastro/config.h'. Note that all
-   `.c' files must start with the inclusion of `config.h' and that
-   `gnuastro/config.h' is only created at installation time (not present
-   during the building of Gnuastro).*/
+   'GAL_CONFIG_SIZEOF_SIZE_T' that we need below) is kept in
+   'config.h'. When building a user's programs, this information is kept in
+   'gnuastro/config.h'. Note that all '.c' files in Gnuastro's source must
+   start with the inclusion of 'config.h' and that 'gnuastro/config.h' is
+   only created at installation time (not present during the building of
+   Gnuastro). */
 #ifndef IN_GNUASTRO_BUILD
 #include <gnuastro/config.h>
 #endif
@@ -58,17 +54,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 */
 
+
+
+
 int
-gal_arithmetic_binary_checkblank(gal_data_t *l, gal_data_t *r);
+gal_units_extract_decimal(char *convert, const char *delimiter,
+                          double *args, size_t n);
 
-char *
-gal_arithmetic_operator_string(int operator);
+double
+gal_units_ra_to_degree (char *convert);
 
-gal_data_t *
-gal_arithmetic_convert_to_compiled_type(gal_data_t *in, int flags);
+double
+gal_units_dec_to_degree (char *convert);
 
+char *
+gal_units_degree_to_ra (double decimal, int usecolon);
 
+char *
+gal_units_degree_to_dec (double decimal, int usecolon);
 
 __END_C_DECLS    /* From C++ preparations */
 
-#endif           /* __GAL_ARITHMETIC_INTERNAL_H__ */
+#endif           /* __GAL_UNITS_H__ */
diff --git a/lib/gnuastro/wcs.h b/lib/gnuastro/wcs.h
index 7395dfd..8ad29ba 100644
--- a/lib/gnuastro/wcs.h
+++ b/lib/gnuastro/wcs.h
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -29,7 +29,10 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <wcslib/wcs.h>
 
 #include <gnuastro/data.h>
+#include <gnuastro/fits.h>
 
+/* Assumed floating point error in the WCS-related functionality. */
+#define GAL_WCS_FLTERROR 1e-12
 
 /* C++ Preparations */
 #undef __BEGIN_C_DECLS
@@ -53,6 +56,25 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 /*************************************************************
+ **************           Constants            ***************
+ *************************************************************/
+/* Macros to identify the type of distortion for conversions. */
+enum gal_wcs_distortions
+{
+  GAL_WCS_DISTORTION_INVALID,         /* Invalid (=0 by C standard).    */
+
+  GAL_WCS_DISTORTION_TPD,             /* The TPD polynomial distortion. */
+  GAL_WCS_DISTORTION_SIP,             /* The SIP polynomial distortion. */
+  GAL_WCS_DISTORTION_TPV,             /* The TPV polynomial distortion. */
+  GAL_WCS_DISTORTION_DSS,             /* The DSS polynomial distortion. */
+  GAL_WCS_DISTORTION_WAT,             /* The WAT polynomial distortion. */
+};
+
+
+
+
+
+/*************************************************************
  ***********               Read WCS                ***********
  *************************************************************/
 struct wcsprm *
@@ -63,6 +85,45 @@ struct wcsprm *
 gal_wcs_read(char *filename, char *hdu, size_t hstartwcs,
              size_t hendwcs, int *nwcs);
 
+struct wcsprm *
+gal_wcs_create(double *crpix, double *crval, double *cdelt,
+               double *pc, char **cunit, char **ctype, size_t ndim);
+
+char *
+gal_wcs_dimension_name(struct wcsprm *wcs, size_t dimension);
+
+
+
+/*************************************************************
+ ***********               Write WCS               ***********
+ *************************************************************/
+void
+gal_wcs_write(struct wcsprm *wcs, char *filename,
+              char *extname, gal_fits_list_key_t *headers,
+              char *program_string);
+
+void
+gal_wcs_write_in_fitsptr(fitsfile *fptr, struct wcsprm *wcs);
+
+
+
+
+/*************************************************************
+ ***********              Distortions              ***********
+ *************************************************************/
+int
+gal_wcs_distortion_from_string(char *distortion);
+
+char *
+gal_wcs_distortion_to_string(int distortion);
+
+int
+gal_wcs_distortion_identify(struct wcsprm *wcs);
+
+struct wcsprm *
+gal_wcs_distortion_convert(struct wcsprm *inwcs,
+                           int out_distortion, size_t *fitsize);
+
 
 
 
@@ -83,6 +144,9 @@ double *
 gal_wcs_warp_matrix(struct wcsprm *wcs);
 
 void
+gal_wcs_clean_errors(struct wcsprm *wcs);
+
+void
 gal_wcs_decompose_pc_cdelt(struct wcsprm *wcs);
 
 double
@@ -94,7 +158,10 @@ gal_wcs_pixel_scale(struct wcsprm *wcs);
 double
 gal_wcs_pixel_area_arcsec2(struct wcsprm *wcs);
 
-
+int
+gal_wcs_coverage(char *filename, char *hdu, size_t *ndim,
+                 double **center, double **width, double **min,
+                 double **max);
 
 
 
diff --git a/lib/interpolate.c b/lib/interpolate.c
index ffeacb3..05cb1e8 100644
--- a/lib/interpolate.c
+++ b/lib/interpolate.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -48,17 +48,18 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /***************         (Dimension agnostic)         ****************/
 /*********************************************************************/
 /* These are bit-flags, so we're using hexadecimal notation. */
-#define INTERPOLATE_FLAGS_NO       0
-#define INTERPOLATE_FLAGS_CHECKED  0x1
-#define INTERPOLATE_FLAGS_BLANK    0x2
+#define INTERPOLATE_FLAGS_NO          0
+#define INTERPOLATE_FLAGS_NGB_CHECKED 0x1
+#define INTERPOLATE_FLAGS_BLANK       0x2
 
 
 
 
 
 /* Parameters for interpolation on threads. */
-struct interpolate_params
+struct interpolate_ngb_params
 {
+  int                         function;
   gal_data_t                    *input;
   size_t                           num;
   gal_data_t                      *out;
@@ -78,11 +79,14 @@ struct interpolate_params
 
 /* Run the interpolation on many threads. */
 static void *
-interpolate_close_neighbors_on_thread(void *in_prm)
+interpolate_neighbors_on_thread(void *in_prm)
 {
-  /* Variables that others depend on. */
+  /* Low-level variables that others depend on. */
   struct gal_threads_params *tprm=(struct gal_threads_params *)in_prm;
-  struct interpolate_params *prm=(struct interpolate_params *)(tprm->params);
+  struct interpolate_ngb_params *prm=
+    (struct interpolate_ngb_params *)(tprm->params);
+
+  /* Higher-level variables. */
   struct gal_tile_two_layer_params *tl=prm->tl;
   int correct_index=(tl && tl->totchannels>1 && !tl->workoverch);
   gal_data_t *input=prm->input;
@@ -92,23 +96,31 @@ interpolate_close_neighbors_on_thread(void *in_prm)
   float dist, pdist;
   uint8_t *b, *bf, *bb;
   gal_list_void_t *tvll;
+  size_t ngb_counter, pind;
   gal_list_dosizet_t *lQ, *sQ;
-  size_t ngb_counter, pind, *dinc;
   size_t i, index, fullind, chstart=0, ndim=input->ndim;
-  gal_data_t *median, *tin, *tout, *tnear, *nearest=NULL;
+  gal_data_t *tin, *tout, *tnear, *value=NULL, *nearest=NULL;
   size_t size = (correct_index ? tl->tottilesinch : input->size);
   size_t *dsize = (correct_index ? tl->numtilesinch : input->dsize);
   size_t *icoord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
                                       "icoord");
   size_t *ncoord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
                                       "ncoord");
-  uint8_t *fullflag=&prm->thread_flags[tprm->id*input->size], *flag=fullflag;
+  uint8_t *flag, *fullflag=&prm->thread_flags[tprm->id*input->size];
+
+  /* Based on the above. */
+  size_t *dinc=gal_dimension_increment(ndim, dsize);
 
 
-  /* Initializations. */
+  /* Initialize the flags array. We need two flags during this processing:
+     1) to see if there are blanks. 2) to see if a neighbor has been
+     checked. These are both binary (0 or 1). So to avoid wasting space, we
+     will use bits to store them. We start with only setting the blank flag
+     once for the whole thread. Then for each interpolated pixel, we reset
+     the neighbor-check flag. */
+  flag=fullflag;
   bb=prm->blanks->array;
   bf=(b=fullflag)+input->size;
-  dinc=gal_dimension_increment(ndim, dsize);
   do *b = *bb++ ? INTERPOLATE_FLAGS_BLANK : 0; while(++b<bf);
 
 
@@ -119,8 +131,9 @@ interpolate_close_neighbors_on_thread(void *in_prm)
     {
       nv=gal_pointer_increment(tvll->v, tprm->id*prm->numneighbors,
                                input->type);
-      gal_list_data_add_alloc(&nearest, nv, tin->type, 1, &prm->numneighbors,
-                              NULL, 0, -1, 1, NULL, NULL, NULL);
+      gal_list_data_add_alloc(&nearest, nv, tin->type, 1,
+                              &prm->numneighbors, NULL, 0, -1, 1,
+                              NULL, NULL, NULL);
       tin=tin->next;
     }
   gal_list_data_reverse(&nearest);
@@ -155,8 +168,8 @@ interpolate_close_neighbors_on_thread(void *in_prm)
          tiled dataset, the caller might want to interpolate the values of
          each channel separately (not mix values from different
          channels). In such a case, the tiles of each channel (and their
-         values in `input' are contiguous. So we need to correct
-         `tprm->indexs[i]' (which is the index over the whole tessellation,
+         values in 'input' are contiguous. So we need to correct
+         'tprm->indexs[i]' (which is the index over the whole tessellation,
          including all channels). */
       if(correct_index)
         {
@@ -179,7 +192,7 @@ interpolate_close_neighbors_on_thread(void *in_prm)
       /* Reset all checked bits in the flags array to 0. */
       ngb_counter=0;
       bf=(b=flag)+size;
-      do *b &= ~(INTERPOLATE_FLAGS_CHECKED); while(++b<bf);
+      do *b &= ~(INTERPOLATE_FLAGS_NGB_CHECKED); while(++b<bf);
 
 
       /* Get the coordinates of this pixel (to be interpolated). */
@@ -229,7 +242,7 @@ interpolate_close_neighbors_on_thread(void *in_prm)
                 IMPORTANT: we must not check for blank values here,
                 otherwise we won't be able to parse over extended blank
                 regions. */
-             if( !(flag[nind] & INTERPOLATE_FLAGS_CHECKED) )
+             if( !(flag[nind] & INTERPOLATE_FLAGS_NGB_CHECKED) )
                {
                  /* Get the coordinates of this neighbor. */
                  gal_dimension_index_to_coord(nind, ndim, dsize, ncoord);
@@ -241,33 +254,49 @@ interpolate_close_neighbors_on_thread(void *in_prm)
                  gal_list_dosizet_add(&lQ, &sQ, nind, dist);
 
                  /* Flag this neighbor as checked. */
-                 flag[nind] |= INTERPOLATE_FLAGS_CHECKED;
+                 flag[nind] |= INTERPOLATE_FLAGS_NGB_CHECKED;
                }
            } );
 
           /* If there are no more meshes to add to the queue, then this
              shows, there were not enough points for
              interpolation. Normally, this loop should only be exited
-             through the `currentnum>=numnearest' check above. */
+             through the 'currentnum>=numnearest' check above. */
           if(sQ==NULL)
             error(EXIT_FAILURE, 0, "%s: only %zu neighbors found while "
                   "you had asked to use %zu neighbors for close neighbor "
-                  "interpolation", __func__, ngb_counter, prm->numneighbors);
+                  "interpolation", __func__, ngb_counter,
+                  prm->numneighbors);
         }
 
-      /* Calculate the median of the values and write it in the output. */
+      /* Calculate the desired statistic, and write it in the output. */
       tout=prm->out;
       for(tnear=nearest; tnear!=NULL; tnear=tnear->next)
         {
-          /* Find the median and copy it, but first, reset the flags (which
-             remain from the last time). */
+          /* Find the desired statistic and copy it, but first, reset the
+             flags (which remain from the last time). */
           tnear->flag &= ~(GAL_DATA_FLAG_SORT_CH | GAL_DATA_FLAG_BLANK_CH);
-          median=gal_statistics_median(tnear, 1);
+          switch(prm->function)
+            {
+            case GAL_INTERPOLATE_NEIGHBORS_FUNC_MIN:
+              value=gal_statistics_minimum(tnear); break;
+              break;
+            case GAL_INTERPOLATE_NEIGHBORS_FUNC_MAX:
+              value=gal_statistics_maximum(tnear); break;
+              break;
+            case GAL_INTERPOLATE_NEIGHBORS_FUNC_MEDIAN:
+              value=gal_statistics_median(tnear, 1); break;
+            default:
+              error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s "
+                    "to fix the problem. The value %d is not a recognized "
+                    "interpolation function identifier", __func__,
+                    PACKAGE_BUGREPORT, prm->function);
+            }
           memcpy(gal_pointer_increment(tout->array, fullind, tout->type),
-                 median->array, gal_type_sizeof(tout->type));
+                 value->array, gal_type_sizeof(tout->type));
 
           /* Clean up and go to next array. */
-          gal_data_free(median);
+          gal_data_free(value);
           tout=tout->next;
         }
     }
@@ -324,27 +353,27 @@ interpolate_copy_input(gal_data_t *input, int 
aslinkedlist)
 
 
 
-/* Interpolate blank values in an array. If the `tl!=NULL', then it is
+/* Interpolate blank values in an array. If the 'tl!=NULL', then it is
    assumed that the tile values correspond to given tessellation. Such that
-   `input[i]' corresponds to `tiles[i]' in the tessellation. */
+   'input[i]' corresponds to 'tiles[i]' in the tessellation. */
 gal_data_t *
-gal_interpolate_close_neighbors(gal_data_t *input,
-                                struct gal_tile_two_layer_params *tl,
-                                uint8_t metric, size_t numneighbors,
-                                size_t numthreads, int onlyblank,
-                                int aslinkedlist)
+gal_interpolate_neighbors(gal_data_t *input,
+                          struct gal_tile_two_layer_params *tl,
+                          uint8_t metric, size_t numneighbors,
+                          size_t numthreads, int onlyblank,
+                          int aslinkedlist, int function)
 {
   gal_data_t *tin, *tout;
-  struct interpolate_params prm;
+  struct interpolate_ngb_params prm;
   size_t ngbvnum=numthreads*numneighbors;
   int permute=(tl && tl->totchannels>1 && tl->workoverch);
 
 
   /* If there are no blank values in the array, AND we should only fill
      blank values, then simply copy the input and abort. */
-  if( (input->flag | GAL_DATA_FLAG_BLANK_CH)     /* Zero bit is meaningful.*/
-      && !(input->flag | GAL_DATA_FLAG_HASBLANK) /* There are no blanks.   */
-      && onlyblank )                             /* Only interpolate blank.*/
+  if( (input->flag | GAL_DATA_FLAG_BLANK_CH)   /* Zero bit is meaningful.*/
+      && !(input->flag | GAL_DATA_FLAG_HASBLANK)/* There are no blanks.  */
+      && onlyblank )                           /* Only interpolate blank.*/
     return interpolate_copy_input(input, aslinkedlist);
 
 
@@ -352,6 +381,7 @@ gal_interpolate_close_neighbors(gal_data_t *input,
   prm.tl           = tl;
   prm.ngb_vals     = NULL;
   prm.input        = input;
+  prm.function     = function;
   prm.onlyblank    = onlyblank;
   prm.numneighbors = numneighbors;
   prm.num          = aslinkedlist ? gal_list_data_number(input) : 1;
@@ -360,10 +390,10 @@ gal_interpolate_close_neighbors(gal_data_t *input,
   /* Set the metric. */
   switch(metric)
     {
-    case GAL_INTERPOLATE_CLOSE_METRIC_RADIAL:
+    case GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL:
       prm.metric=gal_dimension_dist_radial;
       break;
-    case GAL_INTERPOLATE_CLOSE_METRIC_MANHATTAN:
+    case GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN:
       prm.metric=gal_dimension_dist_manhattan;
       break;
     default:
@@ -405,8 +435,8 @@ gal_interpolate_close_neighbors(gal_data_t *input,
 
   /* If we are given a list of datasets, make the necessary
      allocations. The reason we are doing this after a check of
-     `aslinkedlist' is that the `input' might have a `next' element, but
-     the caller might not have called `aslinkedlist'. */
+     'aslinkedlist' is that the 'input' might have a 'next' element, but
+     the caller might not have called 'aslinkedlist'. */
   prm.out->next=NULL;
   if(aslinkedlist)
     for(tin=input->next; tin!=NULL; tin=tin->next)
@@ -438,8 +468,9 @@ gal_interpolate_close_neighbors(gal_data_t *input,
 
 
   /* Spin off the threads. */
-  gal_threads_spin_off(interpolate_close_neighbors_on_thread, &prm,
-                       input->size, numthreads);
+  gal_threads_spin_off(interpolate_neighbors_on_thread, &prm,
+                       input->size, numthreads, input->minmapsize,
+                       input->quietmmap);
 
 
   /* If the values were permuted for the interpolation, then re-order the
@@ -546,12 +577,12 @@ gal_interpolate_1d_make_gsl_spline(gal_data_t *X, 
gal_data_t *Y, int type_1d)
     }
 
   /* Initializations. Note that if Y doesn't have any blank elements and is
-     already in `double' type, then we don't need to make a copy. */
+     already in 'double' type, then we don't need to make a copy. */
   Yd = ( (Yhasblank || Y->type!=GAL_TYPE_FLOAT64)
          ? gal_data_copy_to_new_type(Y, GAL_TYPE_FLOAT64)
          : Y );
   Xd = ( X
-         /* Has to be `Yhasblank', we KNOW X doesn't have blank values. */
+         /* Has to be 'Yhasblank', we KNOW X doesn't have blank values. */
          ? ( (Yhasblank || X->type!=GAL_TYPE_FLOAT64)
              ? gal_data_copy_to_new_type(X, GAL_TYPE_FLOAT64)
              : X )
@@ -751,8 +782,8 @@ gal_interpolate_1d_blank(gal_data_t *in, int type_1d)
       }
       */
 
-      /* Set the blank flags, note that `GAL_DATA_FLAG_BLANK_CH' is already set
-         by the top call to `gal_blank_present'. */
+      /* Set the blank flags, note that 'GAL_DATA_FLAG_BLANK_CH' is already set
+         by the top call to 'gal_blank_present'. */
       if(hasblank)
         in->flag |=  GAL_DATA_FLAG_HASBLANK;
       else
diff --git a/lib/jpeg.c b/lib/jpeg.c
index 9d41b07..99431b7 100644
--- a/lib/jpeg.c
+++ b/lib/jpeg.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -213,14 +213,14 @@ readjpg(char *inname, size_t *outs0, size_t *outs1, 
size_t *numcolors)
   errno=0;
   all=malloc(nc*sizeof *all);
   if(all==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `all'",
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'all'",
           __func__, nc*sizeof *all);
   for(i=0;i<nc;++i)
     {
       errno=0;
       all[i]=malloc(s0*s1*sizeof *all[i]);
       if(all[i]==NULL)
-        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `all[%zu]'",
+        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'all[%zu]'",
               __func__, s0*s1*sizeof *all[i], i);
     }
 
@@ -411,8 +411,8 @@ gal_jpeg_write(gal_data_t *in, char *filename, uint8_t 
quality,
     error(EXIT_FAILURE, 0, "%s: only 1, 3, and 4 color channels are "
           "acceptable, input is a list of %zu data sets", __func__, numch);
   if(in->type!=GAL_TYPE_UINT8)
-    error(EXIT_FAILURE, 0, "%s: input has a `%s' type, but JPEG images can "
-          "only have a `uint8' type", __func__, gal_type_name(in->type, 1));
+    error(EXIT_FAILURE, 0, "%s: input has a '%s' type, but JPEG images can "
+          "only have a 'uint8' type", __func__, gal_type_name(in->type, 1));
 
   /* Make sure the file doesn't exist and that we have write
      permission. Note that the JPEG standard doesn't have multple
diff --git a/lib/kdtree.c b/lib/kdtree.c
new file mode 100644
index 0000000..ef22593
--- /dev/null
+++ b/lib/kdtree.c
@@ -0,0 +1,594 @@
+/*********************************************************************
+kdtree -- Create k-d tree and nearest neighbour searches.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Sachin Kumar Singh <sachinkumarsingh092@gmail.com>
+Contributing author(s):
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Copyright (C) 2020-2021, 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 <errno.h>
+#include <error.h>
+#include <float.h>
+
+#include <gnuastro/data.h>
+#include <gnuastro/table.h>
+#include <gnuastro/blank.h>
+#include <gnuastro/pointer.h>
+#include <gnuastro/permutation.h>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************
+ ********                  Utilities                      *******
+ ****************************************************************/
+/* Main structure to keep kd-tree parameters. */
+struct kdtree_params
+{
+  size_t ndim;            /* Number of dimentions in the nodes. */
+  size_t *input_row;      /* The indexes of the input table. */
+  gal_data_t **coords;    /* The input coordinates array. */
+  uint32_t *left, *right; /* The indexes of the left and right nodes. */
+
+  /* The values of the left and right columns. */
+  gal_data_t *left_col, *right_col;
+};
+
+
+
+
+
+/* Swap 2 nodes of the tree. Instead of physically swaping all the values
+   we swap just the indexes of the node. */
+static void
+kdtree_node_swap(struct kdtree_params *p, size_t node1, size_t node2)
+{
+  uint32_t tmp_left=p->left[node1];
+  uint32_t tmp_right=p->right[node1];
+  size_t tmp_input_row=p->input_row[node1];
+
+  /* No need to swap same node. */
+  if(node1==node2) return;
+
+  p->left[node1]=p->left[node2];
+  p->right[node1]=p->right[node2];
+  p->input_row[node1]=p->input_row[node2];
+
+  p->left[node2]=tmp_left;
+  p->right[node2]=tmp_right;
+  p->input_row[node2]=tmp_input_row;
+}
+
+
+
+
+
+/* Return the distance between 2 given nodes. The distance is equivalent
+   to the radius of the hypersphere having node as its center.
+
+   Return: Radial distace from given point to the node.
+*/
+static double
+kdtree_distance_find(struct kdtree_params *p, size_t node,
+                     double *point)
+{
+  size_t i;
+  double *carr;
+  double t_distance, node_distance=0;
+
+  /* For all dimensions. */
+  for(i=0; i<p->ndim; ++i)
+    {
+      carr=p->coords[i]->array;
+      t_distance=carr[node]-point[i];
+
+      node_distance += t_distance*t_distance;
+    }
+
+  return node_distance;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************
+ ********           Preperations and Cleanup              *******
+ ****************************************************************/
+/* Initialise the kdtree_params structure and do sanity checks. */
+static void
+kdtree_prepare(struct kdtree_params *p, gal_data_t *coords_raw)
+{
+  size_t i;
+  gal_data_t *tmp;
+  p->ndim=gal_list_data_number(coords_raw);
+
+  /* Allocate the coordinate array. */
+  errno=0;
+  p->coords=malloc(p->ndim*sizeof(**(p->coords)));
+  if(p->coords==NULL)
+    error(EXIT_FAILURE, errno, "%s: couldn't allocate %zu bytes "
+         "for 'coords'", __func__, p->ndim*sizeof(**(p->coords)));
+
+  /* Convert input to double type. */
+  tmp=coords_raw;
+  for(i=0; i<p->ndim; ++i)
+    {
+      if(tmp->type == GAL_TYPE_FLOAT64)
+       p->coords[i]=tmp;
+      else
+        p->coords[i]=gal_data_copy_to_new_type(tmp, GAL_TYPE_FLOAT64);
+
+      /* Go to the next column list. */
+      tmp=tmp->next;
+    }
+
+  /* If the 'left_col' is already defined, then we just need to do
+     some sanity checks. */
+  if(p->left_col)
+    {
+      /* Make sure there is more than one column. */
+      if(p->left_col->next==NULL)
+        error(EXIT_FAILURE, 0, "%s: the input kd-tree should be 2 columns",
+              __func__);
+
+      /* Set the right column and check if there aren't any
+         more columns. */
+      p->right_col=p->left_col->next;
+      if(p->right_col->next)
+        error(EXIT_FAILURE, 0, "%s: the input kd-tree shoudn't be more "
+              "than 2 columns", __func__);
+
+      /* Make sure they are the same size. */
+      if(p->left_col->size!=p->right_col->size)
+        error(EXIT_FAILURE, 0, "%s: left and right columns should have "
+              "same size", __func__);
+
+      /* Make sure left is 'uint32_t'. */
+      if(p->left_col->type!=GAL_TYPE_UINT32)
+        error(EXIT_FAILURE, 0, "%s: left kd-tree column should be uint32_t",
+              __func__);
+
+      /* Make sure right is 'uint32_t'. */
+      if(p->right_col->type!=GAL_TYPE_UINT32)
+        error(EXIT_FAILURE, 0, "%s: right kd-tree column should be uint32_t",
+              __func__);
+
+      /* Initailise left and right arrays. */
+      p->left=p->left_col->array;
+      p->right=p->right_col->array;
+    }
+  else
+    {
+      /* Allocate and initialise the kd-tree input_row. */
+      p->input_row=gal_pointer_allocate(GAL_TYPE_SIZE_T, coords_raw->size, 0,
+                                        __func__, "p->input_row");
+      for(i=0; i<coords_raw->size; ++i)        p->input_row[i]=i;
+
+      /* Allocate output and initialize them. */
+      p->left_col=gal_data_alloc(NULL, GAL_TYPE_UINT32, 1,
+                                 coords_raw->dsize, NULL, 0,
+                                 coords_raw->minmapsize,
+                                 coords_raw->quietmmap, "left",
+                                 "index",
+                                 "index of left subtree in the kd-tree");
+      p->right_col=gal_data_alloc(NULL, GAL_TYPE_UINT32, 1,
+                                  coords_raw->dsize, NULL, 0,
+                                  coords_raw->minmapsize,
+                                  coords_raw->quietmmap, "right",
+                                  "index",
+                                  "index of right subtree in the kd-tree");
+
+      /* Fill the elements of the params structure. */
+      p->left_col->next=p->right_col;
+
+      /* Initialise the left and right arrays. */
+      p->left=p->left_col->array;
+      p->right=p->right_col->array;
+      for(i=0;i<coords_raw->size;++i)
+        { p->left[i]=p->right[i]=GAL_BLANK_UINT32; }
+    }
+}
+
+
+
+
+
+/* Cleanup the data and free the memory used by the structure after use. */
+static void
+kdtree_cleanup(struct kdtree_params *p, gal_data_t *coords_raw)
+{
+  size_t i;
+  gal_data_t *tmp;
+
+  /* Clean up. */
+  tmp = coords_raw;
+  for(i = 0; i<p->ndim; ++i)
+    {
+      if(p->coords[i]!=tmp) gal_data_free(p->coords[i]);
+      tmp=tmp->next;
+    }
+
+  /* Free memory. */
+  free(p->coords);
+  free(p->input_row);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************
+ ********                Create KD-Tree                   *******
+ ****************************************************************/
+/* Divide the array into two parts, values more than that of k'th node
+   and values less than k'th node.
+
+   Return: Index of the node whose value is greater than all
+           the nodes before it.
+*/
+static size_t
+kdtree_make_partition(struct kdtree_params *p, size_t node_left,
+                      size_t node_right, size_t node_k,
+                      double *coordinate)
+{
+  /* store_index is the index before which all values are smaller than
+     the value of k'th node. */
+  size_t i, store_index;
+  double k_node_value = coordinate[p->input_row[node_k]];
+
+  /* Move the k'th node to the right. */
+  kdtree_node_swap(p, node_k, node_right);
+
+  /* Move all nodes smaller than k'th node to its left and check
+     the number of elements smaller than the value present at the
+     k'th index. */
+  store_index = node_left;
+  for(i = node_left; i < node_right; ++i)
+    if(coordinate[p->input_row[i]] < k_node_value)
+      {
+        /* Move i'th node to the left side of the k'th index. */
+        kdtree_node_swap(p, store_index, i);
+
+        /* Prepare the place of next smaller node. */
+        store_index++;
+      }
+
+  /* Place k'th node after all the nodes that have lesser value
+     than it, as it was moved to the right initially. */
+  kdtree_node_swap(p, node_right, store_index);
+
+  /* Return the store_index. */
+  return store_index;
+}
+
+
+
+
+
+/* Find the median node of the current axis. Instead of randomly
+   choosing the median node, we use `quickselect alogorithm` to
+   find median node in linear time between the left and right node.
+   This also makes the values in the current axis partially sorted.
+
+   See `https://en.wikipedia.org/wiki/Quickselect`
+   for pseudocode and more details of the algorithm.
+
+   Return: Median node between the given left and right nodes.
+*/
+static size_t
+kdtree_median_find(struct kdtree_params *p, size_t node_left,
+                   size_t node_right, double *coordinate)
+{
+  size_t node_pivot, node_median;
+
+  /* False state, this is a programming error. */
+  if(node_right < node_left)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us to fix "
+          "the problem! For some reason, the node_right (%zu) is "
+          "smaller than node_left (%zu)", __func__, node_right,
+          node_left);
+
+  /* If the two nodes are the same, just return the node. */
+  if(node_right == node_left)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us to fix "
+          "the problem! For some reason, the node_right (%zu) is "
+          "equal to node_left (%zu)", __func__, node_right, node_left);
+
+  /* The required median node between left and right node. */
+  node_median = node_left+(node_right-node_left)/2;
+
+  /* Loop until the median of the current axis is returned. */
+  while(1)
+    {
+      /* Pivot node acts as a reference for the distance from the desired
+        (here median) node. */
+      node_pivot = kdtree_make_partition(p, node_left, node_right,
+                                         node_median, coordinate);
+      /* If median is found, break the loop and return median node. */
+      if(node_median == node_pivot) break;
+
+      /* Change the left or right node based on the position of
+         the pivot node with respect to the required median node. */
+      if(node_median < node_pivot)  node_right = node_pivot - 1;
+      else                          node_left  = node_pivot + 1;
+    }
+  /* Return the median node. */
+  return node_median;
+}
+
+
+
+
+
+/* Make a kd-tree from a given set of points. For tree construction, a
+   median point is selected for each axis and the left and right branches
+   are recursively created by comparing points in that axis.
+
+   Return : Indexes of the nodes in the kd-tree.
+*/
+static uint32_t
+kdtree_fill_subtrees(struct kdtree_params *p, size_t node_left,
+                     size_t node_right, size_t depth)
+{
+  /* Set the working axis. */
+  size_t axis=depth % p->ndim;
+
+  /* node_median is a counter over the `input_row` array.
+     `input_row` array has the input_row(row number). */
+  size_t node_median;
+
+  /* Recursion terminates when the left and right nodes are the
+     same. */
+  if(node_left==node_right) return p->input_row[node_left];
+
+  /* Find the median node. */
+  node_median = kdtree_median_find(p, node_left, node_right,
+                                   p->coords[axis]->array);
+
+  /* node_median == 0 : We are in the lowest node (leaf) so no need
+     When we only have 2 nodes and the median is equal to the left,
+     its the end of the subtree.
+  */
+  if(node_median)
+    p->left[node_median] = ( (node_median == node_left)
+                             ? GAL_BLANK_UINT32
+                             : kdtree_fill_subtrees(p, node_left,
+                                                    node_median-1,
+                                                    depth+1) );
+
+  /* Right and left nodes are non-symytrical. Node left can be equal
+     to node median when there are only 2 points and at this point,
+     there can never be a single point (node left == node right).
+     But node right can never be equal to node median.
+     So we don't check for it.*/
+  p->right[node_median] = kdtree_fill_subtrees(p, node_median+1,
+                                               node_right,
+                                               depth+1);
+
+  /* All subtrees have been parsed, return the node. */
+  return p->input_row[node_median];
+}
+
+
+
+
+
+/* High level function to construct the kd-tree. This function initilises
+   and creates the tree in top-down manner. Returns a list containing the
+   indexes of left and right subtrees. */
+gal_data_t *
+gal_kdtree_create(gal_data_t *coords_raw, size_t *root)
+{
+  struct kdtree_params p={0};
+
+  /* Initialise the params structure. */
+  kdtree_prepare(&p, coords_raw);
+
+  /* Fill the kd-tree*/
+  *root=kdtree_fill_subtrees(&p, 0, coords_raw->size-1, 0);
+
+  /* For a check
+  size_t i;
+  for(i=0;i<coords_raw->size;++i)
+    printf("%-15zu%-15u%-15u\n", p.input_row[i], p.left[i], p.right[i]);
+  */
+
+  /* Do a reverse permutation to sort the indexes
+     back in the input order. */
+  gal_permutation_apply_inverse(p.left_col, p.input_row);
+  gal_permutation_apply_inverse(p.right_col, p.input_row);
+
+  /* Free and clean up */
+  kdtree_cleanup(&p, coords_raw);
+
+  /* Return results. */
+  return p.left_col;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************
+ ********          Nearest-Neighbour Search               *******
+ ****************************************************************/
+/* This is a helper function which finds the nearest neighbour of
+   the given point in a kdtree. It calculates the least distance
+   from the point, and the index of that nearest node (out_nn).
+
+   See `https://en.wikipedia.org/wiki/K-d_tree#Nearest_neighbour_search`
+   for more information.
+*/
+static void
+kdtree_nearest_neighbour(struct kdtree_params *p, uint32_t node_current,
+                         double *point, double *least_dist,
+                         size_t *out_nn, size_t depth)
+{
+  double d, dx, dx2;
+  size_t axis=depth % p->ndim;    /* Set the working axis. */
+  double *coordinates=p->coords[axis]->array;
+
+  /* If no subtree present, don't search further. */
+  if(node_current==GAL_BLANK_UINT32) return;
+
+  /* The distance between search point to the current node.*/
+  d = kdtree_distance_find(p, node_current, point);
+
+  /* Distance between the splitting coordinate of the search
+     point and current node. */
+  dx = coordinates[node_current]-point[axis];
+
+  /* Check if the current node is nearer than the previous
+     nearest node. */
+  if(d < *least_dist)
+    {
+      *least_dist = d;
+      *out_nn = node_current;
+    }
+
+  /* If exact match found (least distance 0), return it. */
+  if(*least_dist==0.0f) return;
+
+  /* Recursively search in subtrees. */
+  kdtree_nearest_neighbour(p, dx > 0
+                              ? p->left[node_current]
+                              : p->right[node_current],
+                           point, least_dist, out_nn, depth+1);
+
+  /* Since the hyperplanes are all axis-aligned, to check if there is a
+     node in other branch which is nearer to the current node is done by a
+     simple comparison to see whether the distance between the splitting
+     coordinate (median node) of the search point and current node is
+     lesser (i.e on same side of hyperplane) than the distance (overall
+     coordinates) from the search point to the current nearest. */
+  dx2 = dx*dx;
+  if(dx2 >= *least_dist) return;
+
+  /* Recursively search other subtrees. */
+  kdtree_nearest_neighbour(p, dx > 0
+                              ? p->right[node_current]
+                              : p->left[node_current],
+                           point, least_dist, out_nn, depth+1);
+}
+
+
+
+
+
+/* High-level function used to find the nearest neighbour of a given
+   point in a kd-tree. It calculates the least distance of the point
+   from the nearest node and returns the index of that node.
+
+   Return: The index of the nearest neighbour node in the kd-tree.
+*/
+size_t
+gal_kdtree_nearest_neighbour(gal_data_t *coords_raw, gal_data_t *kdtree,
+                             size_t root, double *point,
+                             double *least_dist)
+{
+  struct kdtree_params p={0};
+  size_t out_nn=GAL_BLANK_SIZE_T;
+
+  /* Initialisation. */
+  p.left_col=kdtree;
+  *least_dist=DBL_MAX;
+  kdtree_prepare(&p, coords_raw);
+
+  /* Use the low-level function to find th nearest neighbour. */
+  kdtree_nearest_neighbour(&p, root, point, least_dist, &out_nn, 0);
+
+  /* least_dist is the square of the distance between the nearest
+     neighbour and the point (used to improve processing).
+     Square root of that is the actual distance. */
+  *least_dist = sqrt(*least_dist);
+
+  /* For a check
+  printf("%s: root=%zu, out_nn=%zu, least_dis=%f\n",
+         __func__, root, out_nn, least_dist);
+  */
+
+  /* Clean up and return. */
+  kdtree_cleanup(&p, coords_raw);
+  return out_nn;
+}
diff --git a/lib/label.c b/lib/label.c
index 895811d..cf9a224 100644
--- a/lib/label.c
+++ b/lib/label.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -51,10 +51,10 @@ label_check_type(gal_data_t *in, uint8_t needed_type, char 
*variable,
                  const char *func)
 {
   if(in->type!=needed_type)
-    error(EXIT_FAILURE, 0, "%s: the `%s' dataset has `%s' type, but it "
-          "must have a `%s' type.\n\n"
-          "You can use `gal_data_copy_to_new_type' or "
-          "`gal_data_copy_to_new_type_free' to convert your input dataset "
+    error(EXIT_FAILURE, 0, "%s: the '%s' dataset has '%s' type, but it "
+          "must have a '%s' type.\n\n"
+          "You can use 'gal_data_copy_to_new_type' or "
+          "'gal_data_copy_to_new_type_free' to convert your input dataset "
           "to this type before calling this function", func, variable,
           gal_type_name(in->type, 1), gal_type_name(needed_type, 1));
 }
@@ -81,7 +81,7 @@ label_check_type(gal_data_t *in, uint8_t needed_type, char 
*variable,
 /****************************************************************
  *****************          Indexs           ********************
  ****************************************************************/
-/* Put the indexs of each labeled region into an array of `gal_data_t's
+/* Put the indexs of each labeled region into an array of 'gal_data_t's
    (where each element is a dataset containing the respective label's
    indexs). */
 gal_data_t *
@@ -174,7 +174,7 @@ gal_label_indexs(gal_data_t *labels, size_t numlabs, size_t 
minmapsize,
    values. If a certain pixel (at a certain level) has no neighbors, it is
    a local maximum and will be assigned a new label. If it has a labeled
    neighbor, it will take that label and if there is more than one
-   neighboring labeled region that pixel will be a `river` pixel.
+   neighboring labeled region that pixel will be a 'river' pixel.
 
    DON'T FORGET: SET THE FLAGS FOR CONV EQUAL TO INPUT IN SEGMENT.
 
@@ -197,10 +197,10 @@ gal_label_watershed(gal_data_t *values, gal_data_t 
*indexs,
   label_check_type(indexs, GAL_TYPE_SIZE_T,  "indexs", __func__);
   label_check_type(labels, GAL_TYPE_INT32,   "labels", __func__);
   if( gal_dimension_is_different(values, labels) )
-    error(EXIT_FAILURE, 0, "%s: the `values' and `labels' arguments must "
+    error(EXIT_FAILURE, 0, "%s: the 'values' and 'labels' arguments must "
           "have the same size", __func__);
   if(indexs->ndim!=1)
-    error(EXIT_FAILURE, 0, "%s: `indexs' has to be a 1D array, but it is "
+    error(EXIT_FAILURE, 0, "%s: 'indexs' has to be a 1D array, but it is "
           "%zuD", __func__, indexs->ndim);
 
 
@@ -239,8 +239,8 @@ gal_label_watershed(gal_data_t *values, gal_data_t *indexs,
 
 
   /* If the indexs aren't already sorted (by the value they correspond to),
-     sort them given indexs based on their flux (`gal_qsort_index_arr' is
-     defined as static in `gnuastro/qsort.h') */
+     sort them given indexs based on their flux ('gal_qsort_index_arr' is
+     defined as static in 'gnuastro/qsort.h') */
   if( !( (indexs->flag & GAL_DATA_FLAG_SORT_CH)
         && ( indexs->flag
              & (GAL_DATA_FLAG_SORTED_I
@@ -288,7 +288,7 @@ gal_label_watershed(gal_data_t *values, gal_data_t *indexs,
             /* A small sanity check. */
             if(Q!=NULL || cleanup!=NULL)
               error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so "
-                    "we can fix this problem. `Q' and `cleanup' should be "
+                    "we can fix this problem. 'Q' and 'cleanup' should be "
                     "NULL but while checking the equal flux regions they "
                     "aren't", __func__, PACKAGE_BUGREPORT);
 
@@ -332,10 +332,10 @@ gal_label_watershed(gal_data_t *values, gal_data_t 
*indexs,
 
                                     /* If this neighbor has a positive
                                        nlab, it belongs to another object,
-                                       so if `n1' has not been set for the
-                                       whole region (n1==0), put `nlab'
-                                       into `n1'. If `n1' has been set and
-                                       is different from `nlab' then this
+                                       so if 'n1' has not been set for the
+                                       whole region (n1==0), put 'nlab'
+                                       into 'n1'. If 'n1' has been set and
+                                       is different from 'nlab' then this
                                        whole equal flux region should be a
                                        wide river because it is connecting
                                        two connected regions.*/
@@ -361,7 +361,7 @@ gal_label_watershed(gal_data_t *values, gal_data_t *indexs,
                             are on the edge of the indexed region (the
                             neighbor is not in the initial list of pixels
                             to segment). When over-segmenting the noise and
-                            the detections, `label' is zero for the parts
+                            the detections, 'label' is zero for the parts
                             of the image that we are not interested in
                             here. */
                          else labs[*a]=GAL_LABEL_RIVER;
@@ -370,7 +370,7 @@ gal_label_watershed(gal_data_t *values, gal_data_t *indexs,
               }
 
             /* Set the label that is to be given to this equal flux
-               region. If `n1' was set to any value, then that label should
+               region. If 'n1' was set to any value, then that label should
                be used for the whole region. Otherwise, this is a new
                label, see the case for a non-flat region. */
             if(n1) rlab = n1;
@@ -397,7 +397,7 @@ gal_label_watershed(gal_data_t *values, gal_data_t *indexs,
            flux, so simply find the label for this object. */
         else
           {
-            /* `n1' is the label of the first labeled neighbor found, so
+            /* 'n1' is the label of the first labeled neighbor found, so
                we'll initialize it to zero. */
             n1=0;
 
@@ -410,7 +410,7 @@ gal_label_watershed(gal_data_t *values, gal_data_t *indexs,
                set it as a river pixel.*/
             GAL_DIMENSION_NEIGHBOR_OP(*a, ndim, dsize, ndim, dinc,
                {
-                 /* When `n1' has already been set as a river, there is no
+                 /* When 'n1' has already been set as a river, there is no
                     point in looking at the other neighbors. */
                  if(n1!=GAL_LABEL_RIVER)
                    {
@@ -446,7 +446,7 @@ gal_label_watershed(gal_data_t *values, gal_data_t *indexs,
                                     ? GAL_LABEL_RIVER
                                     : n1 ) )
 
-                            /* `nlab==0' (the neighbor lies in the other
+                            /* 'nlab==0' (the neighbor lies in the other
                                domain (sky or detections). To avoid the
                                different domains touching, this pixel
                                should be a river. */
@@ -458,7 +458,7 @@ gal_label_watershed(gal_data_t *values, gal_data_t *indexs,
                of its neighbors. If n1 equals zero, then this is a new
                peak, and a new label should be created.  But if n1!=0, it
                is either a river pixel (has more than one labeled neighbor
-               and has been set to `GAL_LABEL_RIVER' before) or all its
+               and has been set to 'GAL_LABEL_RIVER' before) or all its
                neighbors have the same label. In both such cases, rlab
                should be set to n1.*/
             if(n1) rlab = n1;
@@ -540,20 +540,20 @@ label_clump_significance_sanity(gal_data_t *values, 
gal_data_t *std,
 
   /* Type of values. */
   if( values->type!=GAL_TYPE_FLOAT32 )
-    error(EXIT_FAILURE, 0, "%s: the values dataset must have a `float' "
-          "type, but it has a `%s' type", func,
+    error(EXIT_FAILURE, 0, "%s: the values dataset must have a 'float' "
+          "type, but it has a '%s' type", func,
           gal_type_name(values->type, 1));
 
   /* Type of standard deviation. */
   if( std->type!=GAL_TYPE_FLOAT32 )
     error(EXIT_FAILURE, 0, "%s: the standard deviation dataset must have a "
-          "`float' (`float32') type, but it has a `%s' type", func,
+          "'float' ('float32') type, but it has a '%s' type", func,
           gal_type_name(std->type, 1));
 
   /* Type of labels image. */
   if( label->type!=GAL_TYPE_INT32 )
-    error(EXIT_FAILURE, 0, "%s: the labels dataset must have an `int32' "
-          "type, but it has a `%s' type", func,
+    error(EXIT_FAILURE, 0, "%s: the labels dataset must have an 'int32' "
+          "type, but it has a '%s' type", func,
           gal_type_name(label->type, 1));
 
   /* Dimentionality of the values dataset. */
@@ -564,8 +564,8 @@ label_clump_significance_sanity(gal_data_t *values, 
gal_data_t *std,
 
   /* Type of indexs image. */
   if( indexs->type!=GAL_TYPE_SIZE_T )
-    error(EXIT_FAILURE, 0, "%s: the indexs dataset must have a `size_t' "
-          "type, but it has a `%s' type", func,
+    error(EXIT_FAILURE, 0, "%s: the indexs dataset must have a 'size_t' "
+          "type, but it has a '%s' type", func,
           gal_type_name(label->type, 1));
 
   /* Dimensionality of indexs (must be 1D). */
@@ -590,11 +590,11 @@ label_clump_significance_sanity(gal_data_t *values, 
gal_data_t *std,
           "tessellation (when a tessellation is given)",
           func, std->size, values->size);
 
-  /* If the `array' and `dsize' elements of `sig' have already been set. */
+  /* If the 'array' and 'dsize' elements of 'sig' have already been set. */
   if(sig->array)
     error(EXIT_FAILURE, 0, "%s: the dataset that will contain the "
-          "significance values must have NULL pointers for its `array' "
-          "and `dsize' pointers (they will be allocated here)", func);
+          "significance values must have NULL pointers for its 'array' "
+          "and 'dsize' pointers (they will be allocated here)", func);
 
   /* See if the clumps are to be built starting from local maxima or local
      minima. */
@@ -610,7 +610,7 @@ label_clump_significance_sanity(gal_data_t *values, 
gal_data_t *std,
             if( isnan(second) )
               {
                 /* Note that the elements may have equal values, so for
-                   `second', we want the first non-blank AND different
+                   'second', we want the first non-blank AND different
                    value. */
                 if( f[*a]!=first )
                   second=f[*a];
@@ -622,7 +622,7 @@ label_clump_significance_sanity(gal_data_t *values, 
gal_data_t *std,
   while(++a<af);
 
   /* Note that if all the values are blank or there is only one value
-     covered by all the indexs, then both (or one) of `first' or `second'
+     covered by all the indexs, then both (or one) of 'first' or 'second'
      will be NAN. In either case, the significance measure is not going to
      be meaningful if we assume the clumps start from the maxima or
      minima. So we won't check if they are NaN or not.*/
@@ -649,7 +649,7 @@ enum infocols
     INFO_PEAK_RIVER,     /* Peak (min or max) river value around a clump. */
     INFO_PEAK_CENTER,    /* Peak (min or max) clump value.                */
 
-    INFO_NCOLS,          /* Total number of columns in the `info' table.  */
+    INFO_NCOLS,          /* Total number of columns in the 'info' table.  */
   };
 static void
 label_clump_significance_raw(gal_data_t *values_d, gal_data_t *std_d,
@@ -683,7 +683,7 @@ label_clump_significance_raw(gal_data_t *values_d, 
gal_data_t *std_d,
             /* Add this pixel to this clump's area. */
             ++row[ INFO_INAREA ];
 
-            /* In the loop `INFO_INAREA' is just the pixel counter of this
+            /* In the loop 'INFO_INAREA' is just the pixel counter of this
                clump. The pixels are sorted by flux (decreasing for
                positive clumps and increasing for negative). So the second
                extremum value is just the second pixel of the clump. */
@@ -697,16 +697,16 @@ label_clump_significance_raw(gal_data_t *values_d, 
gal_data_t *std_d,
           {
             /* We are on a river pixel. So the value of this pixel has to
                be added to any of the clumps in touches. But since it might
-               touch a labeled region more than once, we use `ngblabs' to
+               touch a labeled region more than once, we use 'ngblabs' to
                keep track of which label we have already added its value
-               to. `ii` is the number of different labels this river pixel
-               has already been considered for. `ngblabs' will keep the list
+               to. 'ii' is the number of different labels this river pixel
+               has already been considered for. 'ngblabs' will keep the list
                labels. */
             ii=0;
             memset(ngblabs, 0, nngb*sizeof *ngblabs);
 
             /* Look into the 8-connected neighbors (recall that a
-               connectivity of `ndim' means all pixels touching it (even on
+               connectivity of 'ndim' means all pixels touching it (even on
                one vertice). */
             GAL_DIMENSION_NEIGHBOR_OP(*a, ndim, dsize, ndim, dinc, {
                 /* This neighbor's label. */
@@ -784,7 +784,7 @@ gal_label_clump_significance(gal_data_t *values, gal_data_t 
*std,
 
   /* Allocate the arrays to keep the final significance measure (and
      possibly the indexs). */
-  sig->ndim  = 1;                        /* Depends on `cltprm->sn' */
+  sig->ndim  = 1;                        /* Depends on 'cltprm->sn' */
   sig->type  = GAL_TYPE_FLOAT32;
   if(sig->dsize==NULL)
     sig->dsize = gal_pointer_allocate(GAL_TYPE_SIZE_T, 1, 0, __func__,
@@ -823,7 +823,7 @@ gal_label_clump_significance(gal_data_t *values, gal_data_t 
*std,
          for this clump, then do the measurement. */
       if( row[ INFO_INAREA ]>minarea && row[ INFO_RIVAREA ])
         {
-          /* Set the index to write the values. If `keepsmall' is not
+          /* Set the index to write the values. If 'keepsmall' is not
              called, we don't care about the IDs of the clumps anymore, so
              store the signal-to-noise ratios contiguously. Note that
              counter will always be smaller and equal to i. */
@@ -908,7 +908,7 @@ gal_label_grow_indexs(gal_data_t *labels, gal_data_t 
*indexs, int withrivers,
   label_check_type(indexs, GAL_TYPE_SIZE_T, "indexs", __func__);
   label_check_type(labels, GAL_TYPE_INT32,  "labels", __func__);
   if(indexs->ndim!=1)
-    error(EXIT_FAILURE, 0, "%s: `indexs' has to be a 1D array, but it is "
+    error(EXIT_FAILURE, 0, "%s: 'indexs' has to be a 1D array, but it is "
           "%zuD", __func__, indexs->ndim);
 
   /* The basic idea is this: after growing, not all the blank pixels are
@@ -920,22 +920,22 @@ gal_label_grow_indexs(gal_data_t *labels, gal_data_t 
*indexs, int withrivers,
      pixels left to fill in this round (thisround) equals the number of
      blanks.
 
-     To start the loop, we set `thisround' to one more than the number of
+     To start the loop, we set 'thisround' to one more than the number of
      indexed pixels. Note that it will be corrected immediately after the
-     loop has started, it is just important to pass the `while'. */
+     loop has started, it is just important to pass the 'while'. */
   thisround=ninds+1;
   while( thisround > ninds )
     {
-      /* `thisround' will keep the number of pixels to be inspected in this
-         round. `ninds' will count the number of pixels left without a
-         label by the end of this round. Since `ninds' comes from the
+      /* 'thisround' will keep the number of pixels to be inspected in this
+         round. 'ninds' will count the number of pixels left without a
+         label by the end of this round. Since 'ninds' comes from the
          previous loop (or outside, for the first round) it has to be saved
-         in `thisround' to begin counting a fresh. */
+         in 'thisround' to begin counting a fresh. */
       thisround=ninds;
       ninds=0;
 
-      /* Go over all the available indexs. NOTE: while the `indexs->array'
-         pointer remains unchanged, `indexs->size' can/will change (get
+      /* Go over all the available indexs. NOTE: while the 'indexs->array'
+         pointer remains unchanged, 'indexs->size' can/will change (get
          smaller) in every loop. */
       sf = (s=indexs->array) + indexs->size;
       do
@@ -946,7 +946,7 @@ gal_label_grow_indexs(gal_data_t *labels, gal_data_t 
*indexs, int withrivers,
 
           /* Check the neighbors of this pixel. Note that since this
              macro has multiple loops within it, we can't use
-             break. We'll use the `searchngb' variable instead. */
+             break. We'll use the 'searchngb' variable instead. */
           searchngb=1;
           GAL_DIMENSION_NEIGHBOR_OP(*s, labels->ndim, labels->dsize,
             connectivity, dinc,
@@ -972,7 +972,7 @@ gal_label_grow_indexs(gal_data_t *labels, gal_data_t 
*indexs, int withrivers,
                           n1=nlab;
 
                           /* If we want to completely fill in the region
-                             (`withrivers==0'), then there is no point in
+                             ('withrivers==0'), then there is no point in
                              looking in other neighbors, the first
                              neighbor we find, is the one we'll use. */
                           if(!withrivers) searchngb=0;
@@ -991,15 +991,15 @@ gal_label_grow_indexs(gal_data_t *labels, gal_data_t 
*indexs, int withrivers,
              The first one means that no neighbors were found and this
              pixel should be kept for the next loop (we'll be growing the
              objects pixel-layer by pixel-layer). In the other two cases,
-             we just need to write in the value of `n1'. */
+             we just need to write in the value of 'n1'. */
           if(n1)
             {
               /* Set the label. */
               olabel[*s]=n1;
 
               /* If this pixel is a river (can only happen when
-                 `withrivers' is zero), keep it in the loop, because we
-                 want the `indexs' dataset to contain all non-positive
+                 'withrivers' is zero), keep it in the loop, because we
+                 want the 'indexs' dataset to contain all non-positive
                  (non-labeled) pixels, including rivers. */
               if(n1==GAL_LABEL_RIVER)
                 iarray[ ninds++ ] = *s;
@@ -1007,7 +1007,7 @@ gal_label_grow_indexs(gal_data_t *labels, gal_data_t 
*indexs, int withrivers,
           else
             iarray[ ninds++ ] = *s;
 
-          /* Correct the size of the `indexs' dataset. */
+          /* Correct the size of the 'indexs' dataset. */
           indexs->size = indexs->dsize[0] = ninds;
         }
       while(++s<sf);
diff --git a/lib/list.c b/lib/list.c
index fc4be54..757fa8a 100644
--- a/lib/list.c
+++ b/lib/list.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -52,6 +52,9 @@ gal_list_str_add(gal_list_str_t **list, char *value,
 {
   gal_list_str_t *newnode;
 
+  /* If the value is a NULL pointer, don't add to the list. */
+  if(value==NULL) return;
+
   errno=0;
   newnode=malloc(sizeof *newnode);
   if(newnode==NULL)
@@ -1179,11 +1182,11 @@ gal_list_dosizet_pop_smallest(gal_list_dosizet_t 
**largest,
     }
   else
     {
-      /* If `smallest' is NULL, `largest' should also be NULL. */
+      /* If 'smallest' is NULL, 'largest' should also be NULL. */
       if(*largest)
-        error(EXIT_FAILURE, 0, "%s: `largest' and `smallest' pointers must "
+        error(EXIT_FAILURE, 0, "%s: 'largest' and 'smallest' pointers must "
               "both be non-NULL or both be NULL. However, in this call, "
-              "`smallest' was NULL while `largest' isn't NULL", __func__);
+              "'smallest' was NULL while 'largest' isn't NULL", __func__);
       value=GAL_BLANK_SIZE_T;
       *tosort=NAN;
     }
@@ -1286,7 +1289,7 @@ gal_list_data_add(gal_data_t **list, gal_data_t *newnode)
       toadd=tmp;
     }
   else
-    /* Its not a list, so just set it to `toadd'. */
+    /* Its not a list, so just set it to 'toadd'. */
     toadd=newnode;
 
 
diff --git a/lib/match.c b/lib/match.c
index a69fb86..e2ec0d1 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -25,12 +25,14 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdio.h>
 #include <errno.h>
 #include <error.h>
+#include <float.h>
 #include <stdlib.h>
 
 #include <gsl/gsl_sort.h>
 
 #include <gnuastro/box.h>
 #include <gnuastro/list.h>
+#include <gnuastro/blank.h>
 #include <gnuastro/pointer.h>
 #include <gnuastro/permutation.h>
 
@@ -126,9 +128,9 @@ match_coordinate_sanity_check_columns(gal_data_t *coord, 
char *info,
       if(tmp->type!=GAL_TYPE_FLOAT64)
         {
           if(inplace)
-            error(EXIT_FAILURE, 0, "%s: when `inplace' is activated, the "
-                  "input coordinates must have `float64' type. At least "
-                  "one node of the %s list has type of `%s'", __func__, info,
+            error(EXIT_FAILURE, 0, "%s: when 'inplace' is activated, 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));
           else
             *allf64=0;
@@ -203,7 +205,7 @@ match_coordinaes_sanity_check(gal_data_t *coord1, 
gal_data_t *coord2,
 
     default:
       error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-            "the issue. The value %zu not recognized for `ndim'", __func__,
+            "the issue. The value %zu not recognized for 'ndim'", __func__,
             PACKAGE_BUGREPORT, ncoord1);
     }
 }
@@ -217,19 +219,50 @@ match_coordinaes_sanity_check(gal_data_t *coord1, 
gal_data_t *coord2,
 static size_t *
 match_coordinates_prepare_sort(gal_data_t *coords, size_t minmapsize)
 {
+  size_t i;
+  double *darr;
   gal_data_t *tmp;
   size_t *permutation=gal_pointer_allocate(GAL_TYPE_SIZE_T, coords->size, 0,
                                            __func__, "permutation");
 
+  /* Unfortunately 'gsl_sort_index' doesn't account for NaN elements. So we
+     need to set them to the maximum possible floating point value. */
+  if( gal_blank_present(coords, 1) )
+    {
+      darr=coords->array;
+      for(i=0;i<coords->size;++i)
+        if( isnan(darr[i]) ) darr[i]=FLT_MAX;
+    }
+
   /* Get the permutation necessary to sort all the columns (based on the
      first column). */
   gsl_sort_index(permutation, coords->array, 1, coords->size);
 
+  /* For a check.
+  if(coords->size>1)
+    for(size_t i=0; i<coords->size; ++i) printf("%zu\n", permutation[i]);
+  */
+
   /* Sort all the coordinates. */
   for(tmp=coords; tmp!=NULL; tmp=tmp->next)
     gal_permutation_apply(tmp, permutation);
 
-  /* Clean up. */
+  /* For a check.
+  if(coords->size>1)
+    {
+      for(i=0;i<coords->size;++i)
+        {
+          for(tmp=coords; tmp!=NULL; tmp=tmp->next)
+            {
+              printf("%f ", ((double *)(tmp->array))[i]);
+            }
+          printf("\n");
+        }
+      exit(0);
+    }
+  */
+
+  /* Return the permutation. */
   return permutation;
 }
 
@@ -248,7 +281,7 @@ match_coordinates_prepare(gal_data_t *coord1, gal_data_t 
*coord2,
   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. */
+     sorted, then 'inplace' is irrelevant. */
   if(sorted_by_first && allf64)
     {
       *A_out=coord1;
@@ -256,7 +289,7 @@ match_coordinates_prepare(gal_data_t *coord1, gal_data_t 
*coord2,
     }
   else
     {
-      /* Allocating a new list is only necessary when `inplace==0' or all
+      /* Allocating a new list is only necessary when 'inplace==0' or all
          the columns are double. */
       if( inplace && allf64 )
         {
@@ -318,7 +351,7 @@ match_coordinates_prepare(gal_data_t *coord1, gal_data_t 
*coord2,
 /********************************************************************/
 /*************            Coordinate matching           *************/
 /********************************************************************/
-/* Preparations for `match_coordinates_second_in_first'. */
+/* Preparations for 'match_coordinates_second_in_first'. */
 static void
 match_coordinates_sif_prepare(gal_data_t *A, gal_data_t *B,
                               double *aperture, size_t ndim, double **a,
@@ -485,7 +518,7 @@ match_coordinates_second_in_first(gal_data_t *A, gal_data_t 
*B,
                                   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
+     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.*/
   int iscircle=0;
@@ -501,40 +534,38 @@ match_coordinates_second_in_first(gal_data_t *A, 
gal_data_t *B,
   match_coordinates_sif_prepare(A, B, aperture,  ndim, a, b, dist, c, s,
                                 &iscircle);
 
-  /* For each row/record of catalog `a', make a list of the nearest records
+  /* 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)
-    if(blow<br)
+    if( !isnan(a[0][ai]) && blow<br)
       {
-        /* Initialize `bina'. */
+        /* 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
+           '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
+           '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
+           a search here to change 'blow' if necessary before doing further
            searching.*/
         for( blow=prevblow; blow<br && b[0][blow] < a[0][ai]-dist[0]; ++blow)
-          { /* This can be blank, the `for' does all we need :-). */ }
-
+          { /* This can be blank, the 'for' does all we need :-). */ }
 
-        /* `blow' is now found for this `ai' and will be used unchanged to
+        /* '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'. */
+           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'. */
+        /* 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
@@ -550,28 +581,28 @@ match_coordinates_second_in_first(gal_data_t *A, 
gal_data_t *B,
                each other to easily define an independent sorting in the
                second axis. */
             if( ndim<2
-                || ( b[1][bi] >= a[1][ai]-dist[1]
+                || (    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
+                /* 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
+                   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'.
+                   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.
+                   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
+                   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
@@ -594,23 +625,22 @@ match_coordinates_second_in_first(gal_data_t *A, 
gal_data_t *B,
               }
           }
 
-
         /* If there was no objects within the acceptable distance, then the
-           linked list pointer will be NULL, so go on to the next `ai'. */
+           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);
-           }
+        {
+          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);
+        }
         */
       }
 }
@@ -619,7 +649,7 @@ match_coordinates_second_in_first(gal_data_t *A, gal_data_t 
*B,
 
 
 
-/* In `match_coordinates_second_in_first', we made an array of lists, here
+/* 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
@@ -630,25 +660,25 @@ match_coordinates_rearrange(gal_data_t *A, gal_data_t *B,
   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
+  /* 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
+     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__,
+    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.*/
+  /* 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'. */
+       /* 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
@@ -671,7 +701,7 @@ match_coordinates_rearrange(gal_data_t *A, gal_data_t *B,
   }
   */
 
-  /* Re-fill the bina array, but this time only with the `bi' that is
+  /* 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 )
@@ -682,8 +712,8 @@ match_coordinates_rearrange(gal_data_t *A, gal_data_t *B,
        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
+       /* 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] )
@@ -764,17 +794,17 @@ gal_match_coordinates_output(gal_data_t *A, gal_data_t 
*B, size_t *A_perm,
                                  "Distance between the match.");
 
 
-  /* Allocate the `Bmatched' array which is a flag for which rows of the
+  /* Allocate the 'Bmatched' array which is a flag for which rows of the
      second catalog were matched. The columns that had a match will get a
      value of one while we are parsing them below. */
   Bmatched=gal_pointer_allocate(GAL_TYPE_UINT8, B->size, 1, __func__,
                                 "Bmatched");
 
 
-  /* Initialize the indexs. We want the first `nummatched' indexs in both
+  /* Initialize the indexs. We want the first 'nummatched' indexs in both
      outputs to be the matching rows. The non-matched rows should start to
      be indexed after the matched ones. So the first non-matched index is
-     at the index `nummatched'. */
+     at the index 'nummatched'. */
   match_i   = 0;
   nomatch_i = nummatched;
 
@@ -794,7 +824,7 @@ gal_match_coordinates_output(gal_data_t *A, gal_data_t *B, 
size_t *A_perm,
           aind[ match_i   ] = A_perm ? A_perm[ai] : ai;
           bind[ match_i++ ] = B_perm ? B_perm[bi] : bi;
 
-          /* Set a `1' for this object in the second catalog. This will
+          /* Set a '1' for this object in the second catalog. This will
              later be used to find which rows didn't match to fill in the
              output.. */
           Bmatched[ B_perm ? B_perm[bi] : bi ] = 1;
@@ -849,21 +879,21 @@ gal_match_coordinates_output(gal_data_t *A, gal_data_t 
*B, size_t *A_perm,
 /********************************************************************/
 /*************            Coordinate matching           *************/
 /********************************************************************/
-/* Match two positions: the two inputs (`coord1' and `coord2') should be
+/* Match two positions: the two inputs ('coord1' and 'coord2') should be
    lists of coordinates (each is a list of datasets). To speed up the
    search, this function will sort the inputs by their first column. If
    both are already sorted, give a non-zero value to
-   `sorted_by_first'. When sorting is necessary and `inplace' is non-zero,
+   'sorted_by_first'. 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.
+   freed. Therefore when 'inplace==0', the input's won't be changed.
 
    IMPORTANT NOTE: the output permutations will correspond to the initial
-   inputs. Therefore, even when `inplace' is non-zero (and this function
+   inputs. Therefore, even when 'inplace' is non-zero (and this function
    changes the inputs' order), the output permutation will correspond to
    original inputs.
 
-   The output is a list of `gal_data_t' with the following columns:
+   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).
@@ -880,25 +910,25 @@ gal_match_coordinates(gal_data_t *coord1, gal_data_t 
*coord2,
   struct match_coordinate_sfll **bina;
 
   /* Do a small sanity check and make the preparations. After this point,
-     we'll call the two arrays `a' and `b'.*/
+     we'll call the two arrays 'a' and 'b'.*/
   match_coordinaes_sanity_check(coord1, coord2, aperture, inplace,
                                 &allf64);
   match_coordinates_prepare(coord1, coord2, sorted_by_first, inplace, allf64,
                             &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
+  /* 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__,
+    error(EXIT_FAILURE, errno, "%s: %zu bytes for 'bina'", __func__,
           A->size*sizeof *bina);
 
 
-  /* All records in `b' that match each `a' (possibly duplicate). */
+  /* All records in 'b' that match each 'a' (possibly duplicate). */
   match_coordinates_second_in_first(A, B, aperture, bina);
 
 
@@ -922,7 +952,7 @@ gal_match_coordinates(gal_data_t *coord1, gal_data_t 
*coord2,
   if(B_perm) free(B_perm);
 
 
-  /* Set `nummatched' and return output. */
+  /* Set 'nummatched' and return output. */
   *nummatched = out ?  out->next->next->size : 0;
   return out;
 }
diff --git a/lib/options.c b/lib/options.c
index b71d8c3..e029072 100644
--- a/lib/options.c
+++ b/lib/options.c
@@ -4,7 +4,7 @@ Function to parse options and configuration file values.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -134,8 +134,8 @@ gal_options_abort_if_mandatory_missing(struct 
gal_options_common_params *cp)
           "options and configuration files, please see the \"Options\" and "
           "\"Configuration files\" section of the Gnuastro book "
           "respectively. You can read them on the command-line by running "
-          "the following commands (type `SPACE' to flip through pages, type "
-          "`Q' to return to the command-line):\n\n"
+          "the following commands (type 'SPACE' to flip through pages, type "
+          "'Q' to return to the command-line):\n\n"
           "  info gnuastro Options\n"
           "  info gnuastro \"Configuration files\"\n");
 
@@ -187,7 +187,7 @@ gal_options_check_version(struct argp_option *option, char 
*arg,
   /* First see if we are reading or writing. */
   if(lineno==-1)
     {
-      /* `PACKAGE_VERSION' is a static/literal string, but the pointer
+      /* 'PACKAGE_VERSION' is a static/literal string, but the pointer
          returned by this function will be freed, so we must allocate space
          for it.
 
@@ -205,15 +205,15 @@ gal_options_check_version(struct argp_option *option, 
char *arg,
     {
       if(arg==NULL)
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-              "the problem. The value to `arg' is NULL", __func__,
+              "the problem. The value to 'arg' is NULL", __func__,
               PACKAGE_BUGREPORT);
       else if( strcmp(arg, PACKAGE_VERSION) )
         {
           /* Print an error message and abort.  */
           error_at_line(EXIT_FAILURE, 0, filename, lineno, "version "
                         "mis-match: you are running GNU Astronomy Utilities "
-                        "(Gnuastro) version `%s'. However, the `onlyversion' "
-                        "option is set to version `%s'.\n\n"
+                        "(Gnuastro) version '%s'. However, the 'onlyversion' "
+                        "option is set to version '%s'.\n\n"
                         "This was probably done for reproducibility. "
                         "Therefore, manually removing, or changing, the "
                         "option value might produce errors or unexpected "
@@ -251,19 +251,19 @@ gal_options_print_citation(struct argp_option *option, 
char *arg,
   struct gal_options_common_params *cp=(struct gal_options_common_params *)pa;
   char *gnuastro_acknowledgement;
   char *gnuastro_bibtex=
-    "Gnuastro package/infrastructure\n"
-    "-------------------------------\n"
-    "  @ARTICLE{2015ApJS..220....1A,\n"
+    "First paper introducing Gnuastro\n"
+    "--------------------------------\n"
+    "  @ARTICLE{gnuastro,\n"
     "     author = {{Akhlaghi}, M. and {Ichikawa}, T.},\n"
     "      title = \"{Noise-based Detection and Segmentation of Nebulous "
     "Objects}\",\n"
-    "    journal = {\\apjs},\n"
+    "    journal = {ApJS},\n"
     "  archivePrefix = \"arXiv\",\n"
     "     eprint = {1505.01664},\n"
     "   primaryClass = \"astro-ph.IM\",\n"
-    "   keywords = {galaxies: irregular, galaxies: photometry, "
-    "galaxies: structure, methods: data analysis, "
-    "techniques: image processing, techniques: photometric},\n"
+    "   keywords = {galaxies: irregular, galaxies: photometry,\n"
+    "               galaxies: structure, methods: data analysis,\n"
+    "               techniques: image processing, techniques: photometric},\n"
     "       year = 2015,\n"
     "      month = sep,\n"
     "     volume = 220,\n"
@@ -362,15 +362,15 @@ gal_options_check_config(struct argp_option *option, char 
*arg,
       /* Activate the option and let the user know its activated. */
       (*(uint8_t *)(option->value)) = 1;
       printf("-----------------\n"
-             "Parsing of options AFTER `--checkconfig'.\n\n"
+             "Parsing of options AFTER '--checkconfig'.\n\n"
              "IMPORTANT: Any option that was parsed before encountering "
-             "`--checkconfig', on the command-line or in a configuration "
+             "'--checkconfig', on the command-line or in a configuration "
              "file, is not shown here. It is thus recommended to use this "
              "option before calling any other option.\n"
              "-----------------\n");
 
-      /* Print where this option was first seen: if `checkconfig' is called
-         within a configuration file, `filename!=NULL' and has an argument
+      /* Print where this option was first seen: if 'checkconfig' is called
+         within a configuration file, 'filename!=NULL' and has an argument
          (=="1"). But on the command-line, it has no argument or
          filename. */
       if(filename)
@@ -379,7 +379,7 @@ gal_options_check_config(struct argp_option *option, char 
*arg,
         {
           if(arg)
             error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
-                  "fix the problem. `filename==NULL', but `arg!=NULL'",
+                  "fix the problem. 'filename==NULL', but 'arg!=NULL'",
                   __func__, PACKAGE_BUGREPORT);
           else
             printf("Command-line:\n");
@@ -403,7 +403,7 @@ gal_options_read_type(struct argp_option *option, char *arg,
   char *str;
   if(lineno==-1)
     {
-      /* Note that `gal_data_type_as_string' returns a static string. But
+      /* Note that 'gal_data_type_as_string' returns a static string. But
          the output must be an allocated string so we can free it. */
       gal_checkset_allocate_copy(
            gal_type_name( *(uint8_t *)(option->value), 1), &str);
@@ -417,11 +417,11 @@ gal_options_read_type(struct argp_option *option, char 
*arg,
       /* Read the value. */
       if ( (*(uint8_t *)(option->value) = gal_type_from_name(arg) )
            == GAL_TYPE_INVALID )
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' (value to "
-                      "`%s' option) couldn't be recognized as a known "
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
+                      "'%s' option) couldn't be recognized as a known "
                       "type.\n\nFor the full list of known types, please "
                       "run the following command (press SPACE key to go "
-                      "down, and `q' to return to the command-line):\n\n"
+                      "down, and 'q' to return to the command-line):\n\n"
                       "    $ info gnuastro \"Numeric data types\"\n",
                       arg, option->name);
 
@@ -442,7 +442,7 @@ gal_options_read_searchin(struct argp_option *option, char 
*arg,
   char *str;
   if(lineno==-1)
     {
-      /* Note that `gal_data_type_as_string' returns a static string. But
+      /* Note that 'gal_data_type_as_string' returns a static string. But
          the output must be an allocated string so we can free it. */
       gal_checkset_allocate_copy(
         gal_tableintern_searchin_as_string( *(uint8_t *)(option->value)),
@@ -457,11 +457,11 @@ gal_options_read_searchin(struct argp_option *option, 
char *arg,
       /* Read the value. */
       if((*(uint8_t *)(option->value)=gal_tableintern_string_to_searchin(arg))
          == GAL_TABLE_SEARCH_INVALID )
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' (value to "
-                      "`%s' option) couldn't be recognized as a known table "
-                      "search-in field (`name', `unit', or `comment').\n\n"
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
+                      "'%s' option) couldn't be recognized as a known table "
+                      "search-in field ('name', 'unit', or 'comment').\n\n"
                       "For more explanation, please run the following "
-                      "command (press SPACE key to go down, and `q' to "
+                      "command (press SPACE key to go down, and 'q' to "
                       "return to the command-line):\n\n"
                       "    $ info gnuastro \"Selecting table columns\"\n",
                       arg, option->name);
@@ -483,7 +483,7 @@ gal_options_read_tableformat(struct argp_option *option, 
char *arg,
   char *str;
   if(lineno==-1)
     {
-      /* Note that `gal_data_type_as_string' returns a static string. But
+      /* Note that 'gal_data_type_as_string' returns a static string. But
          the output must be an allocated string so we can free it. */
       gal_checkset_allocate_copy(
         gal_tableintern_format_as_string( *(uint8_t *)(option->value)), &str);
@@ -497,10 +497,10 @@ gal_options_read_tableformat(struct argp_option *option, 
char *arg,
       /* Read the value. */
       if( (*(uint8_t *)(option->value)=gal_tableintern_string_to_format(arg) )
           ==GAL_TABLE_FORMAT_INVALID )
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' (value to "
-                      "`%s' option) couldn't be recognized as a known table "
-                      "format field (`txt', `fits-ascii', or "
-                      "`fits-binary').\n\n", arg, option->name);
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
+                      "'%s' option) couldn't be recognized as a known table "
+                      "format field ('txt', 'fits-ascii', or "
+                      "'fits-binary').\n\n", arg, option->name);
 
       /* For no un-used variable warning. This function doesn't need the
          pointer.*/
@@ -521,10 +521,10 @@ gal_options_read_interpmetric(struct argp_option *option, 
char *arg,
     {
       switch(*(uint8_t *)(option->value))
         {
-        case GAL_INTERPOLATE_CLOSE_METRIC_RADIAL:
+        case GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL:
           gal_checkset_allocate_copy("radial", &str);
           break;
-        case GAL_INTERPOLATE_CLOSE_METRIC_MANHATTAN:
+        case GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN:
           gal_checkset_allocate_copy("manhattan", &str);
           break;
         default:
@@ -542,13 +542,13 @@ gal_options_read_interpmetric(struct argp_option *option, 
char *arg,
 
       /* Set the value. */
       if(       !strcmp(arg, "radial") )
-        *(uint8_t *)(option->value) = GAL_INTERPOLATE_CLOSE_METRIC_RADIAL;
+        *(uint8_t *)(option->value) = GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL;
       else if ( !strcmp(arg, "manhattan") )
-        *(uint8_t *)(option->value) = GAL_INTERPOLATE_CLOSE_METRIC_MANHATTAN;
+        *(uint8_t *)(option->value) = 
GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN;
       else
-        error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' (value to "
-                      "`%s' option) isn't valid. Currently only `radial' "
-                      "and `manhattan' metrics are recognized for nearest "
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "'%s' (value to "
+                      "'%s' option) isn't valid. Currently only 'radial' "
+                      "and 'manhattan' metrics are recognized for nearest "
                       "neighbor interpolation", arg, option->name);
 
       /* For no un-used variable warning. This function doesn't need the
@@ -562,9 +562,9 @@ gal_options_read_interpmetric(struct argp_option *option, 
char *arg,
 
 
 /* The input to this function is a string of any number of numbers
-   separated by a comma (`,') and possibly containing fractions, for
-   example: `1,2/3, 4.95'. The output `gal_data_t' contains the array of
-   given values in `double' type. You can read the number from its `size'
+   separated by a comma (',') and possibly containing fractions, for
+   example: '1,2/3, 4.95'. The output 'gal_data_t' contains the array of
+   given values in 'double' type. You can read the number from its 'size'
    element. */
 gal_data_t *
 gal_options_parse_list_of_numbers(char *string, char *filename, size_t lineno)
@@ -576,7 +576,7 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
   double numerator=NAN, denominator=NAN, tmp;
 
   /* The nature of the arrays/numbers read here is very small, so since
-     `p->cp.minmapsize' might not have been read yet, we will set it to -1
+     'p->cp.minmapsize' might not have been read yet, we will set it to -1
      (largest size_t number), so the values are kept in memory. */
   int quietmmap=1;
   size_t minmapsize=-1;
@@ -601,7 +601,7 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
         case ':':
           if(isnan(numerator))
             error_at_line(EXIT_FAILURE, 0, filename, lineno, "a number "
-                          "must be given before `,'. You have given: `%s'",
+                          "must be given before ','. You have given: '%s'",
                           string);
           gal_list_f64_add(&list, isnan(denominator)
                            ? numerator : numerator/denominator);
@@ -613,17 +613,17 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
         /* Divide two numbers. */
         case '/':
           if( isnan(numerator) || !isnan(denominator) )
-            error_at_line(EXIT_FAILURE, 0, filename, lineno, "`/' must "
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "'/' must "
                           "only be between two numbers and used for "
-                          "division. But you have given `%s'", string);
+                          "division. But you have given '%s'", string);
           ++c;
           break;
 
-        /* Extra dot is an error (cases like 2.5.5). Valid `.'s will be
-           read by `strtod'. */
+        /* Extra dot is an error (cases like 2.5.5). Valid '.'s will be
+           read by 'strtod'. */
         case '.':
-          error_at_line(EXIT_FAILURE, 0, filename, lineno, "extra `.' in "
-                        "`%s'", string);
+          error_at_line(EXIT_FAILURE, 0, filename, lineno, "extra '.' in "
+                        "'%s'", string);
           break;
 
         /* Read the number. */
@@ -633,8 +633,8 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
           tmp=strtod(c, &tailptr);
           if(tailptr==c)
             error_at_line(EXIT_FAILURE, 0, filename, lineno, "the first "
-                          "part of `%s' couldn't be read as a number. This "
-                          "was part of `%s'", c, string);
+                          "part of '%s' couldn't be read as a number. This "
+                          "was part of '%s'", c, string);
 
           /* See if the number should be put in the numerator or
              denominator. */
@@ -646,13 +646,13 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
                                  "than two numbers in each element.");
             }
 
-          /* Set `c' to tailptr. */
+          /* Set 'c' to tailptr. */
           c=tailptr;
         }
     }
 
 
-  /* If the last number wasn't finished by a `,', add the read value to the
+  /* If the last number wasn't finished by a ',', add the read value to the
      list */
   if( !isnan(numerator) )
     {
@@ -694,10 +694,62 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
 
 
 
+gal_data_t *
+gal_options_parse_list_of_strings(char *string, char *filename, size_t lineno)
+{
+  size_t num;
+  gal_data_t *out;
+  gal_list_str_t *list=NULL, *tll;
+  char *cp, *token, **strarr, delimiters[]=",:";
+
+  /* The nature of the arrays/numbers read here is very small, so since
+     'p->cp.minmapsize' might not have been read yet, we will set it to -1
+     (largest size_t number), so the values are kept in memory. */
+  int quietmmap=1;
+  size_t minmapsize=-1;
+
+  /* If we have an empty string, just return NULL. */
+  if(string==NULL || *string=='\0') return NULL;
+
+  /* Make a copy of the input string, and save the tokens */
+  gal_checkset_allocate_copy(string, &cp);
+  token=strtok(cp, delimiters);
+  gal_list_str_add(&list, token, 1);
+  while(token!=NULL)
+    {
+      token=strtok(NULL, delimiters);
+      if(token!=NULL)
+        gal_list_str_add(&list, token, 1);
+    }
+
+
+  /* Allocate the output dataset (array containing all the given
+     strings). */
+  num=gal_list_str_number(list);
+  out=gal_data_alloc(NULL, GAL_TYPE_STRING, 1, &num, NULL, 0,
+                     minmapsize, quietmmap, NULL, NULL, NULL);
+
+  /* Fill the output dataset. */
+  strarr=out->array;
+  for(tll=list;tll!=NULL;tll=tll->next)
+    strarr[--num]=tll->v;
+
+  /* Clean up and return. Note that we don't want to free the values in the
+     list, the elements in 'out->array' point to them and will later use
+     them.*/
+  free(cp);
+  gal_list_str_free(list, 0);
+  return out;
+}
+
+
+
+
+
 /* The input to this function is a string of any number of strings
-   separated by a comma (`,') for example: `a,abc,abcd'. The output
-   `gal_data_t' contains the array of given strings. You can read the
-   number of inputs from its `size' element. */
+   separated by a comma (',') for example: 'a,abc,abcd'. The output
+   'gal_data_t' contains the array of given strings. You can read the
+   number of inputs from its 'size' element. */
 gal_data_t *
 gal_options_parse_csv_strings_raw(char *string, char *filename, size_t lineno)
 {
@@ -708,7 +760,7 @@ gal_options_parse_csv_strings_raw(char *string, char 
*filename, size_t lineno)
 
 
   /* The nature of the arrays/numbers read here is very small, so since
-     `p->cp.minmapsize' might not have been read yet, we will set it to -1
+     'p->cp.minmapsize' might not have been read yet, we will set it to -1
      (largest size_t number), so the values are kept in memory. */
   int quietmmap=1;
   size_t minmapsize=-1;
@@ -723,15 +775,15 @@ gal_options_parse_csv_strings_raw(char *string, char 
*filename, size_t lineno)
         case ',':
           if(str==NULL)
             error_at_line(EXIT_FAILURE, 0, filename, lineno, "a string "
-                          "must exist before the first `,'. You have "
-                          "given: `%s'", string);
+                          "must exist before the first ','. You have "
+                          "given: '%s'", string);
           *c='\0';
           gal_list_str_add(&list, str, 1);
           str=NULL;  /* Mark that the next character is the start */
           break;
 
         /* If the character isn't a coma, it is either in the middle of a
-           string at the start of it. If `str==NULL', then it is at the
+           string at the start of it. If 'str==NULL', then it is at the
            start. */
         default: if(str==NULL) str=c;
         }
@@ -779,10 +831,10 @@ gal_options_parse_csv_strings_raw(char *string, char 
*filename, size_t lineno)
 
 
 
-/* `arg' is the value given to an option. It contains multiple strings
-   separated by a comma (`,'). This function will parse `arg' and make a
-   `gal_data_t' array of strings from it. The output `gal_data_t' will be
-   put in `option->value'. */
+/* 'arg' is the value given to an option. It contains multiple strings
+   separated by a comma (','). This function will parse 'arg' and make a
+   'gal_data_t' array of strings from it. The output 'gal_data_t' will be
+   put in 'option->value'. */
 void *
 gal_options_parse_csv_strings(struct argp_option *option, char *arg,
                               char *filename, size_t lineno, void *junk)
@@ -846,6 +898,11 @@ gal_options_parse_csv_strings(struct argp_option *option, 
char *arg,
       /* If the option is already set, just return. */
       if(option->set) return NULL;
 
+      /* Make sure an argument is actually given. */
+      if(*arg=='\0')
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
+                      "given to '--%s'", option->name);
+
       /* Read the values. */
       values=gal_options_parse_csv_strings_raw(arg, filename, lineno);
 
@@ -859,10 +916,65 @@ gal_options_parse_csv_strings(struct argp_option *option, 
char *arg,
 
 
 
+/* Some options can be called multiple times on the command-line, but
+   within the program, all the values must be merged into one. For example
+   '--column=1 --column=2,3 --column=4,5,6'. In this example, the output
+   'gal_list_str_t' will have 3 nodes, but we actually want a list that has
+   6 nodes. */
+void
+gal_options_merge_list_of_csv(gal_list_str_t **list)
+{
+  size_t i;
+  gal_data_t *strs;
+  char *c, **strarr;
+  gal_list_str_t *tmp, *in=*list, *out=NULL;
+
+  /* Go over each input and add it to the list. */
+  for(tmp=in; tmp!=NULL; tmp=tmp->next)
+    {
+      /* Remove any possibly commented new-line where we have a backslash
+         followed by a new-line character (replace the two characters with
+         two single space characters). This can happen with options have
+         have longer scripts and the user is forced to break the line with
+         a '\' followed by newline. */
+      for(c=tmp->v;*c!='\0';++c)
+        if(*c=='\\' && *(c+1)=='\n') { *c=' '; *(++c)=' '; }
+
+      /* Read the different comma-separated strings into an array (within a
+         'gal_data_t'). */
+      strs=gal_options_parse_csv_strings_raw(tmp->v, NULL, 0);
+      strarr=strs->array;
+
+      /* Go through all the items and add the pointers to the output
+         list. We won't re-allocate the string, we'll just set it to NULL
+         in the array. */
+      for(i=0;i<strs->size;++i)
+        {
+          gal_list_str_add(&out, strarr[i], 0);
+          strarr[i]=NULL;
+        }
+
+      /* Clean up. */
+      gal_data_free(strs);
+    }
+
+  /* Free the input list and reverse the output list to be in the same
+     input order. */
+  gal_list_str_free(in, 1);
+  gal_list_str_reverse(&out);
+
+  /* Reset the input pointer. */
+  *list=out;
+}
+
+
+
+
+
 /* Parse the given string into a series of size values (integers, stored as
-   an array of size_t). The output array will be stored in the `value'
+   an array of size_t). The output array will be stored in the 'value'
    element of the option. The last element of the array is
-   `GAL_BLANK_SIZE_T' to allow finding the number of elements within it
+   'GAL_BLANK_SIZE_T' to allow finding the number of elements within it
    later (similar to a string which terminates with a '\0' element). */
 void *
 gal_options_parse_sizes_reverse(struct argp_option *option, char *arg,
@@ -908,6 +1020,11 @@ gal_options_parse_sizes_reverse(struct argp_option 
*option, char *arg,
       /* If the option is already set, just return. */
       if(option->set) return NULL;
 
+      /* Make sure an argument is actually given. */
+      if(*arg=='\0')
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
+                      "given to '--%s'", option->name);
+
       /* Read the values. */
       values=gal_options_parse_list_of_numbers(arg, filename, lineno);
 
@@ -917,19 +1034,19 @@ gal_options_parse_sizes_reverse(struct argp_option 
*option, char *arg,
         {
           if(v[i]<0)
             error_at_line(EXIT_FAILURE, 0, filename, lineno, "a given "
-                          "value in `%s' (%g) is not 0 or positive. The "
-                          "values to the `--%s' option must be positive",
+                          "value in '%s' (%g) is not 0 or positive. The "
+                          "values to the '--%s' option must be positive",
                           arg, v[i], option->name);
 
           if(ceil(v[i]) != v[i])
             error_at_line(EXIT_FAILURE, 0, filename, lineno, "a given "
-                          "value in `%s' (%g) is not an integer. The "
-                          "values to the `--%s' option must be integers",
+                          "value in '%s' (%g) is not an integer. The "
+                          "values to the '--%s' option must be integers",
                           arg, v[i], option->name);
         }
 
       /* Write the values into an allocated size_t array and finish it with
-         a `-1' so the total number can be found later.*/
+         a '-1' so the total number can be found later.*/
       num=values->size;
       array=gal_pointer_allocate(GAL_TYPE_SIZE_T, num+1, 0, __func__,
                                  "array");
@@ -990,6 +1107,11 @@ gal_options_parse_csv_float64(struct argp_option *option, 
char *arg,
       /* If the option is already set, just return. */
       if(option->set) return NULL;
 
+      /* Make sure an argument is actually given. */
+      if(*arg=='\0')
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
+                      "given to '--%s'", option->name);
+
       /* Read the values. */
       values=gal_options_parse_list_of_numbers(arg, filename, lineno);
 
@@ -1008,7 +1130,7 @@ gal_options_parse_csv_float64(struct argp_option *option, 
char *arg,
 
 /* 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
+   2-element array. 'option->value' must point to an already allocated
    2-element array of double type. */
 void *
 gal_options_read_sigma_clip(struct argp_option *option, char *arg,
@@ -1031,10 +1153,10 @@ gal_options_read_sigma_clip(struct argp_option *option, 
char *arg,
 
   /* Check if there was only two numbers. */
   if(in->size!=2)
-    error_at_line(EXIT_FAILURE, 0, filename, lineno, "the `--%s' "
+    error_at_line(EXIT_FAILURE, 0, filename, lineno, "the '--%s' "
                   "option takes two values (separated by a comma) for "
                   "defining the sigma-clip. However, %zu numbers were "
-                  "read in the string `%s' (value to this option).\n\n"
+                  "read in the string '%s' (value to this option).\n\n"
                   "The first number is the multiple of sigma, and the "
                   "second is either the tolerance (if its is less than "
                   "1.0), or a specific number of times to clip (if it "
@@ -1042,23 +1164,23 @@ gal_options_read_sigma_clip(struct argp_option *option, 
char *arg,
                   arg);
 
   /* Copy the sigma clip parameters into the space the caller has given (as
-     the `value' element of `option'). */
+     the 'value' element of 'option'). */
   memcpy(option->value, in->array, 2*sizeof *sigmaclip);
 
   /* Multiple of sigma must be positive. */
   if( sigmaclip[0] <= 0 )
     error_at_line(EXIT_FAILURE, 0, filename, lineno, "the first value to "
-                  "the `--%s' option (multiple of sigma), must be "
-                  "greater than zero. From the string `%s' (value to "
+                  "the '--%s' option (multiple of sigma), must be "
+                  "greater than zero. From the string '%s' (value to "
                   "this option), you have given a value of %g for the "
                   "first value", option->name, arg, sigmaclip[0]);
 
   /* Second value must also be positive. */
   if( sigmaclip[1] <= 0 )
     error_at_line(EXIT_FAILURE, 0, filename, lineno, "the second value "
-                  "to the `--%s' option (tolerance to stop clipping or "
+                  "to the '--%s' option (tolerance to stop clipping or "
                   "number of clips), must be greater than zero. From "
-                  "the string `%s' (value to this option), you have "
+                  "the string '%s' (value to this option), you have "
                   "given a value of %g for the second value",
                   option->name, arg, sigmaclip[1]);
 
@@ -1066,10 +1188,10 @@ gal_options_read_sigma_clip(struct argp_option *option, 
char *arg,
      integer. */
   if( sigmaclip[1] >= 1.0f && ceil(sigmaclip[1]) != sigmaclip[1])
     error_at_line(EXIT_FAILURE, 0, filename, lineno, "when the second "
-                  "value to the `--%s' option is >=1, it is interpretted "
+                  "value to the '--%s' option is >=1, it is interpretted "
                   "as an absolute number of clips. So it must be an "
                   "integer. However, your second value is a floating "
-                  "point number: %g (parsed from `%s')", option->name,
+                  "point number: %g (parsed from '%s')", option->name,
                   sigmaclip[1], arg);
 
   /* Clean up and return. */
@@ -1081,27 +1203,28 @@ gal_options_read_sigma_clip(struct argp_option *option, 
char *arg,
 
 
 
-/* Parse name and (float64) values:  name,value1,value2,value3,...
+/* Parse name and (string/float64) values:  name,value1,value2,value3,...
 
-   The output is a `gal_data_t', where the `name' is the given name and the
-   values are in its array (of `float64' type).
- */
-void *
+   The output is a 'gal_data_t', where the 'name' is the given name and the
+   values are in its array (of 'char *' or 'float64' type). */
+static void *
 gal_options_parse_name_and_values(struct argp_option *option, char *arg,
-                                  char *filename, size_t lineno, void *junk)
+                                  char *filename, size_t lineno, void *junk,
+                                  int str0_f641)
 {
   size_t i, nc;
-  double *darray;
-  char *c, *name, *values;
+  double *darray=NULL;
   gal_data_t *tmp, *existing, *dataset;
+  char *c, *name, *values, **strarr=NULL;
   char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
 
   /* We want to print the stored values. */
   if(lineno==-1)
     {
-      /* Set the value pointer to `dataset'. */
+      /* Set the value pointer to 'existing'. */
       existing=*(gal_data_t **)(option->value);
-      darray = existing->array;
+      if(str0_f641) darray = existing->array;
+      else          strarr = existing->array;
 
       /* First write the name. */
       nc=0;
@@ -1116,8 +1239,11 @@ gal_options_parse_name_and_values(struct argp_option 
*option, char *arg,
                   "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]);
+          if(str0_f641) nc += sprintf(sstr+nc, "%g,", darray[i]);
+          else          nc += sprintf(sstr+nc, "%s,", strarr[i]);
         }
+
+      /* Finish the string. */
       sstr[nc-1]='\0';
 
       /* Copy the string into a dynamically allocated space, because it
@@ -1127,20 +1253,28 @@ gal_options_parse_name_and_values(struct argp_option 
*option, char *arg,
     }
   else
     {
+      /* Make sure an argument is actually given. */
+      if(*arg=='\0')
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
+                      "given to '--%s'", option->name);
+
       /* Parse until the comma or the end of the string.*/
       c=arg; while(*c!='\0' && *c!=',') ++c;
       values = (*c=='\0') ? NULL : c+1;
 
-      /* Name of the dataset (note that `c' is already pointing the end of
-         the `name' and `values' points to the next character). So we can
-         safely set `c' to `\0' to have the `name'. */
+      /* Name of the dataset (note that 'c' is already pointing the end of
+         the 'name' and 'values' points to the next character). So we can
+         safely set 'c' to '\0' to have the 'name'. */
       *c='\0';
       gal_checkset_allocate_copy(arg, &name);
 
-      /* Read the values and write the name. */
-      dataset=gal_options_parse_list_of_numbers(values, filename, lineno);
+      /* Read the values. */
+      dataset=( str0_f641
+                ? gal_options_parse_list_of_numbers(values, filename, lineno)
+                : gal_options_parse_list_of_strings(values, filename, lineno));
 
-      /* If there actually was a string of numbers, then do the rest. */
+      /* If there actually was a string of numbers, add the dataset to the
+         rest. */
       if(dataset)
         {
           dataset->name=name;
@@ -1163,10 +1297,10 @@ gal_options_parse_name_and_values(struct argp_option 
*option, char *arg,
           */
         }
       else
-        error(EXIT_FAILURE, 0, "`--%s' requires a string of numbers "
-              "(separated by `,' or `:') following its first argument, "
-              "please run with `--help' for more information",
-              option->name);
+        error(EXIT_FAILURE, 0, "'--%s' requires a series of %s "
+              "(separated by ',' or ':') following its first argument, "
+              "please run with '--help' for more information",
+              option->name, str0_f641?"numbers":"strings");
 
       /* Our job is done, return NULL. */
       return NULL;
@@ -1177,6 +1311,192 @@ gal_options_parse_name_and_values(struct argp_option 
*option, char *arg,
 
 
 
+void *
+gal_options_parse_name_and_strings(struct argp_option *option, char *arg,
+                                   char *filename, size_t lineno, void *junk)
+{
+  return gal_options_parse_name_and_values(option, arg, filename, lineno,
+                                           junk, 0);
+}
+
+
+
+
+
+void *
+gal_options_parse_name_and_float64s(struct argp_option *option, char *arg,
+                                    char *filename, size_t lineno, void *junk)
+{
+  return gal_options_parse_name_and_values(option, arg, filename, lineno,
+                                           junk, 1);
+}
+
+
+
+
+
+/* Parse strings like this: 'num1,num2:num3,n4:num5,num6' and return it as
+   a data container array: all elements are simply placed after each other
+   in the array. */
+static gal_data_t *
+options_parse_colon_sep_csv(char *instring, char *filename, size_t lineno)
+{
+  char *tailptr;
+  gal_data_t *out;
+  char *pt=instring;
+  size_t dim=0, size;
+  double read, *array;
+  gal_list_f64_t *vertices=NULL;
+
+  /* Parse the string. */
+  while(*pt!='\0')
+    {
+      switch(*pt)
+        {
+        case ',':
+          ++dim;
+          if(dim==2)
+            error_at_line(EXIT_FAILURE, 0, filename, lineno,
+                          "Extra ',' in '%s'", instring);
+          ++pt;
+          break;
+        case ':':
+          if(dim==0)
+            error_at_line(EXIT_FAILURE, 0, filename, lineno,
+                          "not enough coordinates for at least "
+                          "one polygon vertex (in %s)", instring);
+          dim=0;
+          ++pt;
+          break;
+        default:
+          break;
+        }
+
+      /* strtod will skip white spaces if they are before a number,
+         but not when they are before a : or ,. So we need to remove
+         all white spaces. White spaces are usually put beside each
+         other, so if one is encountered, go along the string until
+         the white space characters finish.  */
+      if(isspace(*pt))
+        ++pt;
+      else
+        {
+          /* Read the number: */
+          read=strtod(pt, &tailptr);
+
+          /* Check if there actually was a number.
+          printf("\n\n------\n%zu: %f (%s)\n", dim, read, tailptr);
+          */
+
+          /* Make sure if a number was read at all? */
+          if(tailptr==pt) /* No number was read! */
+            error_at_line(EXIT_FAILURE, 0, filename, lineno,
+                          "%s could not be parsed as a floating point "
+                          "number", tailptr);
+
+          /* Check if there are no extra characters in the number, for
+             example we don't have a case like '1.00132.17', or
+             1.01i:2.0. Such errors are not uncommon when typing large
+             numbers, and if ignored, they can lead to unpredictable
+             results, so its best to abort and inform the user. */
+          if( *tailptr!='\0'
+              && !isspace(*tailptr)
+              && strchr(":,", *tailptr)==NULL )
+            error_at_line(EXIT_FAILURE, 0, filename, lineno,
+                          "'%s' is an invalid floating point number "
+                          "sequence in the value to the '--polygon' "
+                          "option, error detected at '%s'", pt, tailptr);
+
+          /* Add the read coordinate to the list of coordinates. */
+          gal_list_f64_add(&vertices, read);
+
+          /* The job here is done, start from tailptr */
+          pt=tailptr;
+        }
+    }
+
+  /* Convert the list to an array, put it in a data structure, clean up and
+     return. */
+  array=gal_list_f64_to_array(vertices, 1, &size);
+  out=gal_data_alloc(array, GAL_TYPE_FLOAT64, 1, &size, NULL, 0, -1, 1,
+                     NULL, NULL, NULL);
+  gal_list_f64_free(vertices);
+  return out;
+}
+
+
+
+
+
+/* Parse strings that are given to a function in this format
+   'num1,num2:num3,n4:num5,num6' */
+void *
+gal_options_parse_colon_sep_csv(struct argp_option *option, char *arg,
+                                char *filename, size_t lineno, void *junk)
+{
+  double *darray;
+  size_t i, nc, size;
+  gal_data_t *tmp, *dataset, *existing;
+  char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
+
+  /* We want to print the stored values. */
+  if(lineno==-1)
+    {
+      /* Set the value pointer to 'existing'. */
+      existing=*(gal_data_t **)(option->value);
+      darray=existing->array;
+
+      /* Start printing the values. */
+      nc=0;
+      size=existing->size;
+      for(i=0;i<size;i+=2)
+        {
+          /* Make sure we aren't passing the allocated space. */
+          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);
+
+          /* Print the two values in the expected format. */
+          nc += sprintf(sstr+nc, "%.6f,%.6f%s", darray[i], darray[i+1],
+                        (i==(size-2) ? "" : ":") );
+        }
+
+      /* Finish the string. */
+      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
+    {
+      /* Make sure an argument is actually given. */
+      if(*arg=='\0')
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "no value "
+                      "given to '--%s'", option->name);
+
+      /* Parse the desired format and put it in this option's pointer. */
+      dataset=options_parse_colon_sep_csv(arg, filename, lineno);
+
+      /* Add the given dataset to the end of an existing dataset. */
+      existing = *(gal_data_t **)(option->value);
+      if(existing)
+        {
+          for(tmp=existing;tmp!=NULL;tmp=tmp->next)
+            if(tmp->next==NULL) { tmp->next=dataset; break; }
+        }
+      else
+        *(gal_data_t **)(option->value) = dataset;
+
+      /* In this scenario, there is no NULL value. */
+      return NULL;
+    }
+}
+
 
 
 
@@ -1195,10 +1515,10 @@ gal_options_parse_name_and_values(struct argp_option 
*option, char *arg,
 /**********************************************************************/
 /************              Option actions               ***************/
 /**********************************************************************/
-/* The option value has been read and put into the `value' field of the
-   `argp_option' structure. This function will use the `range' field to
+/* The option value has been read and put into the 'value' field of the
+   'argp_option' structure. This function will use the 'range' field to
    define a check and abort with an error if the value is not in the given
-   range. It also takes the `arg' so it can be used for good error message
+   range. It also takes the 'arg' so it can be used for good error message
    (showing the value that could not be read). */
 static void
 options_sanity_check(struct argp_option *option, char *arg,
@@ -1215,7 +1535,7 @@ options_sanity_check(struct argp_option *option, char 
*arg,
                  | GAL_ARITHMETIC_INPLACE );
 
   /* Currently, this function is only for numeric types, so if the value is
-     string type, or its `range' field is `GAL_OPTIONS_RANGE_ANY', then
+     string type, or its 'range' field is 'GAL_OPTIONS_RANGE_ANY', then
      just return without any checks. */
   if( option->type==GAL_TYPE_STRING
       || option->type==GAL_TYPE_STRLL
@@ -1346,8 +1666,8 @@ options_sanity_check(struct argp_option *option, char 
*arg,
 
   /* Use the arithmetic library to check for the condition. We don't want
      to free the value or change its value, so when dealing with the value
-     directly, we won't use the `GAL_ARITHMETIC_FREE', or
-     `GAL_ARITHMETIC_INPLACE' flags. But we will do this when there are
+     directly, we won't use the 'GAL_ARITHMETIC_FREE', or
+     'GAL_ARITHMETIC_INPLACE' flags. But we will do this when there are
      multiple checks so from the two check data structures, we only have
      one remaining. */
   check1=gal_arithmetic(operator1, 1, GAL_ARITHMETIC_NUMOK, value, ref1);
@@ -1361,13 +1681,13 @@ options_sanity_check(struct argp_option *option, char 
*arg,
   /* If the final check is not successful, then print an error. */
   if( *(unsigned char *)(check1->array)==0 )
     error_at_line(EXIT_FAILURE, 0, filename, lineno,
-                  "value to option `%s' must be %s, but the given value "
-                  "is `%s'. Recall that `%s' is \"%s\"", option->name,
+                  "value to option '%s' must be %s, but the given value "
+                  "is '%s'. Recall that '%s' is '%s'", option->name,
                   message, arg, option->name, option->doc);
 
 
   /* Clean up and finish. Note that we used the actual value pointer in the
-     data structure, so first we need to set it to NULL, so `gal_data_free'
+     data structure, so first we need to set it to NULL, so 'gal_data_free'
      doesn't free it, we need it for later (for example to print the option
      values). */
   value->array=NULL;
@@ -1391,8 +1711,8 @@ gal_options_read_check(struct argp_option *option, char 
*arg, char *filename,
   if(option->func)
     {
       /* For the functions that are defined here (for all programs) and
-         need the last pointer, we must pass the `cp' pointer. For the
-         rest, we must pass the `cp->program_struct'. */
+         need the last pointer, we must pass the 'cp' pointer. For the
+         rest, we must pass the 'cp->program_struct'. */
       switch(option->key)
         {
         case GAL_OPTIONS_KEY_CITE:
@@ -1404,13 +1724,13 @@ gal_options_read_check(struct argp_option *option, char 
*arg, char *filename,
         }
 
       /* Call the function to parse the value, flag the option as set and
-         return (except for the `--config' option, which must always be
+         return (except for the '--config' option, which must always be
          unset). */
       option->func(option, arg, filename, lineno, topass);
       if(option->key!=GAL_OPTIONS_KEY_CONFIG) option->set=GAL_OPTIONS_SET;
 
-      /* The `--config' option is printed for `--checkconfig' by its
-         function (`gal_options_call_parse_config_file'), so must be
+      /* The '--config' option is printed for '--checkconfig' by its
+         function ('gal_options_call_parse_config_file'), so must be
          ignored here. */
       if(cp->checkconfig && option->key!=GAL_OPTIONS_KEY_CONFIG)
         printf("  %-25s%s\n", option->name, arg?arg:"ACTIVATED");
@@ -1436,10 +1756,10 @@ gal_options_read_check(struct argp_option *option, char 
*arg, char *filename,
 
           /* Read the string argument into the value. */
           if( gal_type_from_string(&option->value, arg, option->type) )
-            /* Fortunately `error_at_line' will behave like `error' when the
+            /* Fortunately 'error_at_line' will behave like 'error' when the
                filename is NULL (the option was read from a command-line). */
             error_at_line(EXIT_FAILURE, 0, filename, lineno,
-                          "`%s' (value to option `--%s') couldn't be read "
+                          "'%s' (value to option '--%s') couldn't be read "
                           "into the proper numerical type. Common causes "
                           "for this error are:\n"
                           "  - It contains non-numerical characters.\n"
@@ -1474,7 +1794,7 @@ gal_options_read_check(struct argp_option *option, char 
*arg, char *filename,
       else
         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
               "correct it. Options with no arguments, must have "
-              "type `%s'. However, the `%s' option has type %s",
+              "type '%s'. However, the '%s' option has type %s",
               __func__, PACKAGE_BUGREPORT,
               gal_type_name(GAL_OPTIONS_NO_ARG_TYPE, 1),
               option->name, gal_type_name(option->type, 1));
@@ -1487,7 +1807,7 @@ gal_options_read_check(struct argp_option *option, char 
*arg, char *filename,
            (arg && option->type!=GAL_OPTIONS_NO_ARG_TYPE)?arg:"ACTIVATED");
 
 
-  /* Flip the `set' flag to `GAL_OPTIONS_SET'. */
+  /* Flip the 'set' flag to 'GAL_OPTIONS_SET'. */
   option->set=GAL_OPTIONS_SET;
 }
 
@@ -1512,7 +1832,7 @@ gal_options_read_check(struct argp_option *option, char 
*arg, char *filename,
 /**********************************************************************/
 /************            Command-line options           ***************/
 /**********************************************************************/
-/* Set the value given to the command-line, where we have the integer `key'
+/* Set the value given to the command-line, where we have the integer 'key'
    of the option, not its long name as a string. */
 error_t
 gal_options_set_from_key(int key, char *arg, struct argp_option *options,
@@ -1569,14 +1889,14 @@ gal_options_common_argp_parse(int key, char *arg, 
struct argp_state *state)
   struct gal_options_common_params *cp=state->input;
 
   /* In case the user incorrectly uses the equal sign (for example
-     with a short format or with space in the long format, then `arg`
+     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, "
+    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");
 
@@ -1599,11 +1919,11 @@ gal_options_stdin_error(long stdintimeout, int 
precedence, char *name)
                "or the standard input.%s Standard input can come from a "
                "pipe (output of another program) or typed on the "
                "command-line before %ld micro-seconds (configurable with "
-               "the `--stdintimeout' option).", name, name,
+               "the '--stdintimeout' option).", name, name,
                ( precedence
                  ? " If both are provided, a file takes precedence."
                  : "" ), stdintimeout )<0 )
-    error(EXIT_FAILURE, 0, "%s: `asprintf' allocation error", __func__);
+    error(EXIT_FAILURE, 0, "%s: 'asprintf' allocation error", __func__);
 
   return out;
 }
@@ -1755,7 +2075,7 @@ options_set_from_name(char *name, char *arg,  struct 
argp_option *options,
 
                - Not all common options are used by all programs. When a
                  program doesn't use an option, it will be given an
-                 `OPTION_HIDDEN' flag. There is no point in reading the
+                 'OPTION_HIDDEN' flag. There is no point in reading the
                  values of such options.
 
                - When the option already has a value AND it ISN'T a linked
@@ -1822,7 +2142,7 @@ options_parse_file(char *filename,  struct 
gal_options_common_params *cp,
   size_t linelen=10, lineno=0;
 
 
-  /* If `lastconfig' was called prior to this file, then just return and
+  /* If 'lastconfig' was called prior to this file, then just return and
      ignore this configuration file. */
   if( options_lastconfig_has_been_called(cp->coptions) )
     return;
@@ -1841,12 +2161,12 @@ options_parse_file(char *filename,  struct 
gal_options_common_params *cp,
 
 
   /* Allocate the space necessary to keep a copy of each line as we parse
-     it. Note that `getline' is going to later `realloc' this space to fit
+     it. Note that 'getline' is going to later 'realloc' this space to fit
      the line length. */
   errno=0;
   line=malloc(linelen*sizeof *line);
   if(line==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `line'",
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'line'",
           __func__, linelen*sizeof *line);
 
 
@@ -1860,7 +2180,7 @@ options_parse_file(char *filename,  struct 
gal_options_common_params *cp,
           options_read_name_arg(line, filename, lineno, &name, &arg);
 
           /* First look into this program's options, if the option isn't
-             found there, `options_set_from_name' will return 1. So the
+             found there, 'options_set_from_name' will return 1. So the
              condition will succeed and we will start looking into the
              common options, if it isn't found there either, then report an
              error.*/
@@ -1869,8 +2189,8 @@ options_parse_file(char *filename,  struct 
gal_options_common_params *cp,
             if( options_set_from_name(name, arg, cp->coptions, cp,
                                       filename, lineno) )
               error_at_line(EXIT_FAILURE, 0, filename, lineno,
-                            "unrecognized option `%s', for the full list of "
-                            "options, please run with `--help'", name);
+                            "unrecognized option '%s', for the full list of "
+                            "options, please run with '--help'", name);
         }
     }
 
@@ -1888,15 +2208,15 @@ options_parse_file(char *filename,  struct 
gal_options_common_params *cp,
 
 
 
-/* This function will be used when the `--config' option is called. */
+/* This function will be used when the '--config' option is called. */
 void *
 gal_options_call_parse_config_file(struct argp_option *option, char *arg,
                                    char *filename, size_t lineno, void *c)
 {
   struct gal_options_common_params *cp=(struct gal_options_common_params *)c;
 
-  /* The `--config' option is a special function when it comes to
-     `--checkconfig': we'll have to write its value before interpretting
+  /* The '--config' option is a special function when it comes to
+     '--checkconfig': we'll have to write its value before interpretting
      it. */
   if(cp->checkconfig)
     {
@@ -1925,14 +2245,14 @@ gal_options_call_parse_config_file(struct argp_option 
*option, char *arg,
    into it. The directories containing the configuration files are fixed
    for all the programs.
 
-    - `SYSCONFIG_DIR' is passed onto the library functions at compile time
+    - 'SYSCONFIG_DIR' is passed onto the library functions at compile time
       from the command-line. You can search for it in the outputs of
-      `make'. The main reason is that we want the the user still has the
-      chance to change the installation directory after `configure'.
+      'make'. The main reason is that we want the the user still has the
+      chance to change the installation directory after 'configure'.
 
-    - `USERCONFIG_DIR' is defined in `config.h'.
+    - 'USERCONFIG_DIR' is defined in 'config.h'.
 
-    - `CURDIRCONFIG_DIR' is defined in `config.h'. */
+    - 'CURDIRCONFIG_DIR' is defined in 'config.h'. */
 static void
 gal_options_parse_config_files(struct gal_options_common_params *cp)
 {
@@ -1940,11 +2260,11 @@ gal_options_parse_config_files(struct 
gal_options_common_params *cp)
   char *filename;
 
   /* A small sanity check because in multiple places, we have assumed the
-     on/off options have a type of `unsigned char'. */
+     on/off options have a type of 'unsigned char'. */
   if(GAL_OPTIONS_NO_ARG_TYPE != GAL_TYPE_UINT8)
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s so we can fix "
-          "the problem. `GAL_OPTIONS_NO_ARG_TYPE' must be the "
-          "`uint8' type", __func__, PACKAGE_BUGREPORT);
+          "the problem. 'GAL_OPTIONS_NO_ARG_TYPE' must be the "
+          "'uint8' type", __func__, PACKAGE_BUGREPORT);
 
   /* The program's current directory configuration file. */
   if( asprintf(&filename, ".%s/%s.conf", PACKAGE, cp->program_exec)<0 )
@@ -2020,12 +2340,12 @@ gal_options_read_low_level_checks(struct 
gal_options_common_params *cp)
 {
   size_t suggested_mmap=10000000;
 
-  /* If `numthreads' is 0, use the number of threads available to the
+  /* If 'numthreads' is 0, use the number of threads available to the
      system. */
   if(cp->numthreads==0)
     cp->numthreads=gal_threads_number();
 
-  /* If `minmapsize==0' and quiet isn't given, print a warning. */
+  /* If 'minmapsize==0' and quiet isn't given, print a warning. */
   if(cp->minmapsize==0)
     {
       fprintf(stderr, "\n\n"
@@ -2045,7 +2365,7 @@ gal_options_read_low_level_checks(struct 
gal_options_common_params *cp)
 
               "     --minmapsize=%zu\n\n"
 
-              "[This warning can be disabled with the `--quiet' (or `-q') "
+              "[This warning can be disabled with the '--quiet' (or '-q') "
               "option.]\n"
               "===========================\n\n", suggested_mmap,
               suggested_mmap);
@@ -2131,6 +2451,8 @@ option_is_printable(struct argp_option *option)
     case GAL_OPTIONS_KEY_LASTCONFIG:
       return 0;
     }
+
+  /* Everything is fine, print the option. */
   return 1;
 }
 
@@ -2138,8 +2460,8 @@ option_is_printable(struct argp_option *option)
 
 
 
-/* For a given type, print the value in `ptr' in a space of `width'
-   elements. If `width==0', then return the width necessary to print the
+/* For a given type, print the value in 'ptr' in a space of 'width'
+   elements. If 'width==0', then return the width necessary to print the
    value. */
 static int
 options_print_any_type(struct argp_option *option, void *ptr, int type,
@@ -2257,17 +2579,17 @@ options_set_lengths(struct argp_option *poptions,
 
 
 
-/* The `#' before the `doc' string are not required by the configuration
+/* The '#' before the 'doc' string are not required by the configuration
    file parser when the documentation string fits in a line. However, when
-   the `doc' string is longer than 80 characters, it will be cut between
-   multiple lines and without the `#', the start of the line will be read
+   the 'doc' string is longer than 80 characters, it will be cut between
+   multiple lines and without the '#', the start of the line will be read
    as an option. */
 static void
 options_print_doc(FILE *fp, const char *doc, int nvwidth)
 {
   size_t len=strlen(doc);
 
-  /* The `+3' is because of the three extra spaces in this line: one before
+  /* The '+3' is because of the three extra spaces in this line: one before
      the variable name, one after it and one after the value. */
   int i, prewidth=nvwidth+3, width=77-prewidth, cwidth;
 
@@ -2386,30 +2708,30 @@ options_print_all(struct gal_options_common_params *cp, 
char *dirname,
               "# %s (%s) %s.\n"
               "# Written at %s#\n"
               "#  - Empty lines are ignored.\n"
-              "#  - Lines starting with `#` are ignored.\n"
+              "#  - Lines starting with '#' are ignored.\n"
               "#  - The long option name is followed by a value.\n"
               "#  - The name and value should be separated by atleast\n"
               "#    one white space character (for example space or tab).\n"
               "#  - If the value has space, enclose the whole value in\n"
               "#    double quotation (\") signs.\n"
               "#  - After the value, the rest of the line is ignored.\n"
-              "#\n# Run `info %s' for a more elaborate description of each "
+              "#\n# Run 'info %s' for a more elaborate description of each "
               "option.\n",
               cp->program_name, PACKAGE_NAME, PACKAGE_VERSION,
               ctime(&rawtime), cp->program_exec);
     }
   else fp=stdout;
 
-  /* Parse all the options with a title, note that the `Input', `Output'
-     and `Operating mode' options are defined in the common options, while
+  /* Parse all the options with a title, note that the 'Input', 'Output'
+     and 'Operating mode' options are defined in the common options, while
      the (possible) other groups are in the program specific options. We
-     will only be dealing with the `topics' linked list in this function
-     and the strings in `poption' are statically allocated, so its fine to
+     will only be dealing with the 'topics' linked list in this function
+     and the strings in 'poption' are statically allocated, so its fine to
      not waste CPU cycles allocating and freeing.*/
   for(i=0; !gal_options_is_last(&coptions[i]); ++i)
     if(coptions[i].name==NULL && coptions[i].key==0 && coptions[i].doc)
       {
-        /* The `(char *)' is because `.doc' is a constant and this helps
+        /* The '(char *)' is because '.doc' is a constant and this helps
            remove the compiler warning. */
         gal_list_i32_add(&group, coptions[i].group);
         gal_list_str_add(&topic, (char *)coptions[i].doc, 0);
@@ -2453,9 +2775,9 @@ options_print_all(struct gal_options_common_params *cp, 
char *dirname,
   if(dirname)
     {
       printf("\nNew/updated configuration file:\n\n  %s\n\n"
-             "You may inspect it with `cat %s'.\n"
+             "You may inspect it with 'cat %s'.\n"
              "You may use your favorite text editor to modify it later.\n"
-             "Or, run %s again with new values for the options and `--%s'.\n",
+             "Or, run %s again with new values for the options and '--%s'.\n",
              filename, filename, cp->program_name, optionname);
       free(filename);
     }
@@ -2490,7 +2812,7 @@ gal_options_print_state(struct gal_options_common_params 
*cp)
           /* Note that these options can have a value of 1 (enabled) or 0
              (explicitly disabled). Therefore the printing should only be
              done if they have a value of 1. This is why we have defined
-             the `OPTIONS_UINT8VAL' macro above. */
+             the 'OPTIONS_UINT8VAL' macro above. */
           sum += OPTIONS_UINT8VAL;
         }
 
@@ -2531,8 +2853,8 @@ gal_options_print_state(struct gal_options_common_params 
*cp)
 
     /* More than one of the printing options has been called. */
     default:
-      error(EXIT_FAILURE, 0, "only one of the `printparams', `setdirconf' "
-            "and `setusrconf' options can be called in each run");
+      error(EXIT_FAILURE, 0, "only one of the 'printparams', 'setdirconf' "
+            "and 'setusrconf' options can be called in each run");
     }
 }
 
@@ -2548,6 +2870,7 @@ options_as_fits_keywords_write(gal_fits_list_key_t **keys,
 {
   size_t i;
   void *vptr;
+  int vptrfree;
   uint8_t vtype;
   char *name, *doc;
   gal_list_str_t *tmp;
@@ -2560,11 +2883,11 @@ options_as_fits_keywords_write(gal_fits_list_key_t 
**keys,
           for(tmp=*(gal_list_str_t **)(options[i].value);
               tmp!=NULL; tmp=tmp->next)
             {
-              /* `name' and `doc' have a `const' qualifier. */
+              /* 'name' and 'doc' have a 'const' qualifier. */
               gal_checkset_allocate_copy(options[i].name, &name);
               gal_checkset_allocate_copy(options[i].doc,  &doc);
-              gal_fits_key_list_add(keys, GAL_TYPE_STRING, name, 1, tmp->v,
-                                    0, doc, 1, NULL);
+              gal_fits_key_list_add(keys, GAL_TYPE_STRING, name, 1,
+                                    tmp->v, 0, doc, 1, NULL, 0);
             }
         /* Normal types. */
         else
@@ -2572,11 +2895,13 @@ options_as_fits_keywords_write(gal_fits_list_key_t 
**keys,
             /* If the option is associated with a special function for
                reading and writing, we'll need to write the value as a
                string. */
+            vptrfree=0;
             if(options[i].func)
               {
+                vptrfree=1;
                 vtype=GAL_TYPE_STRING;
-                vptr=options[i].func(&options[i], NULL, NULL, (size_t)(-1),
-                                      cp->program_struct);
+                vptr=options[i].func(&options[i], NULL, NULL,
+                                     (size_t)(-1), cp->program_struct);
               }
             else
               {
@@ -2586,7 +2911,7 @@ options_as_fits_keywords_write(gal_fits_list_key_t **keys,
                          : options[i].value );
               }
 
-            /* Write the keyword. Note that `name' and `doc' have a `const'
+            /* Write the keyword. Note that 'name' and 'doc' have a 'const'
                qualifier. */
             gal_checkset_allocate_copy(options[i].name, &name);
             if(vtype==GAL_TYPE_STRING && strlen(vptr)>FLEN_KEYWORD)
@@ -2594,8 +2919,8 @@ options_as_fits_keywords_write(gal_fits_list_key_t **keys,
             else
               {
                 gal_checkset_allocate_copy(options[i].doc,  &doc);
-                gal_fits_key_list_add(keys, vtype, name, 1, vptr, 0, doc, 1,
-                                      NULL);
+                gal_fits_key_list_add(keys, vtype, name, 1, vptr,
+                                      vptrfree, doc, 1, NULL, 0);
               }
           }
       }
diff --git a/lib/pdf.c b/lib/pdf.c
index c3b71ee..55827ee 100644
--- a/lib/pdf.c
+++ b/lib/pdf.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -108,7 +108,7 @@ gal_pdf_write(gal_data_t *in, char *filename, float 
widthincm,
   /* Write the EPS file. */
   gal_eps_write(in, epsname, widthincm, borderwidth, 0, dontoptimize, 1);
 
-  /* Get the size of the image in `pt' units. */
+  /* Get the size of the image in 'pt' units. */
   gal_eps_to_pt(widthincm, in->dsize, w_h_in_pt);
 
   /* Write the ghostscript command to compile the EPS file to PDF. */
@@ -120,7 +120,7 @@ gal_pdf_write(gal_data_t *in, char *filename, float 
widthincm,
   /* Run Ghostscript. */
   if(system(command))
     error(EXIT_FAILURE, 0, "the command to convert a PostScript file to "
-          "PDF (`%s') was not successful! The PostScript file (%s) is "
+          "PDF ('%s') was not successful! The PostScript file (%s) is "
           "left if you want to convert or use it through any other "
           "means", command, epsname);
 
diff --git a/lib/permutation.c b/lib/permutation.c
index 470e240..f3a1439 100644
--- a/lib/permutation.c
+++ b/lib/permutation.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -65,16 +65,16 @@ gal_permutation_check(size_t *permutation, size_t size)
 /***************          Apply permutation        *******************/
 /*********************************************************************/
 /* Re-order the input dataset based on the given permutation. If
-   `permutation' is NULL, then the input won't be touched (no re-ordering).
+   'permutation' is NULL, then the input won't be touched (no re-ordering).
 
-   This is a re-implementation of GSL's `gsl_permute' function (from its
-   `permutation/permute_source.c'). The reason we didn't use that function
-   was that it uses system-specific types (like `long' and `int') which are
+   This is a re-implementation of GSL's 'gsl_permute' function (from its
+   'permutation/permute_source.c'). The reason we didn't use that function
+   was that it uses system-specific types (like 'long' and 'int') which are
    not easily convertable to Gnuastro's width-based types. There is also a
    separate function for each type, heavily using macros to allow a "base"
    function to work on all the types. Thus it is hard to
    read/understand. Since we use fixed-width types, we can easily use
-   `memcpy' and have a type-agnostic implementation (only needing the width
+   'memcpy' and have a type-agnostic implementation (only needing the width
    of the type).
 
    As described in GSL's source code and manual, this implementation comes
@@ -139,7 +139,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'. */
+   'gal_permutation_apply_inverse'. */
 void
 gal_permutation_apply_inverse(gal_data_t *input, size_t *permutation)
 {
diff --git a/lib/pointer.c b/lib/pointer.c
index 183e924..75f6078 100644
--- a/lib/pointer.c
+++ b/lib/pointer.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -38,16 +38,16 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 /* Increment a give pointer depending on the given type.
 
-   When working with the `array' elements of `gal_data_t', we are actually
-   dealing with `void *' pointers. Pointer arithmetic doesn't apply to
-   `void *', because the system doesn't know how much space each element
+   When working with the 'array' elements of 'gal_data_t', we are actually
+   dealing with 'void *' pointers. Pointer arithmetic doesn't apply to
+   'void *', because the system doesn't know how much space each element
    has to increment the pointer respectively.
 
    So, here, we will use the type information to find the increment. This
-   is mainly useful when dealing with the `block' pointer of a tile over a
-   larger image. This function reads the address as a `char *' type (note
-   that `char' is guaranteed to have a size of 1 (byte)). It then
-   increments the `char *' by `increment*sizeof(type)' */
+   is mainly useful when dealing with the 'block' pointer of a tile over a
+   larger image. This function reads the address as a 'char *' type (note
+   that 'char' is guaranteed to have a size of 1 (byte)). It then
+   increments the 'char *' by 'increment*sizeof(type)' */
 void *
 gal_pointer_increment(void *pointer, size_t increment, uint8_t type)
 {
@@ -60,7 +60,7 @@ gal_pointer_increment(void *pointer, size_t increment, 
uint8_t type)
 
 
 /* Find the number of values between two void pointers with a given
-   type. See the explanations before `gal_data_ptr_increment'. */
+   type. See the explanations before 'gal_data_ptr_increment'. */
 size_t
 gal_pointer_num_between(void *earlier, void *later, uint8_t type)
 {
@@ -73,7 +73,7 @@ gal_pointer_num_between(void *earlier, void *later, uint8_t 
type)
 
 
 /* Allocate an array based on the value of type. Note that the argument
-   `size' is the number of elements, necessary in the array, the number of
+   'size' is the number of elements, necessary in the array, the number of
    bytes each element needs will be determined internaly by this function
    using the datatype argument, so you don't have to worry about it. */
 void *
@@ -90,7 +90,7 @@ gal_pointer_allocate(uint8_t type, size_t size, int clear,
     {
       if(varname)
         error(EXIT_FAILURE, errno, "%s: %zu bytes couldn't be allocated "
-              "for variable `%s'", funcname ? funcname : __func__,
+              "for variable '%s'", funcname ? funcname : __func__,
               size * gal_type_sizeof(type), varname);
       else
         error(EXIT_FAILURE, errno, "%s: %zu bytes couldn't be allocated",
@@ -105,7 +105,7 @@ gal_pointer_allocate(uint8_t type, size_t size, int clear,
 
 
 void *
-gal_pointer_allocate_mmap(uint8_t type, size_t size, int clear,
+gal_pointer_mmap_allocate(uint8_t type, size_t size, int clear,
                           char **filename, int quietmmap)
 {
   void *out;
@@ -115,30 +115,24 @@ gal_pointer_allocate_mmap(uint8_t type, size_t size, int 
clear,
   size_t bsize=size*gal_type_sizeof(type);
 
 
-  /* Check if the .gnuastro folder exists, write the file there. If it
-     doesn't exist, then make the .gnuastro directory. If it can't be
-     built, we'll make a randomly named directory. */
-  gal_checkset_allocate_copy("./.gnuastro/", &dirname);
+  /* Check if the 'gnuastro_mmap' folder exists, write the file there. If
+     it doesn't exist, then make it. If it can't be built, we'll make a
+     randomly named file in the current directory. */
+  gal_checkset_allocate_copy("./gnuastro_mmap/", &dirname);
   if( gal_checkset_mkdir(dirname) )
     {
-      /* Free the old name. */
+      /* The directory couldn't be built. Free the old name. */
       free(dirname);
 
-      /* Try `.gnuastro_mmap' (to avoid making a separate directory for
-         each memory mapping if possible). */
-      gal_checkset_allocate_copy("./.gnuastro_mmap/", &dirname);
-      if( gal_checkset_mkdir(dirname) )
-        {
-          free(dirname);
-          dirname=NULL;
-        }
+      /* Set 'dirname' to NULL so it knows not to write in a directory. */
+      dirname=NULL;
     }
 
 
-  /* Set the filename. If `dirname' couldn't be allocated, directly make
+  /* Set the filename. If 'dirname' couldn't be allocated, directly make
      the memory map file in the current directory (just as a hidden
      file). */
-  if( asprintf(filename, "%smmap_XXXXXX", dirname?dirname:"./.gnuastro_")<0 )
+  if( asprintf(filename, "%sXXXXXX", dirname?dirname:"./gnuastro_mmap_")<0 )
     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
   if(dirname) free(dirname);
 
@@ -161,14 +155,18 @@ gal_pointer_allocate_mmap(uint8_t type, size_t size, int 
clear,
 
   /* Inform the user. */
   if(!quietmmap)
-    error(EXIT_SUCCESS, 0, "%s: temporary %zu byte file (consider "
-          "`--minmapsize')", *filename, bsize);
+    error(EXIT_SUCCESS, 0, "%s: temporary memory-mapped file (%zu bytes) "
+          "created for intermediate data that is not stored in RAM (see "
+          "the \"Memory management\" section of Gnuastro's manual for "
+          "optimizing your project's memory management, and thus speed). "
+          "To disable this warning, please use the option '--quiet-mmap'",
+          *filename, bsize);
 
 
   /* Write to the newly set file position so the space is allocated. To do
-     this, we are simply writing `uc' (a byte with value 0) into the space
-     we identified by `lseek' (above). This will ensure that this space is
-     set a side for this array and prepare us to use `mmap'. */
+     this, we are simply writing 'uc' (a byte with value 0) into the space
+     we identified by 'lseek' (above). This will ensure that this space is
+     set a side for this array and prepare us to use 'mmap'. */
   if( write(filedes, &uc, 1) == -1)
     error(EXIT_FAILURE, errno, "%s: %s: unable to write one byte at the "
           "%zu-th position", __func__, *filename, bsize);
@@ -184,7 +182,7 @@ gal_pointer_allocate_mmap(uint8_t type, size_t size, int 
clear,
               "finite number of mmap allocations. It is recommended to use "
               "ordinary RAM allocation for smaller arrays and keep mmap'd "
               "allocation only for the large volumes.\n\n", __func__);
-      error(EXIT_FAILURE, errno, "couldn't map %zu bytes into the file `%s'",
+      error(EXIT_FAILURE, errno, "couldn't map %zu bytes into the file '%s'",
             bsize, *filename);
     }
 
@@ -202,3 +200,70 @@ gal_pointer_allocate_mmap(uint8_t type, size_t size, int 
clear,
   /* Return the mmap'd pointer and save the file name. */
   return out;
 }
+
+
+
+
+
+void
+gal_pointer_mmap_free(char **mmapname, int quietmmap)
+{
+  /* Delete the file keeping the array. */
+  remove(*mmapname);
+
+  /* Inform the user. */
+  if(!quietmmap)
+    error(EXIT_SUCCESS, 0, "%s: deleted", *mmapname);
+
+  /* Free the file name space. */
+  free(*mmapname);
+
+  /* Set the name pointer to NULL since it has been freed. */
+  *mmapname=NULL;
+}
+
+
+
+
+
+void *
+gal_pointer_allocate_ram_or_mmap(uint8_t type, size_t size, int clear,
+                                 size_t minmapsize, char **mmapname,
+                                 int quietmmap, const char *funcname,
+                                 const char *varname)
+{
+  void *out;
+  size_t bytesize=gal_type_sizeof(type)*size;
+
+  /* See if the requested size is larger than 1MB (otherwise,
+     its not worth checking RAM, which involves reading a text
+     file, we won't try memory-mapping anyway). */
+
+  /* If it is decided to do memory-mapping, then do it. */
+  if( gal_checkset_need_mmap(bytesize, minmapsize, quietmmap) )
+    out=gal_pointer_mmap_allocate(type, size, clear, mmapname,
+                                  quietmmap);
+  else
+    {
+      /* Allocate the necessary space in the RAM. */
+      errno=0;
+      out = ( clear
+              ? calloc( size,  gal_type_sizeof(type) )
+              : malloc( size * gal_type_sizeof(type) ) );
+
+      /* If the array is NULL (there was no RAM left: on
+         systems other than Linux, 'malloc' will actually
+         return NULL, Linux doesn't do this unfortunately so we
+         need to read the available RAM). */
+      if(out==NULL)
+        out=gal_pointer_mmap_allocate(type, size, clear,
+                                      mmapname, quietmmap);
+
+      /* The 'errno' is re-set to zero just incase 'malloc'
+         changed it, which may cause problems later. */
+      errno=0;
+    }
+
+  /* Return the allocated dataset. */
+  return out;
+}
diff --git a/lib/polygon.c b/lib/polygon.c
index 75057ef..7566f1c 100644
--- a/lib/polygon.c
+++ b/lib/polygon.c
@@ -5,7 +5,8 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+     Sachin Kumar Singh <sachinkumarsingh092@gmail.com>
+Copyright (C) 2015-2021, 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
@@ -25,12 +26,15 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <math.h>
 #include <errno.h>
 #include <error.h>
+#include <float.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <gsl/gsl_sort.h>
 
+#include <gnuastro/pointer.h>
 #include <gnuastro/polygon.h>
+#include <gnuastro/permutation.h>
 
 
 
@@ -118,7 +122,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 /* Sort the pixels in anti clock-wise order.*/
 void
-gal_polygon_ordered_corners(double *in, size_t n, size_t *ordinds)
+gal_polygon_vertices_sort_convex(double *in, size_t n, size_t *ordinds)
 {
   double angles[GAL_POLYGON_MAX_CORNERS];
   size_t i, tmp, aindexs[GAL_POLYGON_MAX_CORNERS],
@@ -152,7 +156,7 @@ gal_polygon_ordered_corners(double *in, size_t n, size_t 
*ordinds)
     }
 
 
-  /* We only have `n-1' more elements to sort, use the angle of the
+  /* We only have 'n-1' more elements to sort, use the angle of the
      line between the three remaining points and the first point. */
   for(i=0;i<n-1;++i)
     angles[i]=atan2( in[ ordinds[i+1]*2+1 ] - in[ ordinds[0]*2+1 ],
@@ -183,11 +187,49 @@ gal_polygon_ordered_corners(double *in, size_t n, size_t 
*ordinds)
 
 
 
+/* This function checks if the polygon is convex or concave by testing all
+   3 consecutive points of the sorted polygon. If any of the test returns
+   false, the the polygon is concave else it is convex.
+
+   return 1: convex polygon
+   return 0: concave polygon
+   */
+int
+gal_polygon_is_convex(double *v, size_t n)
+{
+  size_t i;
+  int flag=1;
+
+  /* Check the first n-1 edges made by n points. */
+  for(i=0; i<n-2; i++)
+    {
+      if( GAL_POLYGON_LEFT_OF_LINE(&v[i*2], &v[(i+1)*2], &v[(i+2)*2]) )
+        continue;
+      else
+        return 0;
+    }
+
+  /* Check the edge between nth and 1st point */
+  if(flag)
+    {
+      if( GAL_POLYGON_LEFT_OF_LINE(&v[(n-2)*2], &v[(n-1)*2], &v[0]) )
+        return 1;
+      else
+        return 0;
+    }
+
+  return 1;
+}
+
+
+
+
+
 /* The area of a polygon is the sum of the vector products of all the
    vertices in a counterclockwise order. See the Wikipedia page for
    Polygon for more information.
 
-   `v' points to an array of doubles which keep the positions of the
+   'v' points to an array of doubles which keep the positions of the
    vertices such that v[0] and v[1] are the positions of the first
    corner to be considered.
 
@@ -213,8 +255,61 @@ gal_polygon_area(double *v, size_t n)
 
 
 
-/* We have a polygon with `n' vertices whose vertices are in the array
-   `v' (with 2*n elements). Such that v[0], v[1] are the two
+/* This fuction test if a point is inside the polygon using winding number
+  algorithm. See its wiki here:
+  https://en.wikipedia.org/wiki/Point_in_polygon#Winding_number_algorithm
+
+  We have a polygon with 'n' vertices whose vertices are in the array 'v'
+  (with 2*n elements). Such that v[0], v[1] are the two coordinates of the
+  first vertice. The vertices also have to be sorted in a counter clockwise
+  fashion. We also have a point (with coordinates (x, y) == (p[0], p[1])
+  and we want to see if it is inside the polygon or not.
+
+  If point is outside the polygon, return 0.
+  If point is inside the polygon, return a non-zero number.
+  */
+int
+gal_polygon_is_inside(double *v, double *p, size_t n)
+{
+  /* The winding number(wn) keeping the count of number of times the ray
+     crosses the polygon. */
+  size_t wn=0, i=0, j=n-1;
+
+  /* Loop through all the edges of the polygon*/
+  while(i<n)
+    {
+      /* Edge from v[i] to v[i+1] in upward direction */
+      if(v[j*2+1] <= p[1])
+        {
+          if(v[i*2+1] > p[1])
+            /* 'p' left of edge is an upward intersection, increase wn. */
+            if( GAL_POLYGON_TRI_CROSS_PRODUCT(&v[j*2], &v[i*2], p) > 0 )
+              wn++;
+        }
+      else{
+        /* edge from v[i] to v[i+1] in downward direction */
+        if(v[i*2+1] <= p[1])
+          /* p right of edge is a downward intersection, decrease wn */
+          if( GAL_POLYGON_TRI_CROSS_PRODUCT(&v[j*2], &v[i*2], p) < 0 )
+            wn--;
+      }
+
+      /* Increment 'j' */
+      j=i++;
+
+      /* For a check:
+         printf("winding number: %ld, %.3f\n", wn);
+      */
+    }
+  return wn;
+}
+
+
+
+
+
+/* We have a polygon with 'n' vertices whose vertices are in the array
+   'v' (with 2*n elements). Such that v[0], v[1] are the two
    coordinates of the first vertice. The vertices also have to be
    sorted in a counter clockwise fashion. We also have a point (with
    coordinates p[0], p[1]) and we want to see if it is inside the
@@ -222,10 +317,10 @@ gal_polygon_area(double *v, size_t n)
 
    If the point is inside the polygon, it will always be to the left
    of the edge connecting the two vertices when the vertices are
-   traversed in order. See the comments above `gal_polygon_area' for an
+   traversed in order. See the comments above 'gal_polygon_area' for an
    explanation about i and j and the loop.*/
 int
-gal_polygon_pin(double *v, double *p, size_t n)
+gal_polygon_is_inside_convex(double *v, double *p, size_t n)
 {
   size_t i=0, j=n-1;
 
@@ -242,8 +337,8 @@ gal_polygon_pin(double *v, double *p, size_t n)
 
 
 
-/* Similar to gal_polygon_pin, except that if the point is on one of the
-   edges of a polygon, this will return 0. */
+/* Similar to gal_polygon_is_inside_convex, except that if the point
+   is on one of the edges of a polygon, this will return 0. */
 int
 gal_polygon_ppropin(double *v, double *p, size_t n)
 {
@@ -268,8 +363,96 @@ gal_polygon_ppropin(double *v, double *p, size_t n)
 
 
 
+/* This function uses the concept of winding, which defines the relative
+   order in which the vertices of a polygon are listed to determine the
+   orientation of vertices. If orientation is positive vertices are in
+   clockwise direction, else is negative for counter-clockwise
+   direction. Zero sum implies a figure like 8, with equal orientation in
+   both direction.
+
+   See the link below for a detailed description:
+   "https://www.element84.com/blog/determining-the-winding-of-a-
+   polygon-given-as-a-set-of-ordered-points"
+
+   return 1: sorted in counter-clockwise order or equal orientation.
+   return 0: sorted clockwise order.
+   */
+int
+gal_polygon_is_counterclockwise(double *v, size_t n)
+{
+  double sum=0.0f;
+  size_t i=0, j=n-1;
+
+  while(i<n)
+    {
+      sum+=( (v[i*2]-v[j*2])*(v[i*2+1]+v[j*2+1]) );
+      j=i++;
+    }
+
+  return sum>0 ? 0 : 1;
+}
+
+
+
+
+
+/* This function checks if the vertices are actually sorted in the
+   counterclockwise. If they are do nothing, otherwise if they are
+   clockwise, convert them to counter-clockwise direction
+
+   return 1: success
+   return 0: error
+   */
+int
+gal_polygon_to_counterclockwise(double *v, size_t n)
+{
+  size_t i, j=0;
+  size_t *permutation;
+  gal_data_t *temp=NULL;
+
+  /* Operation only necesssary when polygon isn't counter-clockwise. */
+  if(gal_polygon_is_counterclockwise(v, n)==0)
+    {
+      /* Allocate space for permutation array, which stores the order of
+         index in which the vertices are to be ordered for a
+         counter-clockwise direction. */
+      permutation=gal_pointer_allocate(GAL_TYPE_SIZE_T, 2*n, 0,
+                                       __func__, "permutation");
+      for(i=0; i<=2*n-1;)
+        {
+          /* Below sequence of steps ensures that the permutation has
+             indexes reversed but in order (x,y). Simple reversing would
+             have turned it in (y,x) format. */
+          j++;
+          permutation[j]  =(2*n-1-i++);
+          permutation[j-1]=(2*n-1-i++);
+          j++;
+        }
+
+      /* Put the vertices in the 'gal_data_t' object */
+      temp=gal_data_alloc(v, GAL_TYPE_FLOAT64, 1, &n, NULL, 0,
+                          -1, 0, NULL, NULL, NULL);
+
+      /* Apply permutations to just reverse the order of clockwise to
+         counter-clockwise. */
+      gal_permutation_apply(temp, permutation);
+
+      /* Free allocated spaces. */
+      temp->array=NULL;
+      free(permutation);
+      gal_data_free(temp);
+    }
+
+  /* Return value, so far it will always return 1. */
+  return 1;
+}
+
+
+
+
+
 /* Find the intersection of a line segment (Aa--Ab) and an infinite
-   line (Ba--Bb) and put the intersection point in the output `o'. All
+   line (Ba--Bb) and put the intersection point in the output 'o'. All
    the points are assumed to be two element double arrays already
    allocated outside this function.
 
@@ -368,11 +551,11 @@ seginfintersection(double *Aa, double *Ab, double *Ba, 
double *Bb,
    done
 
    The difference is that we are not using lists, but arrays to keep
-   polygon vertices. The two polygons are called Subject (`s') and
-   Clip (`c') with `n' and `m' vertices respectively.
+   polygon vertices. The two polygons are called Subject ('s') and
+   Clip ('c') with 'n' and 'm' vertices respectively.
 
-   The output is stored in `o' and the number of elements in the
-   output are stored in what `*numcrn' (for number of corners) points
+   The output is stored in 'o' and the number of elements in the
+   output are stored in what '*numcrn' (for number of corners) points
    to.*/
 void
 gal_polygon_clip(double *s, size_t n, double *c, size_t m,
@@ -437,3 +620,279 @@ gal_polygon_clip(double *s, size_t n, double *c, size_t m,
     }
   *numcrn=outnum;
 }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/***************************************************************/
+/*******     Basic operations for concave-sort     *************/
+/***************************************************************/
+/* The point structures allows storing of points in an array
+   like a pair. */
+struct point
+{
+  double x;
+  double y;
+};
+
+
+
+
+
+/* This function allows us to find the rightmost point in the given
+   array based on its x-coordinates. */
+static void
+polygon_leftmost_point(double *in, size_t n, struct point *p)
+{
+  size_t i, min_index=-1;
+  double tmp_min = DBL_MAX;
+
+  /* Loop through the entire array and find the rightmost corner and
+     store the index of that maximum vetex. */
+  for(i=0; i<n; i++)
+    if(tmp_min > in[i*2])
+      {
+        min_index = i;
+        tmp_min = in[i*2];
+      }
+  p->x=in[min_index*2];
+  p->y=in[min_index*2+1];
+
+  /* For a check:
+  printf("leftmost point: %.3f \n", in[min_index*2]);
+  */
+}
+
+
+
+
+
+/* This function allows us to find the leftmost point int the given
+   array based on x coordinates. */
+static void
+polygon_rightmost_point(double *in, size_t n, struct point *p)
+{
+  size_t i, max_index=-1;
+  double tmp_max = DBL_MIN;
+
+  /* Loop through the entire array and find the leftmost corner and
+     store the index of that maximum vetex. */
+  for(i=0; i<n; i++)
+    if(tmp_max < in[i*2])
+      {
+        max_index = i;
+        tmp_max = in[i*2];
+      }
+  p->x=in[max_index*2];
+  p->y=in[max_index*2+1];
+
+  /* For a check:
+  printf("rightmost point: %.3f \n", in[max_index*2]);
+  */
+}
+
+
+
+
+
+/* This function uses cross-product and right-hand rule
+    to check the position of points (x, y) w.r.t the vector joining
+    the leftmost and the rightmost point(the diagonal vector).
+
+    Return 1: If point lies to the left-hand side of the diagonal vector.
+    Return 0: If point lies in the diagonal vector
+    Return -1: If point lies to the right-hand side of the diagonal vector.
+    */
+static int
+polygon_leftof_vector(double *in, size_t n, double x, double y)
+{
+  double test;
+  struct point l, r;
+
+  /* Perform the cross-product test. */
+  polygon_leftmost_point(in, n, &l);
+  polygon_rightmost_point(in, n, &r);
+  test = (r.y-y)*(r.x-l.x) - (r.y-l.y)*(r.x-x);
+
+  /* Due to the choice of return value, we multiply 'test' by -1 */
+  test = -1*test;
+  return test?(test>0?1:-1):0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/***************************************************************/
+/********   Sorting and Merging for concave sort     ***********/
+/***************************************************************/
+/* This function makes the two temporary partition of the input
+   array into A and B which keep the points above and below the
+   diagonal vector. */
+static void
+polygon_make_arr(double *in, size_t n, size_t *A_size, size_t *B_size,
+                 struct point *A, struct point *B)
+{
+  /* Here j and k are the indexes for A and B arrays respectively. */
+  size_t i, j = 0, k = 0;
+  *A_size = 0, *B_size = 0;
+
+  /* Loop through the input array and make the desired partition
+    and also calculate their respective sizes. */
+  for(i=0; i<n; i++)
+    {
+      if(polygon_leftof_vector(in, n, in[i*2], in[i*2+1]) <= 0)
+      {
+        A[j].x=in[i*2];
+        A[j].y=in[i*2+1];
+        /* For a check:
+        printf("A =: (%.3f, %.3f)\n", A[j].x, A[j].y);
+        */
+        j++;
+        (*A_size)++;
+      }
+      else
+      {
+        B[k].x=in[i*2];
+        B[k].y=in[i*2+1];
+        /* For a check:
+        printf("B =: (%.3f, %.3f)\n", B[k].x, B[k].y);
+        */
+        k++;
+        (*B_size)++;
+      }
+    }
+
+  /* For a check:
+  printf("sizes of A & B array ==> %ld, %ld \n", *A_size, *B_size);
+  */
+}
+
+
+
+
+
+/* The comparator functions for qsort. CompareA arranges the array in
+   ascending order according to their x-coordinate. */
+static int
+polygon_compareA(const void *a, const void *b)
+{
+  struct point *p1 = (struct point *)a, *p2 = (struct point *)b;
+  return ( p1->x==p2->x
+           ? 0
+           : (p1->x<p2->x ? -1 : 1) );
+}
+
+
+
+
+
+/* The comparator functions for qsort. CompareB arranges in descending
+   order according to their x-coordintes. */
+static int
+polygon_compareB(const void *a, const void *b)
+{
+  struct point *p1 = (struct point *)a, *p2 = (struct point *)b;
+  return ( p1->x==p2->x
+           ? 0
+           : (p1->x<p2->x ? 1 : -1) );
+}
+
+
+
+
+
+/* This function arranges the A and B arrays and merges them
+    together to the output array. Hence it is the main function
+    which should be called when using concave sort. */
+void
+gal_polygon_vertices_sort(double *vertices, size_t n, size_t *ordinds)
+{
+  size_t i, j, A_size = 0, B_size = 0;
+  struct point A[GAL_POLYGON_MAX_CORNERS];
+  struct point B[GAL_POLYGON_MAX_CORNERS];
+  struct point sorted[GAL_POLYGON_MAX_CORNERS];
+  struct point tordinds[GAL_POLYGON_MAX_CORNERS];
+
+  /* Sanity check. */
+  if(n>GAL_POLYGON_MAX_CORNERS)
+    error(EXIT_FAILURE, 0, "%s: most probably a bug! The number of corners "
+          "is more than %d. This is an internal value and cannot be set from "
+          "the outside. Most probably some bug has caused this un-normal "
+          "value. Please contact us at %s so we can solve this problem",
+          __func__, GAL_POLYGON_MAX_CORNERS, PACKAGE_BUGREPORT);
+
+  /* Make arrays A and B and store the vertices in them. Currently points
+     are stored in based on their position from the diagonal vector. */
+  polygon_make_arr(vertices, n, &A_size, &B_size, A, B);
+
+  /* Now, we put the contents of A and B in the temporary array. Firstly,
+     we put the contents of A and then save the last index of A (stored in
+     i) and continue from that index while copying from B(using j). */
+  for(i=0; i<A_size+B_size; i++)
+    {
+      tordinds[i].x=vertices[i*2];
+      tordinds[i].y=vertices[i*2+1];
+    }
+
+  /* Now sort the arrays A and B w.r.t their x axis, sorting A in ascending
+     order and B in descending order. */
+  qsort(A, A_size, sizeof(struct point), polygon_compareA);
+  qsort(B, B_size, sizeof(struct point), polygon_compareB);
+
+  /*Finally, we put the contents of A and B in a final sorted array.*/
+  for(i=0; i<A_size; i++) sorted[i]=A[i];
+  for(j=0; j<B_size; j++) sorted[i++]=B[j];
+
+  /* For a check.
+  for(i=0; i<A_size+B_size; i++)
+    printf("sorted array := %lf %lf\n", sorted[i], sorted[i*2+1]);
+  */
+
+  /* The temporary array is now used to find the location of points stored
+     in sorted array and assign index in ordinds accordingly.*/
+  for(i=0; i<n; i++)
+    for(j=0; j<n; j++)
+      if( tordinds[i].x == sorted[j].x && tordinds[i].y == sorted[j].y )
+        {
+          ordinds[j]=i;
+          break;
+        }
+
+  /* For a check.
+  for(i=0;i<n;i++) printf("%ld\n", ordinds[i]);
+  */
+}
diff --git a/lib/qsort.c b/lib/qsort.c
index 20aa253..3bae85d 100644
--- a/lib/qsort.c
+++ b/lib/qsort.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, Free Software Foundation, Inc.
 
 Gnuastro is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -34,14 +34,14 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /*****************************************************************/
 /**********                  Macros               ****************/
 /*****************************************************************/
-/* When one or both elements are NaN, the simple comparison, like `(tb >
+/* When one or both elements are NaN, the simple comparison, like '(tb >
    ta) - (tb < ta)', will give 0 (as if the elements are equal). However,
    some preference has to be given to the NaN element in a comparison,
    otherwise the output is not going to be reasonable. We also don't want
    to check NaNs on every comparison (it will slow down the processing).
 
    So we'll exploit the fact that when there comparison result doesn't
-   equal zero, we don't have any NaNs and this `COMPARE_FLOAT_POSTPROCESS'
+   equal zero, we don't have any NaNs and this 'COMPARE_FLOAT_POSTPROCESS'
    macro is called only when the comparison gives zero. Being larger or
    smaller isn't defined for NaNs, so we'll just put them in the end of the
    sorted list whether it is sorted by decreasing or increasing mode.*/
diff --git a/lib/speclines.c b/lib/speclines.c
index c98c212..ffa03b1 100644
--- a/lib/speclines.c
+++ b/lib/speclines.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2019, Free Software Foundation, Inc.
+Copyright (C) 2019-2021, 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
@@ -48,11 +48,11 @@ gal_speclines_line_name(int linecode)
     case GAL_SPECLINES_NII:              return GAL_SPECLINES_NAME_NII;
     case GAL_SPECLINES_HALPHA:           return GAL_SPECLINES_NAME_HALPHA;
     case GAL_SPECLINES_NIIBLUE:          return GAL_SPECLINES_NAME_NIIBLUE;
-    case GAL_SPECLINES_OIIIRED:          return GAL_SPECLINES_NAME_OIIIRED;
-    case GAL_SPECLINES_OIII:             return GAL_SPECLINES_NAME_OIII;
-    case GAL_SPECLINES_OIIIBLUE:         return GAL_SPECLINES_NAME_OIIIBLUE;
+    case GAL_SPECLINES_OIIIRED_VIS:      return GAL_SPECLINES_NAME_OIIIRED_VIS;
+    case GAL_SPECLINES_OIII_VIS:         return GAL_SPECLINES_NAME_OIII_VIS;
+    case GAL_SPECLINES_OIIIBLUE_VIS:     return 
GAL_SPECLINES_NAME_OIIIBLUE_VIS;
     case GAL_SPECLINES_HBETA:            return GAL_SPECLINES_NAME_HBETA;
-    case GAL_SPECLINES_HEIIRED:          return GAL_SPECLINES_NAME_HEIIRED;
+    case GAL_SPECLINES_HEII_VIS:         return GAL_SPECLINES_NAME_HEII_VIS;
     case GAL_SPECLINES_HGAMMA:           return GAL_SPECLINES_NAME_HGAMMA;
     case GAL_SPECLINES_HDELTA:           return GAL_SPECLINES_NAME_HDELTA;
     case GAL_SPECLINES_HEPSILON:         return GAL_SPECLINES_NAME_HEPSILON;
@@ -67,10 +67,24 @@ gal_speclines_line_name(int linecode)
     case GAL_SPECLINES_CIIIRED:          return GAL_SPECLINES_NAME_CIIIRED;
     case GAL_SPECLINES_CIII:             return GAL_SPECLINES_NAME_CIII;
     case GAL_SPECLINES_CIIIBLUE:         return GAL_SPECLINES_NAME_CIIIBLUE;
-    case GAL_SPECLINES_HEIIBLUE:         return GAL_SPECLINES_NAME_HEIIBLUE;
+    case GAL_SPECLINES_SiIIIRED:         return GAL_SPECLINES_NAME_SiIIIRED;
+    case GAL_SPECLINES_SiIII:            return GAL_SPECLINES_NAME_SiIII;
+    case GAL_SPECLINES_SiIIIBLUE:        return GAL_SPECLINES_NAME_SiIIIBLUE;
+    case GAL_SPECLINES_OIIIRED_UV:       return GAL_SPECLINES_NAME_OIIIRED_UV;
+    case GAL_SPECLINES_OIII_UV:          return GAL_SPECLINES_NAME_OIII_UV;
+    case GAL_SPECLINES_OIIIBLUE_UV:      return GAL_SPECLINES_NAME_OIIIBLUE_UV;
+    case GAL_SPECLINES_HEII_UV:          return GAL_SPECLINES_NAME_HEII_UV;
+    case GAL_SPECLINES_CIVRED:           return GAL_SPECLINES_NAME_CIVRED;
+    case GAL_SPECLINES_CIV:              return GAL_SPECLINES_NAME_CIV;
+    case GAL_SPECLINES_CIVBLUE:          return GAL_SPECLINES_NAME_CIVBLUE;
+    case GAL_SPECLINES_NV:               return GAL_SPECLINES_NAME_NV;
     case GAL_SPECLINES_LYALPHA:          return GAL_SPECLINES_NAME_LYALPHA;
+    case GAL_SPECLINES_LYBETA:           return GAL_SPECLINES_NAME_LYBETA;
+    case GAL_SPECLINES_LYGAMMA:          return GAL_SPECLINES_NAME_LYGAMMA;
+    case GAL_SPECLINES_LYDELTA:          return GAL_SPECLINES_NAME_LYDELTA;
+    case GAL_SPECLINES_LYEPSILON:        return GAL_SPECLINES_NAME_LYEPSILON;
     case GAL_SPECLINES_LYLIMIT:          return GAL_SPECLINES_NAME_LYLIMIT;
-    default:                           return NULL;
+    default: return NULL;
     }
   return NULL;
 }
@@ -97,16 +111,16 @@ gal_speclines_line_code(char *name)
     return GAL_SPECLINES_HALPHA;
   else if( !strcmp(name, GAL_SPECLINES_NAME_NIIBLUE) )
     return GAL_SPECLINES_NIIBLUE;
-  else if( !strcmp(name, GAL_SPECLINES_NAME_OIIIRED) )
-    return GAL_SPECLINES_OIIIRED;
-  else if( !strcmp(name, GAL_SPECLINES_NAME_OIII) )
-    return GAL_SPECLINES_OIII;
-  else if( !strcmp(name, GAL_SPECLINES_NAME_OIIIBLUE) )
-    return GAL_SPECLINES_OIIIBLUE;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_OIIIRED_VIS) )
+    return GAL_SPECLINES_OIIIRED_VIS;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_OIII_VIS) )
+    return GAL_SPECLINES_OIII_VIS;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_OIIIBLUE_VIS) )
+    return GAL_SPECLINES_OIIIBLUE_VIS;
   else if( !strcmp(name, GAL_SPECLINES_NAME_HBETA) )
     return GAL_SPECLINES_HBETA;
-  else if( !strcmp(name, GAL_SPECLINES_NAME_HEIIRED) )
-    return GAL_SPECLINES_HEIIRED;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_HEII_VIS) )
+    return GAL_SPECLINES_HEII_VIS;
   else if( !strcmp(name, GAL_SPECLINES_NAME_HGAMMA) )
     return GAL_SPECLINES_HGAMMA;
   else if( !strcmp(name, GAL_SPECLINES_NAME_HDELTA) )
@@ -135,10 +149,38 @@ gal_speclines_line_code(char *name)
     return GAL_SPECLINES_CIII;
   else if( !strcmp(name, GAL_SPECLINES_NAME_CIIIBLUE) )
     return GAL_SPECLINES_CIIIBLUE;
-  else if( !strcmp(name, GAL_SPECLINES_NAME_HEIIBLUE) )
-    return GAL_SPECLINES_HEIIBLUE;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_SiIIIRED) )
+    return GAL_SPECLINES_SiIIIRED;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_SiIII) )
+    return GAL_SPECLINES_SiIII;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_SiIIIBLUE) )
+    return GAL_SPECLINES_SiIIIBLUE;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_OIIIRED_UV) )
+    return GAL_SPECLINES_OIIIRED_UV;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_OIII_UV) )
+    return GAL_SPECLINES_OIII_UV;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_OIIIBLUE_UV) )
+    return GAL_SPECLINES_OIIIBLUE_UV;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_HEII_UV) )
+    return GAL_SPECLINES_HEII_UV;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_CIVRED) )
+    return GAL_SPECLINES_CIVRED;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_CIV) )
+    return GAL_SPECLINES_CIV;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_CIVBLUE) )
+    return GAL_SPECLINES_CIVBLUE;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_NV) )
+    return GAL_SPECLINES_NV;
   else if( !strcmp(name, GAL_SPECLINES_NAME_LYALPHA) )
     return GAL_SPECLINES_LYALPHA;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_LYBETA) )
+    return GAL_SPECLINES_LYBETA;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_LYGAMMA) )
+    return GAL_SPECLINES_LYGAMMA;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_LYDELTA) )
+    return GAL_SPECLINES_LYDELTA;
+  else if( !strcmp(name, GAL_SPECLINES_NAME_LYEPSILON) )
+    return GAL_SPECLINES_LYEPSILON;
   else if( !strcmp(name, GAL_SPECLINES_NAME_LYLIMIT) )
     return GAL_SPECLINES_LYLIMIT;
   else return GAL_SPECLINES_INVALID;
@@ -162,11 +204,11 @@ gal_speclines_line_angstrom(int linecode)
     case GAL_SPECLINES_NII:           return GAL_SPECLINES_ANGSTROM_NII;
     case GAL_SPECLINES_HALPHA:        return GAL_SPECLINES_ANGSTROM_HALPHA;
     case GAL_SPECLINES_NIIBLUE:       return GAL_SPECLINES_ANGSTROM_NIIBLUE;
-    case GAL_SPECLINES_OIIIRED:       return GAL_SPECLINES_ANGSTROM_OIIIRED;
-    case GAL_SPECLINES_OIII:          return GAL_SPECLINES_ANGSTROM_OIII;
-    case GAL_SPECLINES_OIIIBLUE:      return GAL_SPECLINES_ANGSTROM_OIIIBLUE;
+    case GAL_SPECLINES_OIIIRED_VIS:   return 
GAL_SPECLINES_ANGSTROM_OIIIRED_VIS;
+    case GAL_SPECLINES_OIII_VIS:      return GAL_SPECLINES_ANGSTROM_OIII_VIS;
+    case GAL_SPECLINES_OIIIBLUE_VIS:  return 
GAL_SPECLINES_ANGSTROM_OIIIBLUE_VIS;
     case GAL_SPECLINES_HBETA:         return GAL_SPECLINES_ANGSTROM_HBETA;
-    case GAL_SPECLINES_HEIIRED:       return GAL_SPECLINES_ANGSTROM_HEIIRED;
+    case GAL_SPECLINES_HEII_VIS:      return GAL_SPECLINES_ANGSTROM_HEII_VIS;
     case GAL_SPECLINES_HGAMMA:        return GAL_SPECLINES_ANGSTROM_HGAMMA;
     case GAL_SPECLINES_HDELTA:        return GAL_SPECLINES_ANGSTROM_HDELTA;
     case GAL_SPECLINES_HEPSILON:      return GAL_SPECLINES_ANGSTROM_HEPSILON;
@@ -181,11 +223,25 @@ gal_speclines_line_angstrom(int linecode)
     case GAL_SPECLINES_CIIIRED:       return GAL_SPECLINES_ANGSTROM_CIIIRED;
     case GAL_SPECLINES_CIII:          return GAL_SPECLINES_ANGSTROM_CIII;
     case GAL_SPECLINES_CIIIBLUE:      return GAL_SPECLINES_ANGSTROM_CIIIBLUE;
-    case GAL_SPECLINES_HEIIBLUE:      return GAL_SPECLINES_ANGSTROM_HEIIBLUE;
+    case GAL_SPECLINES_SiIIIRED:      return GAL_SPECLINES_ANGSTROM_SiIIIRED;
+    case GAL_SPECLINES_SiIII:         return GAL_SPECLINES_ANGSTROM_SiIII;
+    case GAL_SPECLINES_SiIIIBLUE:     return GAL_SPECLINES_ANGSTROM_SiIIIBLUE;
+    case GAL_SPECLINES_OIIIRED_UV:    return GAL_SPECLINES_ANGSTROM_OIIIRED_UV;
+    case GAL_SPECLINES_OIII_UV:       return GAL_SPECLINES_ANGSTROM_OIII_UV;
+    case GAL_SPECLINES_OIIIBLUE_UV:   return 
GAL_SPECLINES_ANGSTROM_OIIIBLUE_UV;
+    case GAL_SPECLINES_HEII_UV:       return GAL_SPECLINES_ANGSTROM_HEII_UV;
+    case GAL_SPECLINES_CIVRED:        return GAL_SPECLINES_ANGSTROM_CIVRED;
+    case GAL_SPECLINES_CIV:           return GAL_SPECLINES_ANGSTROM_CIV;
+    case GAL_SPECLINES_CIVBLUE:       return GAL_SPECLINES_ANGSTROM_CIVBLUE;
+    case GAL_SPECLINES_NV:            return GAL_SPECLINES_ANGSTROM_NV;
     case GAL_SPECLINES_LYALPHA:       return GAL_SPECLINES_ANGSTROM_LYALPHA;
+    case GAL_SPECLINES_LYBETA:        return GAL_SPECLINES_ANGSTROM_LYBETA;
+    case GAL_SPECLINES_LYGAMMA:       return GAL_SPECLINES_ANGSTROM_LYGAMMA;
+    case GAL_SPECLINES_LYDELTA:       return GAL_SPECLINES_ANGSTROM_LYDELTA;
+    case GAL_SPECLINES_LYEPSILON:     return GAL_SPECLINES_ANGSTROM_LYEPSILON;
     case GAL_SPECLINES_LYLIMIT:       return GAL_SPECLINES_ANGSTROM_LYLIMIT;
     default:
-      error(EXIT_FAILURE, 0, "%s: `%d' not recognized line identifier",
+      error(EXIT_FAILURE, 0, "%s: '%d' not recognized line identifier",
             __func__, linecode);
     }
   return NAN;
diff --git a/lib/statistics.c b/lib/statistics.c
index c942126..4bc94c7 100644
--- a/lib/statistics.c
+++ b/lib/statistics.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -55,7 +55,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
  ********               Simple statistics                 *******
  ****************************************************************/
 /* Return the number of non-blank elements in an array as a single element,
-   `size_t' type data structure. */
+   'size_t' type data structure. */
 gal_data_t *
 gal_statistics_number(gal_data_t *input)
 {
@@ -65,7 +65,7 @@ gal_statistics_number(gal_data_t *input)
 
   /* If there is no blank values in the input, then the total number is
      just the size. */
-  if(gal_blank_present(input, 0)) /* `{}' necessary for `else'. */
+  if(gal_blank_present(input, 0)) /* '{}' necessary for 'else'. */
     { GAL_TILE_PARSE_OPERATE(input, NULL, 0, 1, {++counter;}); }
   else
     counter = input->size;
@@ -149,7 +149,7 @@ gal_statistics_sum(gal_data_t *input)
 
   /* See if the input actually has any elements. */
   if(input->size)
-    /* Parse the dataset. Note that in `gal_data_alloc' we set the `clear'
+    /* Parse the dataset. Note that in 'gal_data_alloc' we set the 'clear'
        flag to 1, so it will be 0.0f. */
     GAL_TILE_PARSE_OPERATE(input, out, 0, 1, {++n; *o += *i;});
 
@@ -174,12 +174,12 @@ gal_statistics_mean(gal_data_t *input)
 
   /* See if the input actually has any elements. */
   if(input->size)
-    /* Parse the dataset. Note that in `gal_data_alloc' we set the `clear'
+    /* Parse the dataset. Note that in 'gal_data_alloc' we set the 'clear'
        flag to 1, so it will be 0.0f. */
     GAL_TILE_PARSE_OPERATE(input, out, 0, 1, {++n; *o += *i;});
 
   /* Above, we calculated the sum and number, so if there were any elements
-     in the dataset (`n!=0'), divide the sum by the number, otherwise, put
+     in the dataset ('n!=0'), divide the sum by the number, otherwise, put
      a blank value in the output. */
   if(n) *((double *)(out->array)) /= n;
   else gal_blank_write(out->array, out->type);
@@ -196,17 +196,30 @@ gal_data_t *
 gal_statistics_std(gal_data_t *input)
 {
   size_t dsize=1, n=0;
-  double s=0.0f, s2=0.0f;
+  double s=0.0f, s2=0.0f, *o;
   gal_data_t *out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &dsize,
                                  NULL, 1, -1, 1, NULL, NULL, NULL);
 
   /* See if the input actually has any elements. */
-  if(input->size)
-    /* Parse the input. */
-    GAL_TILE_PARSE_OPERATE(input, out, 0, 1, {++n; s += *i; s2 += *i * *i;});
+  o=out->array;
+  switch(input->size)
+    {
+    /* No inputs. */
+    case 0: o[0]=GAL_BLANK_FLOAT64; break;
+
+    /* When we only have a single element, theoretically the standard
+       deviation should be 0. But due to floating-point errors, it will
+       probably not be. So we'll manually set it to zero. */
+    case 1: o[0]=0; break;
 
-  /* Write the standard deviation into the output. */
-  *((double *)(out->array)) = n ? sqrt( (s2-s*s/n)/n ) : GAL_BLANK_FLOAT64;
+    /* More than one element. */
+    default:
+      GAL_TILE_PARSE_OPERATE(input, out, 0, 1, {++n; s += *i; s2 += *i * *i;});
+      o[0]=sqrt( (s2-s*s/n)/n );
+      break;
+    }
+
+  /* Return the output dataset. */
   return out;
 }
 
@@ -221,17 +234,34 @@ gal_data_t *
 gal_statistics_mean_std(gal_data_t *input)
 {
   size_t dsize=2, n=0;
-  double s=0.0f, s2=0.0f;
+  double s=0.0f, s2=0.0f, *o;
   gal_data_t *out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &dsize,
                                  NULL, 1, -1, 1, NULL, NULL, NULL);
+
   /* See if the input actually has any elements. */
-  if(input->size)
-    /* Parse the input. */
-    GAL_TILE_PARSE_OPERATE(input, out, 0, 1, {++n; s += *i; s2 += *i * *i;});
+  o=out->array;
+  switch(input->size)
+    {
+    /* No inputs. */
+    case 0: o[0]=o[1]=GAL_BLANK_FLOAT64; break;
 
-  /* Write in the output values and return. */
-  ((double *)(out->array))[0] = n ? s/n                  : GAL_BLANK_FLOAT64;
-  ((double *)(out->array))[1] = n ? sqrt( (s2-s*s/n)/n ) : GAL_BLANK_FLOAT64;
+    /* When we only have a single element, theoretically the standard
+       deviation should be 0. But due to floating-point errors, it will
+       probably not be. So we'll manually set it to zero. */
+    case 1:
+      GAL_TILE_PARSE_OPERATE(input, out, 0, 1, {s+=*i;});
+      o[0]=s; o[1]=0;
+      break;
+
+    /* More than one element. */
+    default:
+      GAL_TILE_PARSE_OPERATE(input, out, 0, 1, {++n; s += *i; s2 += *i * *i;});
+      o[0]=s/n;
+      o[1]=sqrt( (s2-s*s/n)/n );
+      break;
+    }
+
+  /* Return the output dataset. */
   return out;
 }
 
@@ -241,7 +271,7 @@ gal_statistics_mean_std(gal_data_t *input)
 
 /* The input is a sorted array with no blank values, we want the median
    value to be put inside the already allocated space which is pointed to
-   by `median'. It is in the same type as the input. */
+   by 'median'. It is in the same type as the input. */
 #define MED_IN_SORTED(IT) {                                             \
     IT *a=sorted->array;                                                \
     *(IT *)median = n%2 ? a[n/2]  : (a[n/2]+a[n/2-1])/2;                \
@@ -278,7 +308,7 @@ statistics_median_in_sorted_no_blank(gal_data_t *sorted, 
void *median)
 
 
 /* Return the median value of the dataset in the same type as the input as
-   a one element dataset. If the `inplace' flag is set, the input data
+   a one element dataset. If the 'inplace' flag is set, the input data
    structure will be modified: it will have no blank values and will be
    sorted (increasing). */
 gal_data_t *
@@ -313,7 +343,7 @@ gal_statistics_quantile_index(size_t size, double quantile)
   /* Some sanity checks. */
   if(size==0)
     {
-      error(0, 0, "%s: `size' is 0. The quantile is not defined for "
+      error(0, 0, "%s: 'size' is 0. The quantile is not defined for "
               "a zero-sized array\n", __func__);
       return GAL_BLANK_SIZE_T;
     }
@@ -390,7 +420,7 @@ gal_statistics_quantile(gal_data_t *input, double quantile, 
int inplace)
 
 
 /* Return the index of the (first) point in the sorted dataset that has the
-   closest value to `value' (which has to be the same type as the `input'
+   closest value to 'value' (which has to be the same type as the 'input'
    dataset). */
 #define STATS_QFUNC_IND(IT) {                                           \
     IT *r, *a=nbs->array, *af=a+nbs->size, v=*((IT *)(value->array));   \
@@ -400,7 +430,7 @@ gal_statistics_quantile(gal_data_t *input, double quantile, 
int inplace)
     r=a++;                                                              \
                                                                         \
     /* Increasing array: */                                             \
-    if( *a < *(a+1) )                                                   \
+    if( nbs->flag & GAL_DATA_FLAG_SORTED_I )                            \
       {                                                                 \
         if( v>=*r )                                                     \
           {                                                             \
@@ -425,17 +455,21 @@ gal_statistics_quantile(gal_data_t *input, double 
quantile, int inplace)
     if(parsed && a<af) index = a-r;                                     \
   }
 size_t
-gal_statistics_quantile_function_index(gal_data_t *input, gal_data_t *value,
-                                       int inplace)
+gal_statistics_quantile_function_index(gal_data_t *input,
+                                       gal_data_t *invalue, int inplace)
 {
   int parsed=0;
+  gal_data_t *value;
   size_t index=GAL_BLANK_SIZE_T;
   gal_data_t *nbs=gal_statistics_no_blank_sorted(input, inplace);
 
-  /* A sanity check. */
-  if(nbs->type!=value->type)
-    error(EXIT_FAILURE, 0, "%s: the types of the input dataset and requested "
-          "value have to be the same", __func__);
+  /* Make sure the value has the same type. */
+  if(invalue->size>1)
+    error(EXIT_FAILURE, 0, "%s: the 'value' argument must only have "
+          "one element", __func__);
+  value = ( (nbs->type==invalue->type)
+            ? invalue
+            : gal_data_copy_to_new_type(invalue, nbs->type) );
 
   /* Only continue processing if we have non-blank elements. */
   if(nbs->size)
@@ -464,6 +498,7 @@ gal_statistics_quantile_function_index(gal_data_t *input, 
gal_data_t *value,
     }
 
   /* Clean up and return. */
+  if(value!=invalue) gal_data_free(value);
   if(nbs!=input) gal_data_free(nbs);
   return index;
 }
@@ -489,11 +524,19 @@ gal_statistics_quantile_function(gal_data_t *input, 
gal_data_t *value,
                                  int inplace)
 {
   double *d;
-  size_t dsize=1;
+  size_t ind, dsize=1;
   gal_data_t *nbs=gal_statistics_no_blank_sorted(input, inplace);
   gal_data_t *out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &dsize,
                                  NULL, 1, -1, 1, NULL, NULL, NULL);
-  size_t ind=gal_statistics_quantile_function_index(input, value, inplace);
+
+  /* Sanity checks. */
+  if(value->size>1)
+    error(EXIT_FAILURE, 0, "%s: the 'value' argument must only have "
+          "one element", __func__);
+
+  /* Calculate the index of the value. */
+  ind=gal_statistics_quantile_function_index(input, value, inplace);
+  //printf("ind: %zu (%zu)\n", ind, input->size);
 
   /* Only continue processing if there are non-blank values. */
   if(nbs->size)
@@ -537,6 +580,64 @@ gal_statistics_quantile_function(gal_data_t *input, 
gal_data_t *value,
 
 
 
+/* Pull out unique elements */
+#define UNIQUE_BYTYPE(TYPE) {                                           \
+    size_t i, j;                                                        \
+    TYPE *a=out->array, b;                                              \
+                                                                        \
+    /* Write the blank value for this type into 'b'. */                 \
+    gal_blank_write(&b, out->type);                                     \
+                                                                        \
+    /* Go over the elements, and set the duplicates to blank. */        \
+    /* Note that for integers and floats, the behavior of blank/NaN */  \
+    /* differs: for floats (NaN), we can identify a blank using the  */ \
+    /* fact that by definition, NaN!=NaN. */                            \
+    if(b==b)                                                            \
+      for(i=0;i<out->size;++i)                                          \
+        { if(a[i]!=b)    for(j=i+1;j<out->size;++j) if(a[i]==a[j]) a[j]=b;} \
+    else                                                                \
+      for(i=0;i<out->size;++i)                                          \
+        { if(a[i]==a[i]) for(j=i+1;j<out->size;++j) if(a[i]==a[j]) a[j]=b;} \
+  }
+
+gal_data_t *
+gal_statistics_unique(gal_data_t *input, int inplace)
+{
+  gal_data_t *out = inplace ? input : gal_data_copy(input);
+
+  /* Since we are replacing the repeated elements with blank, re-set the
+     blank flags. */
+  out->flag &= ~GAL_DATA_FLAG_BLANK_CH; /* Set bit to 0. */
+  out->flag &= ~GAL_DATA_FLAG_HASBLANK; /* Set bit to 0. */
+
+  /* Set all non-unique elements to blank. */
+  switch(out->type)
+    {
+    case GAL_TYPE_UINT8:   UNIQUE_BYTYPE( uint8_t  ); break;
+    case GAL_TYPE_INT8:    UNIQUE_BYTYPE( int8_t   ); break;
+    case GAL_TYPE_UINT16:  UNIQUE_BYTYPE( uint16_t ); break;
+    case GAL_TYPE_INT16:   UNIQUE_BYTYPE( int16_t  ); break;
+    case GAL_TYPE_UINT32:  UNIQUE_BYTYPE( uint32_t ); break;
+    case GAL_TYPE_INT32:   UNIQUE_BYTYPE( int32_t  ); break;
+    case GAL_TYPE_UINT64:  UNIQUE_BYTYPE( uint64_t ); break;
+    case GAL_TYPE_INT64:   UNIQUE_BYTYPE( int64_t  ); break;
+    case GAL_TYPE_FLOAT32: UNIQUE_BYTYPE( float    ); break;
+    case GAL_TYPE_FLOAT64: UNIQUE_BYTYPE( double   ); break;
+    default:
+      error(EXIT_FAILURE, 0, "the 'unique' operator doesn't support type "
+            "code '%u'", out->type);
+    }
+
+  /* Remove all blank elements (note that 'gal_blank_remove' also corrects
+     the size of the dataset and sets it to 1D). */
+  gal_blank_remove_realloc(out);
+  return out;
+}
+
+
+
+
+
 
 
 
@@ -586,7 +687,7 @@ struct statistics_mode_params
 
 
 /*
-  Given a mirror point (`m'), return the maximum distance between the
+  Given a mirror point ('m'), return the maximum distance between the
   mirror distribution and the original distribution.
 
   The basic idea behind finding the mode is comparing the mirrored CDF
@@ -596,28 +697,28 @@ struct statistics_mode_params
   checked, it then finds the maximum difference between the mirrored CDF
   about the given point and the input CDF.
 
-  `zf` keeps the value at the mirror (zero) point.  `i` is used to count
-  the pixels after the mirror in the mirror distribution. So `m+i` is the
+  'zf' keeps the value at the mirror (zero) point.  'i' is used to count
+  the pixels after the mirror in the mirror distribution. So 'm+i' is the
   index of the mirrored distribution and mf=zf+(zf-a[m-i])=2*zf-a[m-i] is
-  the mirrored flux at this point. Having found `mf', we find the `j` such
-  that a[m+j] has the nearest flux to `mf`.
+  the mirrored flux at this point. Having found 'mf', we find the 'j' such
+  that a[m+j] has the nearest flux to 'mf'.
 
   The desired difference between the input CDF and the mirrored one
-  for each `i` is then simply: `j-i`.
-
-  Once `i` is incremented, `mf` will increase, so to find the new `j` we
-  don't need to begin looking from `j=0`. Remember that the array is
-  sorted, so the desired `j` is definitely larger than the previous
-  `j`. So, if we keep the previous `j` in `prevj` then, all we have to do
-  is to start incrementing `j` from `prevj`. This will really help in
-  speeding up the job :-D. Only for the first element, `prevj=0`. */
+  for each 'i' is then simply: 'j-i'.
+
+  Once 'i' is incremented, 'mf' will increase, so to find the new 'j' we
+  don't need to begin looking from 'j=0'. Remember that the array is
+  sorted, so the desired 'j' is definitely larger than the previous
+  'j'. So, if we keep the previous 'j' in 'prevj' then, all we have to do
+  is to start incrementing 'j' from 'prevj'. This will really help in
+  speeding up the job :-D. Only for the first element, 'prevj=0'. */
 #define MIRR_MAX_DIFF(IT) {                                             \
     IT *a=p->data->array, zf=a[m], mf=2*zf-a[m-i];                      \
                                                                         \
     /* When a[m+j]>mf, we have reached the last pixel to check. Now, */ \
     /* we just have to see which one of a[m+j-1] or a[m+j] is closer */ \
-    /* to `mf'. We then change `j` accordingly and break out of the  */ \
-    /* `j' loop. */                                                     \
+    /* to 'mf'. We then change 'j' accordingly and break out of the  */ \
+    /* 'j' loop. */                                                     \
     for(j=prevj;j<size-m;++j)                                           \
       if(a[m+j]>mf)                                                     \
         {                                                               \
@@ -653,7 +754,7 @@ mode_mirror_max_index_diff(struct statistics_mode_params 
*p, size_t m)
   /* Go over the mirrored points. */
   for(i=1; i<p->numcheck && i<=m && m+i<size ;i+=p->interval)
     {
-      /* Find `j': the index of the closest point in the original
+      /* Find 'j': the index of the closest point in the original
          distribution that has a value similar to the mirror
          distribution. */
       switch(p->data->type)
@@ -682,8 +783,8 @@ mode_mirror_max_index_diff(struct statistics_mode_params 
*p, size_t m)
          has been found. We want the mirrored distribution to be within the
          actual distribution, not beyond it, so the only acceptable results
          are when i<j. But we also have noise, so we can't simply use that
-         as the criterion, small `j's with `i>j' are acceptable. So, only
-         when `i>j+errordiff' the result is not acceptable! */
+         as the criterion, small 'j's with 'i>j' are acceptable. So, only
+         when 'i>j+errordiff' the result is not acceptable! */
       if(i>j+errordiff)
         {
           maxdiff = MODE_MIRROR_ABOVE;
@@ -704,7 +805,7 @@ mode_mirror_max_index_diff(struct statistics_mode_params 
*p, size_t m)
 
 
 /* Find the mode through the Golden-section search. It is assumed that
-   `mode_mirror_max_index_diff' has one minimum (within the statistical
+   'mode_mirror_max_index_diff' has one minimum (within the statistical
    errors) in the function. To find that minimum, the golden section search
    algorithm is going to used. Read the Wikipedia article for a very nice
    introduction.
@@ -713,8 +814,8 @@ mode_mirror_max_index_diff(struct statistics_mode_params 
*p, size_t m)
    interval and thus decreasing the interval until a certain tolerance is
    reached.
 
-   If the input interval is on points `a' and `b', then the middle point
-   (lets call it `c', where c>a and c<b) to test should be positioned such
+   If the input interval is on points 'a' and 'b', then the middle point
+   (lets call it 'c', where c>a and c<b) to test should be positioned such
    that (b-c)/(c-a)=MODE_GOLDEN_RATIO. Once we open up this relation, we
    can find c using:
 
@@ -768,7 +869,7 @@ mode_golden_section(struct statistics_mode_params *p)
   /* +++++++++++++ Start of addition to the golden section search.
 
      The mirrored distribution's cumulative frequency plot has be lower
-     than the actual's cfp. If it isn't, `di` will be MODE_MIRROR_ABOVE. In
+     than the actual's cfp. If it isn't, 'di' will be MODE_MIRROR_ABOVE. In
      this case, the normal golden section minimization is not going to give
      us what we want. So we have this modification. In such cases, we want
      the search to go to the lower interval. */
@@ -828,28 +929,28 @@ mode_golden_section(struct statistics_mode_params *p)
 
 /* Once the mode is found, we need to do a quality control. This quality
    control is the measure of its symmetricity. Let's assume the mode index
-   is at `m', since an index is just a count, from the Poisson
-   distribution, the error in `m' is sqrt(m).
+   is at 'm', since an index is just a count, from the Poisson
+   distribution, the error in 'm' is sqrt(m).
 
-   Now, let's take `b' to be the first point that the difference between
+   Now, let's take 'b' to be the first point that the difference between
    the cumulative distribution of the mirror and actual data deviate more
    than sqrt(m). For a scale parameter, lets assume that the index of 5% of
-   `m` is `a`. We could have taken the distribution minimum, but the
+   'm' is 'a'. We could have taken the distribution minimum, but the
    scatter in the minimum can be too high!
 
    Now, the "symmetricity" of the mode can be defined as: (b-m)/(m-a). For
    a completly symmetric mode, this should be 1. Note that the search for
-   `b` only goes to the 95% of the distribution.  */
+   'b' only goes to the 95% of the distribution.  */
 #define MODE_SYM(IT) {                                                  \
     IT *a=p->data->array, af=0, bf=0, mf=0, fi;                         \
                                                                         \
-    /* Set the values at the mirror and at `a' (see above). */          \
+    /* Set the values at the mirror and at 'a' (see above). */          \
     mf=a[m];                                                            \
     af=a[ gal_statistics_quantile_index(2*m+1, MODE_SYM_LOW_Q) ];       \
     if(mf<=af) return 0;                                                \
                                                                         \
     /* This loop is very similar to that of */                          \
-    /* `mode_mirror_max_index_diff'. It will find the index where the */\
+    /* 'mode_mirror_max_index_diff'. It will find the index where the */\
     /* difference between the two cumulative frequency plots exceeds */ \
     /* that of the error in the mirror index.*/                         \
     for(i=1; i<topi-m ;i+=1)                                            \
@@ -924,7 +1025,7 @@ mode_symmetricity(struct statistics_mode_params *p, size_t 
m, void *b_val)
 
 
 
-/* Return the mode and related parameters in a float64 `gal_data_t' with
+/* Return the mode and related parameters in a float64 'gal_data_t' with
    the following elements in its array, the array:
 
       array[0]: mode
@@ -934,13 +1035,13 @@ mode_symmetricity(struct statistics_mode_params *p, 
size_t m, void *b_val)
 
   The inputs are:
 
-    - `input' is the input dataset, it doesn't have to be sorted and can
+    - 'input' is the input dataset, it doesn't have to be sorted and can
       have blank values.
 
-    - `mirrordist' is the maximum distance after the mirror point to check
+    - 'mirrordist' is the maximum distance after the mirror point to check
       as a multiple of sigma.
 
-    - `inplace' is either 0 or 1. If it is 1 and the input array has blank
+    - 'inplace' is either 0 or 1. If it is 1 and the input array has blank
       values and is not sorted, then the removal of blank values and
       sorting will occur in-place (input will be modified): all blank
       elements in the input array will be removed and it will be sorted. */
@@ -963,7 +1064,7 @@ gal_statistics_mode(gal_data_t *input, float mirrordist, 
int inplace)
   /* A small sanity check. */
   if(mirrordist<=0)
     error(EXIT_FAILURE, 0, "%s: %f not acceptable as a value to "
-          "`mirrordist'. Only positive values can be given to it",
+          "'mirrordist'. Only positive values can be given to it",
           __func__, mirrordist);
 
 
@@ -972,7 +1073,7 @@ gal_statistics_mode(gal_data_t *input, float mirrordist, 
int inplace)
 
 
   /* It can happen that the whole array is blank. In such cases,
-     `p.data->size==0', so set all output elements to NaN and return. */
+     'p.data->size==0', so set all output elements to NaN and return. */
   oa=out->array;
   if(p.data->size==0) { oa[0]=oa[1]=oa[2]=oa[3]=NAN; return out; }
 
@@ -1004,7 +1105,7 @@ gal_statistics_mode(gal_data_t *input, float mirrordist, 
int inplace)
 
 
   /* Do the golden-section search iteration, read the mode value from the
-     input array and save it in the `tmptype' data structure that has the
+     input array and save it in the 'tmptype' data structure that has the
      same type as the input. */
   modeindex = mode_golden_section(&p);
   memcpy( tmptype->array,
@@ -1096,7 +1197,7 @@ statistics_make_mirror(gal_data_t *noblank_sorted, size_t 
index,
 
 
 /* Make a mirrored histogram and cumulative frequency plot with the mirror
-   distribution of the input with a value at `value'.
+   distribution of the input with a value at 'value'.
 
    The output is a linked list of data structures: the first is the bins
    with one bin at the mirror point, the second is the histogram with a
@@ -1251,7 +1352,7 @@ gal_statistics_is_sorted(gal_data_t *input, int 
updateflags)
 
         default:
           error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
-                "the problem. The value %d is not recognized for `out'",
+                "the problem. The value %d is not recognized for 'out'",
                 __func__, PACKAGE_BUGREPORT, out);
         }
     }
@@ -1263,7 +1364,7 @@ gal_statistics_is_sorted(gal_data_t *input, int 
updateflags)
 
 
 /* This function is ignorant to blank values, if you want to make sure
-   there is no blank values, you can call `gal_blank_remove' first. */
+   there is no blank values, you can call 'gal_blank_remove' first. */
 #define STATISTICS_SORT(QSORT_F) {                                      \
     qsort(input->array, input->size, gal_type_sizeof(input->type), QSORT_F); \
   }
@@ -1309,7 +1410,7 @@ gal_statistics_sort_increasing(gal_data_t *input)
 
 
 
-/* See explanations above `gal_statistics_sort_increasing'. */
+/* See explanations above 'gal_statistics_sort_increasing'. */
 void
 gal_statistics_sort_decreasing(gal_data_t *input)
 {
@@ -1353,12 +1454,12 @@ gal_statistics_sort_decreasing(gal_data_t *input)
 
 
 /* Return a dataset that doesn't have blank values and is sorted. If the
-   `inplace' value is set to 1, then the input array will be modified,
+   'inplace' value is set to 1, then the input array will be modified,
    otherwise, a new array will be allocated with the desired properties. So
-   if it is already sorted and has blank values, the `inplace' variable is
+   if it is already sorted and has blank values, the 'inplace' variable is
    irrelevant.
 
-   This function can also work on tiles, in that case, `inplace' is
+   This function can also work on tiles, in that case, 'inplace' is
    useless, because a tile doesn't own its dataset and the dataset is not
    contiguous. */
 gal_data_t *
@@ -1372,7 +1473,7 @@ gal_statistics_no_blank_sorted(gal_data_t *input, int 
inplace)
     {
       /* If this is a tile, then first we have to copy it into a contiguous
          piece of memory. After this step, we will only be dealing with
-         `contig' (for a contiguous patch of memory). */
+         'contig' (for a contiguous patch of memory). */
       if(input->block)
         {
           /* Copy the input into a contiguous patch of memory. */
@@ -1380,15 +1481,15 @@ gal_statistics_no_blank_sorted(gal_data_t *input, int 
inplace)
 
           /* When the data was a tile, we have already copied the array
              into a separate allocated space. So to avoid any further
-             copying, we will just set the `inplace' variable to 1. */
+             copying, we will just set the 'inplace' variable to 1. */
           inplace=1;
         }
       else contig=input;
 
 
       /* Make sure there are no blanks in the array that will be
-         used. After this step, we won't be dealing with `input' any more,
-         but with `noblank'. */
+         used. After this step, we won't be dealing with 'input' any more,
+         but with 'noblank'. */
       if( gal_blank_present(contig, 1) )
         {
           /* See if we should allocate a new dataset to remove blanks or if
@@ -1399,7 +1500,7 @@ gal_statistics_no_blank_sorted(gal_data_t *input, int 
inplace)
       else noblank=contig;
 
       /* Make sure the array is sorted. After this step, we won't be
-         dealing with `noblank' any more but with `sorted'. */
+         dealing with 'noblank' any more but with 'sorted'. */
       if(noblank->size)
         {
           if( gal_statistics_is_sorted(noblank, 1) )
@@ -1474,21 +1575,21 @@ gal_statistics_no_blank_sorted(gal_data_t *input, int 
inplace)
 
    Input arguments:
 
-     * The `input' set you want to apply the bins to. This is only
+     * The 'input' set you want to apply the bins to. This is only
        necessary if the range argument is not complete, see below. If
-       `range' has all the necessary information, you can pass a NULL
-       pointer for `input'.
+       'range' has all the necessary information, you can pass a NULL
+       pointer for 'input'.
 
-     * The `inrange' data structure keeps the desired range along each
+     * The 'inrange' data structure keeps the desired range along each
        dimension of the input data structure, it has to be in float32
-       type. Note that if
+       type. Note these points:
 
          - If you want the full range of the dataset (in any dimensions,
-           then just set `range' to NULL and the range will be specified
+           then just set 'range' to NULL and the range will be specified
            from the minimum and maximum value of the dataset.
 
          - If there is one element for each dimension in range, then it is
-           viewed as a quantile (Q), and the range will be: `Q to 1-Q'.
+           viewed as a quantile (Q), and the range will be: 'Q to 1-Q'.
 
          - If there are two elements for each dimension in range, then they
            are assumed to be your desired minimum and maximum values. When
@@ -1497,7 +1598,7 @@ gal_statistics_no_blank_sorted(gal_data_t *input, int 
inplace)
 
      * The number of bins: must be larger than 0.
 
-     * `onebinstart' A desired value for onebinstart. Note that with this
+     * 'onebinstart' A desired value for onebinstart. Note that with this
         option, the bins won't start and end exactly on the given range
         values, it will be slightly shifted to accommodate this
         request.
@@ -1516,7 +1617,7 @@ gal_statistics_regular_bins(gal_data_t *input, gal_data_t 
*inrange,
 
   /* Some sanity checks. */
   if(numbins==0)
-    error(EXIT_FAILURE, 0, "%s: `numbins' cannot be given a value of 0",
+    error(EXIT_FAILURE, 0, "%s: 'numbins' cannot be given a value of 0",
           __func__);
   if(input->size==0)
     error(EXIT_FAILURE, 0, "%s: input's size is 0", __func__);
@@ -1562,7 +1663,7 @@ gal_statistics_regular_bins(gal_data_t *input, gal_data_t 
*inrange,
           else max=ra[1];
         }
 
-      /* Clean up: if `range' was allocated. */
+      /* Clean up: if 'range' was allocated. */
       if(range!=inrange) gal_data_free(range);
     }
   /* No range was given, find the minimum and maximum. */
@@ -1626,11 +1727,11 @@ gal_statistics_regular_bins(gal_data_t *input, 
gal_data_t *inrange,
 
 
 /* Make a histogram of all the elements in the given dataset with bin
-   values that are defined in the `inbins' structure (see
-   `gal_statistics_regular_bins'). `inbins' is not mandatory, if you pass a
+   values that are defined in the 'inbins' structure (see
+   'gal_statistics_regular_bins'). 'inbins' is not mandatory, if you pass a
    NULL pointer, the bins structure will be built within this function
-   based on the `numbins' input. As a result, when you have already defined
-   the bins, `numbins' is not used. */
+   based on the 'numbins' input. As a result, when you have already defined
+   the bins, 'numbins' is not used. */
 
 #define HISTOGRAM_TYPESET(IT) {                                         \
     IT *a=input->array, *af=a+input->size;                              \
@@ -1638,8 +1739,8 @@ gal_statistics_regular_bins(gal_data_t *input, gal_data_t 
*inrange,
       if(*a>=min && *a<=max)                                            \
         {                                                               \
           h_i=(*a-min)/binwidth;                                        \
-          /* When `*a' is the largest element (within floating point */ \
-          /* errors), `h_i' can be one element larger than the       */ \
+          /* When '*a' is the largest element (within floating point */ \
+          /* errors), 'h_i' can be one element larger than the       */ \
           /* number of bins. But since its in the dataset, we need   */ \
           /* to count it. So we'll put it in the last bin.           */ \
           ++h[ h_i - (h_i==hist->size ? 1 : 0) ];                       \
@@ -1661,7 +1762,7 @@ gal_statistics_histogram(gal_data_t *input, gal_data_t 
*bins, int normalize,
      either use the old implementation, or GSL's histogram
      functionality. */
   if(bins==NULL)
-    error(EXIT_FAILURE, 0, "%s: `bins' is NULL", __func__);
+    error(EXIT_FAILURE, 0, "%s: 'bins' is NULL", __func__);
   if(bins->status!=GAL_STATISTICS_BINS_REGULAR)
     error(EXIT_FAILURE, 0, "%s: the input bins are not regular. Currently "
           "it is only implemented for regular bins", __func__);
@@ -1669,9 +1770,9 @@ gal_statistics_histogram(gal_data_t *input, gal_data_t 
*bins, int normalize,
     error(EXIT_FAILURE, 0, "%s: input's size is 0", __func__);
 
 
-  /* Check if normalize and `maxone' are not called together. */
+  /* Check if normalize and 'maxone' are not called together. */
   if(normalize && maxone)
-    error(EXIT_FAILURE, 0, "%s: only one of `normalize' and `maxone' may "
+    error(EXIT_FAILURE, 0, "%s: only one of 'normalize' and 'maxone' may "
           "be given", __func__);
 
 
@@ -1764,15 +1865,156 @@ gal_statistics_histogram(gal_data_t *input, gal_data_t 
*bins, int normalize,
 
 
 
+/* Build a 2D histogram from the two input columns (a list) and two bins
+   (also a list). */
+#define HISTOGRAM2D_TYPESET(AT, BT) {                                   \
+    BT *b=input->next->array;                                           \
+    AT *a=input->array, *af=a+input->size;                              \
+    do                                                                  \
+      {                                                                 \
+        if(*a>=mina && *a<=maxa && *b>=minb && *b<=maxb)                \
+          {                                                             \
+            i=(*a-mina)/binwidtha;                                      \
+            j=(*b-minb)/binwidthb;                                      \
+            /* When '*a' is the largest element (within floating */     \
+            /* point errors), 'ii' can be one element larger than */    \
+            /* the number of bins. But since its in the dataset, we */  \
+            /* need to count it. So we'll put it in the last bin. */    \
+            if(i==bsizea) --i;                                          \
+            if(j==bsizeb) --j;                                          \
+            ++h[ i*bsizeb+j ];                                          \
+          }                                                             \
+        ++b;                                                            \
+      }                                                                 \
+    while(++a<af);                                                      \
+  }
+
+#define HISTOGRAM2D_TYPESET_A(AT) {                                     \
+    switch(input->next->type)                                           \
+      {                                                                 \
+      case GAL_TYPE_UINT8:    HISTOGRAM2D_TYPESET(AT, uint8_t);  break; \
+      case GAL_TYPE_INT8:     HISTOGRAM2D_TYPESET(AT, int8_t);   break; \
+      case GAL_TYPE_UINT16:   HISTOGRAM2D_TYPESET(AT, uint16_t); break; \
+      case GAL_TYPE_INT16:    HISTOGRAM2D_TYPESET(AT, int16_t);  break; \
+      case GAL_TYPE_UINT32:   HISTOGRAM2D_TYPESET(AT, uint32_t); break; \
+      case GAL_TYPE_INT32:    HISTOGRAM2D_TYPESET(AT, int32_t);  break; \
+      case GAL_TYPE_UINT64:   HISTOGRAM2D_TYPESET(AT, uint64_t); break; \
+      case GAL_TYPE_INT64:    HISTOGRAM2D_TYPESET(AT, int64_t);  break; \
+      case GAL_TYPE_FLOAT32:  HISTOGRAM2D_TYPESET(AT, float);    break; \
+      case GAL_TYPE_FLOAT64:  HISTOGRAM2D_TYPESET(AT, double);   break; \
+      default:                                                          \
+        error(EXIT_FAILURE, 0, "%s: type code %d not recognized",       \
+              __func__, input->type);                                   \
+      }                                                                 \
+  }
+
+gal_data_t *
+gal_statistics_histogram2d(gal_data_t *input, gal_data_t *bins)
+{
+  uint32_t *h;
+  double *o1, *o2;
+  gal_data_t *tmp, *out;
+  size_t i, j, bsizea, bsizeb, outsize;
+  double *da, *db, binwidtha, binwidthb, mina, minb, maxa, maxb;
+
+  /* Basic sanity checks */
+  if(input->next==NULL)
+    error(EXIT_FAILURE, 0, "%s: 'input' has to be a list of two datasets",
+          __func__);
+  if(bins->next==NULL)
+    error(EXIT_FAILURE, 0, "%s: 'bins' has to be a list of two datasets",
+          __func__);
+  if(input->next->next)
+    error(EXIT_FAILURE, 0, "%s: 'input' should only contain two datasets, "
+          "not more", __func__);
+  if(bins->next->next)
+    error(EXIT_FAILURE, 0, "%s: 'bins' should only contain two datasets, "
+          "not more", __func__);
+  if(input->size != input->next->size)
+    error(EXIT_FAILURE, 0, "the two input datasets have to have the "
+          "same size");
+  if(bins->status!=GAL_STATISTICS_BINS_REGULAR
+     || bins->next->status!=GAL_STATISTICS_BINS_REGULAR)
+    error(EXIT_FAILURE, 0, "%s: the input bins are not regular. Currently "
+          "it is only implemented for regular bins", __func__);
+
+  /* For easy reading of bin sizes. */
+  da=bins->array;
+  bsizea=bins->size;
+  db=bins->next->array;
+  bsizeb=bins->next->size;
+
+  /* Allocate the output. */
+  outsize=bsizea*bsizeb;
+  out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &outsize,
+                     NULL, 1, input->minmapsize, input->quietmmap,
+                     "bin_dim1", input->unit,
+                     "Bin centers along first axis.");
+  tmp=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &outsize,
+                     NULL, 1, input->minmapsize, input->quietmmap,
+                     "bin_dim2", input->next->unit,
+                     "Bin centers along second axis.");
+  out->next=tmp;
+  tmp=gal_data_alloc(NULL, GAL_TYPE_UINT32, 1, &outsize,
+                     NULL, 1, input->minmapsize, input->quietmmap,
+                     "hist_number", "counts",
+                     "Number of data points within each 2D-bin (box).");
+  out->next->next=tmp;
+
+  /* Fill in the first two output columns and set the histogram pointer. */
+  o1=out->array;
+  o2=out->next->array;
+  h=out->next->next->array;
+  for(i=0;i<bsizea;++i)
+    for(j=0;j<bsizeb;++j)
+      {
+        o1[i*bsizeb+j]=da[i];
+        o2[i*bsizeb+j]=db[j];
+      }
+
+  /* Set the minimum and maximum range of the histogram from the bins. */
+  binwidtha=da[1]-da[0];
+  binwidthb=db[1]-db[0];
+  mina=da[0]-binwidtha/2;
+  minb=db[0]-binwidthb/2;
+  maxa=da[ bins->size - 1      ] + binwidtha/2;
+  maxb=db[ bins->next->size - 1] + binwidthb/2;
+
+  /* Fill the histogram column. */
+  switch(input->type)
+    {
+    case GAL_TYPE_UINT8:     HISTOGRAM2D_TYPESET_A(uint8_t);     break;
+    case GAL_TYPE_INT8:      HISTOGRAM2D_TYPESET_A(int8_t);      break;
+    case GAL_TYPE_UINT16:    HISTOGRAM2D_TYPESET_A(uint16_t);    break;
+    case GAL_TYPE_INT16:     HISTOGRAM2D_TYPESET_A(int16_t);     break;
+    case GAL_TYPE_UINT32:    HISTOGRAM2D_TYPESET_A(uint32_t);    break;
+    case GAL_TYPE_INT32:     HISTOGRAM2D_TYPESET_A(int32_t);     break;
+    case GAL_TYPE_UINT64:    HISTOGRAM2D_TYPESET_A(uint64_t);    break;
+    case GAL_TYPE_INT64:     HISTOGRAM2D_TYPESET_A(int64_t);     break;
+    case GAL_TYPE_FLOAT32:   HISTOGRAM2D_TYPESET_A(float);       break;
+    case GAL_TYPE_FLOAT64:   HISTOGRAM2D_TYPESET_A(double);      break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
+            __func__, input->type);
+    }
+
+  /* Return the final output */
+  return out;
+}
+
+
+
+
+
 /* Make a cumulative frequency plot (CFP) of all the elements in the given
-   dataset with bin values that are defined in the `bins' structure (see
-   `gal_statistics_regular_bins').
+   dataset with bin values that are defined in the 'bins' structure (see
+   'gal_statistics_regular_bins').
 
    The CFP is built from the histogram: in each bin, the value is the sum
    of all previous bins in the histogram. Thus, if you have already
    calculated the histogram before calling this function, you can pass it
-   onto this function as the data structure in `bins->next'. If
-   `bin->next!=NULL', then it is assumed to be the histogram. If it is
+   onto this function as the data structure in 'bins->next'. If
+   'bin->next!=NULL', then it is assumed to be the histogram. If it is
    NULL, then the histogram will be calculated internally and freed after
    the job is finished.
 
@@ -1900,9 +2142,9 @@ gal_statistics_cfp(gal_data_t *input, gal_data_t *bins, 
int normalize)
 
    Inputs:
 
-     - `multip': multiple of the standard deviation,
+     - 'multip': multiple of the standard deviation,
 
-     - `param' must be positive and determines the type of clipping:
+     - 'param' must be positive and determines the type of clipping:
 
          - param<1.0: interpretted as a tolerance level to stop clipping.
 
@@ -1957,30 +2199,30 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
   uint8_t bytolerance = param>=1.0f ? 0 : 1;
   double oldmed=NAN, oldmean=NAN, oldstd=NAN;
   size_t num=0, one=1, four=4, size, oldsize;
-  gal_data_t *median_i, *median_d, *out, *meanstd;
+  gal_data_t *fcopy, *median_i, *median_d, *out, *meanstd;
   gal_data_t *nbs=gal_statistics_no_blank_sorted(input, inplace);
   size_t maxnum = param>=1.0f ? param : GAL_STATISTICS_SIG_CLIP_MAX_CONVERGE;
 
 
   /* Some sanity checks. */
   if( multip<=0 )
-    error(EXIT_FAILURE, 0, "%s: `multip', must be greater than zero. The "
+    error(EXIT_FAILURE, 0, "%s: 'multip', must be greater than zero. The "
           "given value was %g", __func__, multip);
   if( param<=0 )
-    error(EXIT_FAILURE, 0, "%s: `param', must be greater than zero. The "
+    error(EXIT_FAILURE, 0, "%s: 'param', must be greater than zero. The "
           "given value was %g", __func__, param);
   if( param >= 1.0f && ceil(param) != param )
-    error(EXIT_FAILURE, 0, "%s: when `param' is larger than 1.0, it is "
+    error(EXIT_FAILURE, 0, "%s: when 'param' is larger than 1.0, it is "
           "interpretted as an absolute number of clips. So it must be an "
           "integer. However, your given value %g", __func__, param);
   if( (nbs->flag & GAL_DATA_FLAG_SORT_CH)==0 )
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
-          "problem. `nbs->flag', doesn't have the `GAL_DATA_FLAG_SORT_CH' "
+          "problem. 'nbs->flag', doesn't have the 'GAL_DATA_FLAG_SORT_CH' "
           "bit activated", __func__, PACKAGE_BUGREPORT);
   if( (nbs->flag & GAL_DATA_FLAG_SORTED_I)==0
       && (nbs->flag & GAL_DATA_FLAG_SORTED_D)==0 )
     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
-          "problem. `nbs' isn't sorted", __func__, PACKAGE_BUGREPORT);
+          "problem. 'nbs' isn't sorted", __func__, PACKAGE_BUGREPORT);
 
 
   /* Allocate the necessary spaces. */
@@ -1993,21 +2235,45 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
   /* Only continue processing if we have non-blank elements. */
   oa=out->array;
   nbs_array=nbs->array;
-  if(nbs->size==0)
+  switch(nbs->size)
     {
+    /* There was nothing in the input! */
+    case 0:
       if(!quiet)
         printf("NO SIGMA-CLIPPING: all input elements are blank or input's "
                "size is zero.\n");
       oa[0] = oa[1] = oa[2] = oa[3] = NAN;
-    }
-  else
-    {
-      /* Print the comments. */
+      break;
+
+    /* Only one element, convert it to floating point and put it as the
+       mean and median (the standard deviation will be zero by
+       definition). */
+    case 1:
+      /* Write the values. */
+      fcopy=gal_data_copy_to_new_type(nbs, GAL_TYPE_FLOAT32);
+      oa[0] = 1;
+      oa[1] = *((float *)(fcopy->array));
+      oa[2] = *((float *)(fcopy->array));
+      oa[3] = 0;
+      gal_data_free(fcopy);
+
+      /* Print the comments if requested. */
+      if(!quiet)
+        {
+          printf("%-8s %-10s %-15s %-15s %-15s\n",
+                 "round", "number", "median", "mean", "STD");
+          printf("%-8d %-10.0f %-15g %-15g %-15g\n",
+                 0, oa[0], oa[1], oa[2], oa[3]);
+        }
+      break;
+
+    /* More than one element. */
+    default:
+      /* Print the comments if requested. */
       if(!quiet)
         printf("%-8s %-10s %-15s %-15s %-15s\n",
                "round", "number", "median", "mean", "STD");
 
-
       /* Do the clipping, but first initialize the values that will be
          changed during the clipping: the start of the array and the
          array's size. */
@@ -2016,7 +2282,7 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
       while(num<maxnum && size)
         {
           /* Find the average and Standard deviation, note that both
-             `start' and `size' will be different in the next round. */
+             'start' and 'size' will be different in the next round. */
           nbs->array = start;
           nbs->size = oldsize = size;
 
@@ -2046,13 +2312,13 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
                    num+1, size, *med, *mean, *std);
 
           /* If we are to work by tolerance, then check if we should jump
-             out of the loop. Normally, `oldstd' should be larger than std,
+             out of the loop. Normally, 'oldstd' should be larger than std,
              because the possible outliers have been removed. If it is not,
              it means that we have clipped too much and must stop anyway,
              so we don't need an absolute value on the difference!
 
              Note that when all the elements are identical after the clip,
-             `std' will be zero. In this case we shouldn't calculate the
+             'std' will be zero. In this case we shouldn't calculate the
              tolerance (because it will be infinity and thus lager than the
              requested tolerance level value).*/
           if( bytolerance && num>0 )
@@ -2095,7 +2361,7 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
           gal_data_free(median_d);
         }
 
-      /* If we were in tolerance mode and `num' and `maxnum' are equal (the
+      /* If we were in tolerance mode and 'num' and 'maxnum' are equal (the
          loop didn't stop by tolerance), so the outputs should be NaN. */
       out->status=num;
       if( size==0 || (bytolerance && num==maxnum) )
@@ -2123,11 +2389,15 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
 /* Find the first outlier in a distribution. */
 #define OUTLIER_BYTYPE(IT) {                                            \
     IT *arr=nbs->array;                                                 \
-    for(i=window_size;i<nbs->size;++i)                                  \
+    for(i=window_size; i<nbs->size && i!=0; pos1_neg0 ? ++i : --i)      \
       {                                                                 \
         /* Fill in the distance array. */                               \
-        for(j=0; j<wtakeone; ++j)                                       \
-          darr[j] = arr[i-window_size+j+1] - arr[i-window_size+j];      \
+        if(pos1_neg0)                                                   \
+          for(j=0; j<wtakeone; ++j)                                     \
+            darr[j] = arr[i-window_size+j+1] - arr[i-window_size+j];    \
+        else                                                            \
+          for(j=0; j<wtakeone; ++j)                                     \
+            darr[j] = arr[i+window_size-j+1] - arr[i+window_size-j];    \
                                                                         \
         /* Get the sigma-clipped information. */                        \
         sclip=gal_statistics_sigma_clip(dist, sigclip_multip,           \
@@ -2140,7 +2410,7 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
                  (float)(arr[i]-arr[i-1]), sarr[1], sarr[3],            \
                  (((double)(arr[i]-arr[i-1])) - sarr[1])/sarr[3]);      \
                                                                         \
-        /* Terminate the loop if the dist. is larger than requested. */ \
+        /* Terminate the loop if the dist is larger than requested. */  \
         /* This shows we have reached the first outlier's position. */  \
         if( (((double)(arr[i]-arr[i-1])) - sarr[1]) > sigma*sarr[3] )   \
           {                                                             \
@@ -2159,9 +2429,10 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
       }                                                                 \
   }
 gal_data_t *
-gal_statistics_outlier_positive(gal_data_t *input, size_t window_size,
-                                float sigma, float sigclip_multip,
-                                float sigclip_param, int inplace, int quiet)
+gal_statistics_outlier_bydistance(int pos1_neg0, gal_data_t *input,
+                                  size_t window_size, float sigma,
+                                  float sigclip_multip, float sigclip_param,
+                                  int inplace, int quiet)
 {
   float *sarr;
   double *darr;
@@ -2188,7 +2459,6 @@ gal_statistics_outlier_positive(gal_data_t *input, size_t 
window_size,
         }
       */
 
-
       /* Allocate space to keep the distances. */
       wtakeone=window_size-1;
       dist=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &wtakeone, NULL,
@@ -2301,10 +2571,10 @@ gal_statistics_outlier_flat_cfp(gal_data_t *input, 
size_t numprev,
 
   /* Sanity checks. */
   if(thresh<=0)
-    error(EXIT_FAILURE, 0, "%s: the value of `thresh' (%g) must be "
+    error(EXIT_FAILURE, 0, "%s: the value of 'thresh' (%g) must be "
           "positive", __func__, thresh);
   if(numprev==0)
-    error(EXIT_FAILURE, 0, "%s: `numprev' (%zu) cannot be zero", __func__,
+    error(EXIT_FAILURE, 0, "%s: 'numprev' (%zu) cannot be zero", __func__,
           numprev);
 
   /* Remove all blanks and sort the dataset. */
diff --git a/lib/table.c b/lib/table.c
index b653267..eb65630 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -50,15 +50,15 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /***************         Information about a table        ***************/
 /************************************************************************/
 /* Store the information of each column in a table (either as a text file
-   or as a FITS table) into an array of data structures with `numcols'
+   or as a FITS table) into an array of data structures with 'numcols'
    structures (one data structure for each column). The number of rows is
-   stored in `numrows'. The type of the table (e.g., ascii text file, or
-   FITS binary or ASCII table) will be put in `tableformat' (macros defined
-   in `gnuastro/table.h'.
+   stored in 'numrows'. The type of the table (e.g., ascii text file, or
+   FITS binary or ASCII table) will be put in 'tableformat' (macros defined
+   in 'gnuastro/table.h'.
 
    Note that other than the character strings (column name, units and
    comments), nothing in the data structure(s) will be allocated by this
-   function for the actual data (e.g., the `array' or `dsize' elements). */
+   function for the actual data (e.g., the 'array' or 'dsize' elements). */
 gal_data_t *
 gal_table_info(char *filename, char *hdu, gal_list_str_t *lines,
                size_t *numcols, size_t *numrows, int *tableformat)
@@ -90,6 +90,9 @@ gal_table_print_info(gal_data_t *allcols, size_t numcols, 
size_t numrows)
   int Nw=3, nw=4, uw=5, tw=4;   /* Initial width from label's width */
   char *name, *unit, *comment;
 
+  /* If there aren't any columns, there is no need to print anything. */
+  if(numcols==0) return;
+
   /* Set the widths to print the column information. The width for the
      column number can easily be identified from the logarithm of the
      number of columns. */
@@ -126,12 +129,14 @@ gal_table_print_info(gal_data_t *allcols, size_t numcols, 
size_t numrows)
       printf("%-*zu%-*s%-*s%-*s%s\n", Nw, i+1,
              nw, name ? name : GAL_BLANK_STRING ,
              uw, unit ? unit : GAL_BLANK_STRING ,
-             tw, gal_type_name(allcols[i].type, 1),
+             tw,
+             allcols[i].type ? gal_type_name(allcols[i].type, 1) : "--",
              comment ? comment : GAL_BLANK_STRING);
     }
 
   /* Print the number of rows. */
-  printf("--------\nNumber of rows: %zu\n--------\n", numrows);
+  if(numrows!=GAL_BLANK_SIZE_T)
+    printf("--------\nNumber of rows: %zu\n--------\n", numrows);
 }
 
 
@@ -173,7 +178,7 @@ table_regexerrorexit(int errcode, regex_t *compiled, char 
*input)
   (void) regerror(errcode, compiled, regexerrbuf, length);
 
   error(EXIT_FAILURE, 0, "%s: regular expression error: %s in value to "
-        "`--column' (`-c'): `%s'", __func__, regexerrbuf, input);
+        "'--column' ('-c'): '%s'", __func__, regexerrbuf, input);
 }
 
 
@@ -231,12 +236,12 @@ gal_table_list_of_indexs(gal_list_str_t *cols, gal_data_t 
*allcols,
         nummatch=0;
         len=strlen(tmp->v);
 
-        /* REGULAR EXPRESSION: the first and last characters are `/'. */
+        /* 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
+               '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;
@@ -271,7 +276,7 @@ gal_table_list_of_indexs(gal_list_str_t *cols, gal_data_t 
*allcols,
             /* 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. */
+               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);
@@ -285,7 +290,7 @@ gal_table_list_of_indexs(gal_list_str_t *cols, gal_data_t 
*allcols,
             /* Free the regex_t structure: */
             regfree(regex);
 
-            /* Put the `/' back into the input string. This is done because
+            /* 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]='/';
@@ -358,7 +363,7 @@ gal_table_list_of_indexs(gal_list_str_t *cols, gal_data_t 
*allcols,
            are done (and program is aborted) before this step. */
         if(nummatch==0)
           {
-            if( asprintf(&errorstring, "`%s' didn't match any of the "
+            if( asprintf(&errorstring, "'%s' didn't match any of the "
                          "column %ss.", tmp->v,
                          gal_tableintern_searchin_as_string(searchin))<0 )
               error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
@@ -366,7 +371,7 @@ gal_table_list_of_indexs(gal_list_str_t *cols, gal_data_t 
*allcols,
           }
 
 
-        /* Keep the value of `nummatch' if the user requested it. */
+        /* Keep the value of 'nummatch' if the user requested it. */
         if(colmatch) colmatch[colcount++]=nummatch;
       }
 
@@ -391,18 +396,18 @@ gal_table_list_of_indexs(gal_list_str_t *cols, gal_data_t 
*allcols,
 
 
 
-/* Read the specified columns in a table (named `filename') into a linked
-   list of data structures. If the file is FITS, then `hdu' will also be
-   used, otherwise, `hdu' is ignored. The information to search for columns
-   should be specified by the `cols' linked list as string values in each
+/* Read the specified columns in a table (named 'filename') into a linked
+   list of data structures. If the file is FITS, then 'hdu' will also be
+   used, otherwise, 'hdu' is ignored. The information to search for columns
+   should be specified by the 'cols' linked list as string values in each
    node of the list, the strings in each node can be a number, an exact
    match to a column name, or a regular expression (in GNU AWK format)
-   enclosed in `/ /'. The `searchin' value comes from the
-   `gal_table_where_to_search' enumerator and has to be one of its given
-   types. If `cols' is NULL, then this function will read the full table.
+   enclosed in '/ /'. The 'searchin' value comes from the
+   'gal_table_where_to_search' enumerator and has to be one of its given
+   types. If 'cols' is NULL, then this function will read the full table.
 
    The output is a linked list with the same order of the cols linked
-   list. Note that one column node in the `cols' list might give multiple
+   list. Note that one column node in the 'cols' list might give multiple
    columns, in this case, the order of output columns that correspond to
    that one input, are in order of the table (which column was read first).
    So the first requested column is the first popped data structure and so
@@ -431,11 +436,11 @@ gal_table_read(char *filename, char *hdu, gal_list_str_t 
*lines,
   /* Depending on the table format, read the columns into the output
      structure. Note that the functions here pop each index, read/store the
      desired column and pop the next, so after these functions, the output
-     linked list will have the opposite order of its input `indexll'
+     linked list will have the opposite order of its input 'indexll'
      list. So before calling any of them, we will first reverse the
-     `indexll' list, so the output data structure list will have the same
+     'indexll' list, so the output data structure list will have the same
      order as the input list of desired columns. Also note that after these
-     functions, the `indexll' will be all freed (each popped element is
+     functions, the 'indexll' will be all freed (each popped element is
      actually freed).*/
   gal_list_sizet_reverse(&indexll);
   switch(tableformat)
@@ -453,7 +458,7 @@ gal_table_read(char *filename, char *hdu, gal_list_str_t 
*lines,
 
     default:
       error(EXIT_FAILURE, 0, "%s: table format code %d not recognized for "
-            "`tableformat'", __func__, tableformat);
+            "'tableformat'", __func__, tableformat);
     }
 
   /* Clean up. */
@@ -489,7 +494,7 @@ gal_table_read(char *filename, char *hdu, gal_list_str_t 
*lines,
 /***************              Write a table               ***************/
 /************************************************************************/
 /* Write the basic information that is necessary by each program into the
-   comments field. Note that the `comments' has to be already sorted in the
+   comments field. Note that the 'comments' has to be already sorted in the
    proper order. */
 void
 gal_table_comments_add_intro(gal_list_str_t **comments, char *program_string,
@@ -503,9 +508,9 @@ gal_table_comments_add_intro(gal_list_str_t **comments, 
char *program_string,
   else      gitdescribe[0]='\0';
 
   /* Git version and time of program's starting, this will be the second
-     line. Note that ctime puts a `\n' at the end of its string, so we'll
-     have to remove that. Also, note that since we are allocating `msg', we
-     are setting the allocate flag of `gal_list_str_add' to 0. */
+     line. Note that ctime puts a '\n' at the end of its string, so we'll
+     have to remove that. Also, note that since we are allocating 'msg', we
+     are setting the allocate flag of 'gal_list_str_add' to 0. */
   if( asprintf(&tmp, "Created%s on %s", gitdescribe, ctime(rawtime))<0 )
     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
   tmp[ strlen(tmp)-1 ]='\0';
@@ -523,12 +528,12 @@ gal_table_comments_add_intro(gal_list_str_t **comments, 
char *program_string,
 
 
 /* The input is a linked list of data structures and some comments. The
-   table will then be written into `filename' with a format that is
-   specified by `tableformat'. */
+   table will then be written into 'filename' with a format that is
+   specified by 'tableformat'. */
 void
-gal_table_write(gal_data_t *cols, gal_list_str_t *comments,
-                int tableformat, char *filename, char *extname,
-                uint8_t colinfoinstdout)
+gal_table_write(gal_data_t *cols, struct gal_fits_list_key_t **keylist,
+                gal_list_str_t *comments, int tableformat, char *filename,
+                char *extname, uint8_t colinfoinstdout)
 {
   /* If a filename was given, then the tableformat is relevant and must be
      used. When the filename is empty, a text table must be printed on the
@@ -536,13 +541,14 @@ gal_table_write(gal_data_t *cols, gal_list_str_t 
*comments,
   if(filename)
     {
       if(gal_fits_name_is_fits(filename))
-        gal_fits_tab_write(cols, comments, tableformat, filename, extname);
+        gal_fits_tab_write(cols, comments, tableformat, filename, extname,
+                           keylist);
       else
-        gal_txt_write(cols, comments, filename, colinfoinstdout);
+        gal_txt_write(cols, keylist, comments, filename, colinfoinstdout);
     }
   else
     /* Write to standard output. */
-    gal_txt_write(cols, comments, filename, colinfoinstdout);
+    gal_txt_write(cols, keylist, comments, filename, colinfoinstdout);
 }
 
 
@@ -560,7 +566,8 @@ gal_table_write_log(gal_data_t *logll, char *program_string,
   gal_table_comments_add_intro(&comments, program_string, rawtime);
 
   /* Write the log file to disk */
-  gal_table_write(logll, comments, GAL_TABLE_FORMAT_TXT, filename, "LOG", 0);
+  gal_table_write(logll, NULL, comments, GAL_TABLE_FORMAT_TXT,
+                  filename, "LOG", 0);
 
   /* In verbose mode, print the information. */
   if(!quiet)
diff --git a/lib/tableintern.c b/lib/tableintern.c
index 25a1c24..bd8a1c2 100644
--- a/lib/tableintern.c
+++ b/lib/tableintern.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -37,6 +37,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #include <gnuastro-internal/timing.h>
 #include <gnuastro-internal/checkset.h>
+#include <gnuastro-internal/tableintern.h>
 
 
 
@@ -67,7 +68,7 @@ gal_tableintern_error_col_selection(char *filename, char *hdu,
   /* Abort with with the proper error. */
   error(EXIT_FAILURE, 0, "%s: %s\n\nFor more information on selecting "
         "columns in Gnuastro, please run the following command (press "
-        "`SPACE' to go down and `q' to return to the command-line):\n\n"
+        "'SPACE' to go down and 'q' to return to the command-line):\n\n"
         "    $ info gnuastro \"Selecting table columns\"\n\n"
         "To define a better column selection criteria, you can see "
         "the list of column meta-data in this table, with the following "
@@ -135,8 +136,8 @@ gal_tableintern_format_as_string(uint8_t tableformat)
 
 
 
-/* In programs, the `searchin' variable is much more easier to format in as
-   a description than an integer (which is what `gal_table_read_cols'
+/* In programs, the 'searchin' variable is much more easier to format in as
+   a description than an integer (which is what 'gal_table_read_cols'
    needs). This function will check the string value and give the
    corresponding integer value.*/
 uint8_t
@@ -175,30 +176,30 @@ gal_tableintern_searchin_as_string(uint8_t searchin)
 
 
 
-/* For programs that output tables, the `--tableformat' option will be used
+/* For programs that output tables, the '--tableformat' option will be used
    to specify what format the output table should be in. When the output
    file is a FITS file, there are multiple formats, so to simplify the
    coding in each program, this function will do a sanity check on the
-   value given to the `--tableformat' parameter. */
+   value given to the '--tableformat' parameter. */
 void
 gal_tableintern_check_fits_format(char *filename, int tableformat)
 {
   if( filename && gal_fits_name_is_fits(filename) )
     {
-      /* When `--tableformat' was not given. */
+      /* When '--tableformat' was not given. */
       if(tableformat==GAL_TABLE_FORMAT_INVALID)
-        error(EXIT_FAILURE, 0, "`%s' (output file) is a FITS file but the "
+        error(EXIT_FAILURE, 0, "'%s' (output file) is a FITS file but the "
               "desired format of the FITS table has not been specified with "
-              "the `--tableformat' option. For FITS tables, this option can "
-              "take two values: `fits-ascii', or `fits-binary'", filename);
+              "the '--tableformat' option. For FITS tables, this option can "
+              "take two values: 'fits-ascii', or 'fits-binary'", filename);
 
-      /* When `--tableformat' didn't have the correct value. */
+      /* When '--tableformat' didn't have the correct value. */
       if( tableformat != GAL_TABLE_FORMAT_AFITS
           && tableformat != GAL_TABLE_FORMAT_BFITS )
-        error(EXIT_FAILURE, 0, "`%s' (output file) is a FITS file but "
+        error(EXIT_FAILURE, 0, "'%s' (output file) is a FITS file but "
               "is not a recognized FITS table format. For FITS tables, "
-              "`--tableformat' can take two values: `fits-ascii', or "
-              "`fits-binary'", filename);
+              "'--tableformat' can take two values: 'fits-ascii', or "
+              "'fits-binary'", filename);
     }
 }
 
@@ -226,11 +227,11 @@ gal_tableintern_check_fits_format(char *filename, int 
tableformat)
 /************************************************************************/
 /* Fill in/adjust the basic information necessary to print a column. This
    information can be used for printing a plain text file or for FITS ASCII
-   tables. The `fmt' and `lng' should point to pre-allocated arrays. The
-   best way is: `char fmt[2], lng[3];' in the same function calling this.
+   tables. The 'fmt' and 'lng' should point to pre-allocated arrays. The
+   best way is: 'char fmt[2], lng[3];' in the same function calling this.
 
    The width and precision, which are also necessary for printing, are
-   updated in the data structure's `disp_width' and `disp_precision'
+   updated in the data structure's 'disp_width' and 'disp_precision'
    elements. */
 void
 gal_tableintern_col_print_info(gal_data_t *col, int tableformat,
@@ -250,7 +251,7 @@ gal_tableintern_col_print_info(gal_data_t *col, int 
tableformat,
       break;
     default:
       error(EXIT_FAILURE, 0, "%s: is only for plain text or FITS ASCII "
-            "tables. The input `tableformat' code %d not recognized",
+            "tables. The input 'tableformat' code %d not recognized",
             __func__, tableformat);
     }
 
@@ -404,10 +405,10 @@ gal_tableintern_col_print_info(gal_data_t *col, int 
tableformat,
 
 
 
-/* Use the input `blank' string and the input column to put the blank value
-   in the column's array. This function should later be generalized into a
-   function to read a string into a given data type (see
-   `gal_data_string_to_array_elem'). It is only here temporarily. */
+/* Use the input 'blank' string and the input column to put the blank value
+   in the column's array. If the string cannot be interpretted as a blank
+   of that type, then store it in the 'mmapname' element of the data
+   structure. */
 void
 gal_tableintern_read_blank(gal_data_t *col, char *blank)
 {
@@ -419,14 +420,20 @@ gal_tableintern_read_blank(gal_data_t *col, char *blank)
   /* Just for a sanity check, the ndim and array elements should be zero. */
   if(col->ndim || col->array)
     error(EXIT_FAILURE, 0, "%s: the number of dimensions, and the "
-          "`array' element of `col' must be zero", __func__);
+          "'array' element of 'col' must be zero", __func__);
 
   /* Read the blank value as the given type. If successful, then
-     `gal_data_string_to_type' will return 0. In that case, we need to
-     initialize the necessary paramters to read this data structure
-     correctly. */
-  if( !gal_type_from_string((void **)(&col->array), blank, col->type) )
+     'gal_data_string_to_type' will return 0. In that case, we need to
+     initialize the necessary parameters to read this data structure
+     correctly. If it isn't successful, then  */
+  if( gal_type_from_string((void **)(&col->array), blank, col->type) )
     {
+      col->flag=GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING;
+      gal_checkset_allocate_copy(blank, (char **)(&col->array));
+    }
+  else
+    {
+      col->flag=0;
       col->dsize=gal_pointer_allocate(GAL_TYPE_SIZE_T, 1, 0, __func__,
                                       "col->dsize");
       col->dsize[0]=col->ndim=col->size=1;
diff --git a/lib/threads.c b/lib/threads.c
index 4aeaaf6..d5eca6a 100644
--- a/lib/threads.c
+++ b/lib/threads.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -29,6 +29,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdlib.h>
 
 #include <gnuastro/threads.h>
+#include <gnuastro/pointer.h>
 
 #include <nproc.h>         /* from Gnulib, in Gnuastro's source */
 
@@ -85,11 +86,11 @@ pthread_barrier_init(pthread_barrier_t *b, 
pthread_barrierattr_t *attr,
 
 /* Suspend the calling thread (tell it to wait), until the limiting number
    of barriers is reached by the other threads. When the number isn't
-   reached yet (process goes into the `else'), then we use the
-   `pthread_cond_wait' function, which will wait until a broadcast is
-   announced by another thread that succeeds the `if'. After the thread no
-   longer needs the condition variable, we increment `b->condfinished' so
-   `pthread_barrier_destroy' can know if it should wait (sleep) or
+   reached yet (process goes into the 'else'), then we use the
+   'pthread_cond_wait' function, which will wait until a broadcast is
+   announced by another thread that succeeds the 'if'. After the thread no
+   longer needs the condition variable, we increment 'b->condfinished' so
+   'pthread_barrier_destroy' can know if it should wait (sleep) or
    continue.*/
 int
 pthread_barrier_wait(pthread_barrier_t *b)
@@ -107,7 +108,7 @@ pthread_barrier_wait(pthread_barrier_t *b)
     {
       /* Initially b->count is smaller than b->limit, otherwise control
          would never have reached here. However, when it get to
-         `pthread_cond_wait', this thread goes into a suspended period and
+         'pthread_cond_wait', this thread goes into a suspended period and
          is only awaken when a broad-cast is made. But we only want this
          thread to finish when b->count >= b->limit, so we have this while
          loop. */
@@ -178,24 +179,26 @@ gal_threads_number()
 
 
 
-/* We have `numactions` jobs and we want their indexs to be divided
-   between `numthreads` CPU threads. This function will give each index to
+/* We have 'numactions' jobs and we want their indexs to be divided
+   between 'numthreads' CPU threads. This function will give each index to
    a thread such that the maximum difference between the number of
    images for each thread is 1. The results will be saved in a 2D
-   array of `outthrdcols` columns and each row will finish with a
+   array of 'outthrdcols' columns and each row will finish with a
    (size_t) -1, which is larger than any possible index!. */
-void
+char *
 gal_threads_dist_in_threads(size_t numactions, size_t numthreads,
+                            size_t minmapsize, int quietmmap,
                             size_t **outthrds, size_t *outthrdcols)
 {
   size_t *sp, *fp;
+  char *mmapname=NULL;
   size_t i, *thrds, thrdcols;
   *outthrdcols = thrdcols = numactions/numthreads+2;
 
-  errno=0;
-  thrds=*outthrds=malloc(numthreads*thrdcols*sizeof *thrds);
-  if(thrds==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating thrds", __func__);
+  /* Allocate the space to keep the identifiers. */
+  thrds=*outthrds=gal_pointer_allocate_ram_or_mmap(GAL_TYPE_SIZE_T,
+                              numthreads*thrdcols, 0, minmapsize, &mmapname,
+                              0, __func__, "thrds");
 
   /* Initialize all the elements to NONINDEX. */
   fp=(sp=thrds)+numthreads*thrdcols;
@@ -217,6 +220,9 @@ gal_threads_dist_in_threads(size_t numactions, size_t 
numthreads,
     }
   exit(0);
   */
+
+  /* Return the name of the possibly memory-mapped file. */
+  return mmapname;
 }
 
 
@@ -261,10 +267,10 @@ gal_threads_attr_barrier_init(pthread_attr_t *attr, 
pthread_barrier_t *b,
 /************     Run a function on multiple threads  **************/
 /*******************************************************************/
 /* Run a given function on the given tiles. The function has to be
-   link-able with your final executable and has to have only one `void *'
-   argument and return a `void *' value. To have access to
+   link-able with your final executable and has to have only one 'void *'
+   argument and return a 'void *' value. To have access to
    variables/parameters in the function, you have to define a structure and
-   pass its pointer as `caller_params'.
+   pass its pointer as 'caller_params'.
 
    Here is one simple example. At least two functions and one structure are
    necessary to use this function.
@@ -291,8 +297,8 @@ gal_threads_attr_barrier_init(pthread_attr_t *attr, 
pthread_barrier_t *b,
        {
 
            THE INDEX OF THE TARGET IS NOW AVAILABLE AS
-           `tprm->indexs[i]'. YOU CAN USE IT IN WHAT EVER MANNER YOU LIKE
-           ALONG WITH THE SET OF VARIABLES/ARRAYS in `prm'.
+           'tprm->indexs[i]'. YOU CAN USE IT IN WHAT EVER MANNER YOU LIKE
+           ALONG WITH THE SET OF VARIABLES/ARRAYS in 'prm'.
 
        }
 
@@ -328,10 +334,12 @@ gal_threads_attr_barrier_init(pthread_attr_t *attr, 
pthread_barrier_t *b,
 */
 void
 gal_threads_spin_off(void *(*worker)(void *), void *caller_params,
-                     size_t numactions, size_t numthreads)
+                     size_t numactions, size_t numthreads,
+                     size_t minmapsize, int quietmmap)
 {
   int err;
   pthread_t t;          /* All thread ids saved in this, not used. */
+  char *mmapname=NULL;
   pthread_attr_t attr;
   pthread_barrier_t b;
   struct gal_threads_params *prm;
@@ -342,7 +350,7 @@ gal_threads_spin_off(void *(*worker)(void *), void 
*caller_params,
 
   /* Sanity check. */
   if(numthreads==0)
-    error(EXIT_FAILURE, 0, "%s: the number of threads (`numthreads') "
+    error(EXIT_FAILURE, 0, "%s: the number of threads ('numthreads') "
           "cannot be zero", __func__);
 
   /* Allocate the array of parameters structure structures. */
@@ -356,12 +364,13 @@ gal_threads_spin_off(void *(*worker)(void *), void 
*caller_params,
     }
 
   /* Distribute the actions into the threads: */
-  gal_threads_dist_in_threads(numactions, numthreads, &indexs, &thrdcols);
+  mmapname=gal_threads_dist_in_threads(numactions, numthreads, minmapsize,
+                                       quietmmap, &indexs, &thrdcols);
 
   /* Do the job: when only one thread is necessary, there is no need to
      spin off one thread, just call the workerfunction directly (spinning
      off threads is expensive). This is for the generic thread spinner
-     function, not this simple function where `numthreads' is a
+     function, not this simple function where 'numthreads' is a
      constant. */
   if(numthreads==1)
     {
@@ -402,7 +411,13 @@ gal_threads_spin_off(void *(*worker)(void *), void 
*caller_params,
       pthread_barrier_destroy(&b);
     }
 
+  /* If 'mmapname' is NULL, then 'indexs' is in RAM and we can safely
+     'free' it. However, when its not NULL, then the space for 'indexs' has
+     been memory-mapped (its not in RAM) so special treatment is necessary
+     to delete it through the proper function. */
+  if(mmapname) gal_pointer_mmap_free(&mmapname, quietmmap);
+  else         free(indexs);
+
   /* Clean up. */
   free(prm);
-  free(indexs);
 }
diff --git a/lib/tiff.c b/lib/tiff.c
index d688dce..2ab622f 100644
--- a/lib/tiff.c
+++ b/lib/tiff.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2018-2019, Free Software Foundation, Inc.
+Copyright (C) 2018-2021, 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
@@ -92,8 +92,8 @@ gal_tiff_suffix_is_tiff(char *name)
 
 
 /* Users may define the TIFF directory to read as a string, in that case,
-   this function can be used to convert it to a `size_t' for use in
-   `gal_tiff_read'.  */
+   this function can be used to convert it to a 'size_t' for use in
+   'gal_tiff_read'.  */
 size_t
 gal_tiff_dir_string_read(char *string)
 {
@@ -104,7 +104,7 @@ gal_tiff_dir_string_read(char *string)
   errno=0;
   dir=strtol(string, &tailptr, 0);
   if(tailptr==string)
-    error(EXIT_FAILURE, 0, "%s: `%s' couldn't be read as an integer",
+    error(EXIT_FAILURE, 0, "%s: '%s' couldn't be read as an integer",
           __func__, string);
   if(errno)
     error(EXIT_FAILURE, errno, "%s: reading %s", __func__, string);
@@ -162,7 +162,7 @@ tiff_type_read(TIFF *tif, char *filename, size_t dir)
   tiff_read_tag(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample, filename, dir);
 
   /* Read the formatting of each pixel. If no such keyword exists, use the
-     value of `SAMPLEFORMAT_UINT'. */
+     value of 'SAMPLEFORMAT_UINT'. */
   if( TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat) != 1 )
     sampleformat=SAMPLEFORMAT_UINT;
 
@@ -239,7 +239,7 @@ tiff_img_info(TIFF *tif, uint8_t *type, size_t *ndim, 
size_t *dsize,
   uint16_t u16;
   uint32_t u32;
 
-  /* Based on if `IMAGEDEPTH' is defined in the TIFF header, set the
+  /* Based on if 'IMAGEDEPTH' is defined in the TIFF header, set the
      dimensions. */
   if( TIFFGetField(tif, TIFFTAG_IMAGEDEPTH, &u32) )
     dsize[ d++ ]=u32;
@@ -266,7 +266,7 @@ tiff_img_info(TIFF *tif, uint8_t *type, size_t *ndim, 
size_t *dsize,
 
 
 
-/* Based on the `TIFFReadContigStripData' function of `tools/tiffinfo.c' of
+/* Based on the 'TIFFReadContigStripData' function of 'tools/tiffinfo.c' of
    Libtiff's source. */
 void
 tiff_read_contig_strip_data(TIFF *tif, char *filename, size_t dir,
@@ -299,8 +299,8 @@ tiff_read_contig_strip_data(TIFF *tif, char *filename, 
size_t dir,
               __func__, filename, dir);
 
       /* Copy the contents of the buffer to the output array. Note that
-         `ostart' is the byte count already, so the type is
-         irrelevant. Thus, we can read `out->array' as a `char *'
+         'ostart' is the byte count already, so the type is
+         irrelevant. Thus, we can read 'out->array' as a 'char *'
          pointer.*/
       memcpy( (char *)(out->array)+ostart, buf, nrow*scanline);
       ostart+=nrow*scanline;
@@ -314,7 +314,7 @@ tiff_read_contig_strip_data(TIFF *tif, char *filename, 
size_t dir,
 
 
 
-/* Based on the `TIFFReadSeparateStripData' function of `tools/tiffinfo.c'
+/* Based on the 'TIFFReadSeparateStripData' function of 'tools/tiffinfo.c'
    of Libtiff's source. */
 static void
 tiff_read_separate_strip_data(TIFF* tif, char *filename, size_t dir,
@@ -404,17 +404,17 @@ tiff_separate_channels_reverse(gal_data_t *out, size_t 
numch,
   /* Parse over the rows and write them in the output. */
   for(i=0;i<out->dsize[0];++i)
     {
-      /* `j' is the output row. */
+      /* 'j' is the output row. */
       j=out->dsize[0]-1-i;
 
-      /* `k' is the element in each row and `l' is the color/channel. */
+      /* 'k' is the element in each row and 'l' is the color/channel. */
       for(k=0;k<ch->dsize[1];++k)
         {
           /* Initialize the color/channel counter ocpy the elements. */
           l=0;
           for(tch=ch; tch!=NULL; tch=tch->next)
             {
-              /* Elements of the `j'th row into the `i'th. */
+              /* Elements of the 'j'th row into the 'i'th. */
               memcpy( (char *)(tch->array) + i*width  + k*so,
                       (char *)(out->array) + j*lwidth + k*numch*so + so*l,
                       so);
@@ -461,14 +461,14 @@ tiff_reverse_rows(gal_data_t *out)
       /* Go over the channel. */
       while(j>i)
         {
-          /* Copy the `i'th row into a temporary array. */
+          /* Copy the 'i'th row into a temporary array. */
           memcpy(tmp, (char *)(ch->array)+i*width, width);
 
-          /* Put the `j'th row into the `i'th row. */
+          /* Put the 'j'th row into the 'i'th row. */
           memcpy( (char *)(ch->array)+i*width, (char *)(ch->array)+j*width,
                   width );
 
-          /* Put the `tmp' row into `j'. */
+          /* Put the 'tmp' row into 'j'. */
           memcpy( (char *)(ch->array)+j*width, tmp, width);
 
           /* Increment the points. */
@@ -488,8 +488,8 @@ tiff_reverse_rows(gal_data_t *out)
 
 
 
-/* Read the data following the `TIFFReadData' of Libtiff's
-   `tools/tiffinfo.c' in the libtiff source code. */
+/* Read the data following the 'TIFFReadData' of Libtiff's
+   'tools/tiffinfo.c' in the libtiff source code. */
 static gal_data_t *
 tiff_img_read(TIFF *tif, char *filename, size_t dir, size_t minmapsize,
               int quietmmap)
@@ -522,7 +522,7 @@ tiff_img_read(TIFF *tif, char *filename, size_t dir, size_t 
minmapsize,
 
 
   /* The reading of the dataset depends on how it is organized, so first
-     we'll look into the `planarconfig' field. */
+     we'll look into the 'planarconfig' field. */
   if( TIFFIsTiled(tif) )
     {
       if(config==PLANARCONFIG_CONTIG)
@@ -579,7 +579,7 @@ gal_tiff_read(char *filename, size_t dir, size_t 
minmapsize, int quietmmap)
   /* Open the TIFF file. */
   tif=TIFFOpen(filename, "r");
   if(tif==NULL)
-    error(EXIT_FAILURE, 0, "%s: `%s' couldn't be opened for reading",
+    error(EXIT_FAILURE, 0, "%s: '%s' couldn't be opened for reading",
           __func__, filename);
 
   /* If anything other than the first directory (value of zero) is
@@ -594,7 +594,7 @@ gal_tiff_read(char *filename, size_t dir, size_t 
minmapsize, int quietmmap)
 
           /* Close the TIFF file and return. */
           TIFFClose(tif);
-          error(EXIT_FAILURE, 0, "%s: `%s' has %zu director%s/extension%s, "
+          error(EXIT_FAILURE, 0, "%s: '%s' has %zu director%s/extension%s, "
                 "and directories are counted from 0. You have asked for "
                 "directory %zu", __func__, filename, dircount,
                 dircount==1?"y":"ies", dircount==1?"":"s", dir);
diff --git a/lib/tile-internal.c b/lib/tile-internal.c
index ed9e051..1bfb684 100644
--- a/lib/tile-internal.c
+++ b/lib/tile-internal.c
@@ -6,7 +6,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2019, Free Software Foundation, Inc.
+Copyright (C) 2019-2021, 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
@@ -26,16 +26,20 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdio.h>
 #include <errno.h>
 #include <error.h>
+#include <string.h>
 #include <stdlib.h>
 
 #include <gnuastro/tile.h>
+#include <gnuastro/threads.h>
 #include <gnuastro/pointer.h>
 #include <gnuastro/statistics.h>
+#include <gnuastro/interpolate.h>
+#include <gnuastro/permutation.h>
 
 #include <gnuastro-internal/tile-internal.h>
 
 
-/* The main working function for `threshold_no_outlier'. The main
+/* The main working function for 'threshold_no_outlier'. The main
    purpose/problem is this: when we have channels, the qthresh values for
    each channel should be treated independently. */
 static void
@@ -44,11 +48,11 @@ tileinternal_no_outlier_work(gal_data_t *first, gal_data_t 
*second,
                              size_t tottilesinch, double *outliersclip,
                              float outliersigma)
 {
-  gal_data_t *outlier, *nbs;
   size_t i, osize=first->size;
   size_t start=tottilesinch*channelid;
   float *oa1=NULL, *oa2=NULL, *oa3=NULL;
-  float o, *arr1=NULL, *arr2=NULL, *arr3=NULL;
+  gal_data_t *nbs, *outlier_p, *outlier_n;
+  float o_p, o_n, *arr1=NULL, *arr2=NULL, *arr3=NULL;
 
   /* A small sanity check. */
   if(first->type!=GAL_TYPE_FLOAT32)
@@ -56,9 +60,9 @@ tileinternal_no_outlier_work(gal_data_t *first, gal_data_t 
*second,
 
   /* Correct the arrays (if necessary). IMPORTANT: The datasets are
      multi-dimensional. However, when estimating the quantile, their
-     dimensionality doesn't matter (only the `size' element is checked by
-     `gal_statistics_quantile', not `ndim' or `dsize'). So we just need to
-     correct `size' if channels are to be considered. */
+     dimensionality doesn't matter (only the 'size' element is checked by
+     'gal_statistics_quantile', not 'ndim' or `dsize'). So we just need to
+     correct 'size' if channels are to be considered. */
   if(start || tottilesinch!=first->size)
     {
       /* Keep the original values for re-setting later. */
@@ -71,7 +75,8 @@ tileinternal_no_outlier_work(gal_data_t *first, gal_data_t 
*second,
       second->array=gal_pointer_increment(second->array, start,
                                            second->type);
       if(third)
-        third->array=gal_pointer_increment(third->array, start, third->type);
+        third->array=gal_pointer_increment(third->array, start,
+                                           third->type);
 
       /* Correct their sizes. */
       first->size=tottilesinch;
@@ -83,56 +88,133 @@ tileinternal_no_outlier_work(gal_data_t *first, gal_data_t 
*second,
      first array. */
   arr1=first->array;
   nbs=gal_statistics_no_blank_sorted(first, 0);
-  outlier=gal_statistics_outlier_positive(nbs, nbs->size/2, outliersigma,
-                                          outliersclip[0], outliersclip[1],
-                                          0, 1);
+  outlier_p=gal_statistics_outlier_bydistance(1, nbs, nbs->size/2,
+                                              outliersigma, outliersclip[0],
+                                              outliersclip[1], 1, 1);
+  outlier_n=gal_statistics_outlier_bydistance(0, nbs, nbs->size/2,
+                                              outliersigma, outliersclip[0],
+                                              outliersclip[1], 1, 1);
+  /* For a check.
+  {
+    float *med;
+    gal_data_t *median=gal_statistics_median(nbs, 1);
+    float *out_n=outlier_n->array, *out_p=outlier_p->array;
+    med=median->array;
+    printf("vals: %f (out_n), %f (med), %f (out_p)\n",
+           out_n[0], med[0], out_p[0]);
+    exit(0);
+  }
+  */
+
+  /* Clean up the temporary 'nbs' array. */
   gal_data_free(nbs);
-  if(outlier)
+
+  /* If outliers exist, then implement them. */
+  if(outlier_p)
     {
-      o = *((float *)(outlier->array));
-      for(i=0;i<first->size;++i)
-        /* Just note that we have blank (NaN) values, so to avoid doing a
-           NaN check with `isnan', we will check if the value is below the
-           quantile, if it succeeds (isn't NaN and is below the quantile),
-           then we'll put it's actual value, otherwise, a NaN. */
-        arr1[i] = arr1[i]<=o ? arr1[i] : NAN;
-      gal_data_free(outlier);
+      o_p = *((float *)(outlier_p->array));
+      gal_data_free(outlier_p);
+      if(outlier_n)
+        {
+          /* For easy reading, put the negative outlier into 'o_n'. */
+          o_n = *((float *)(outlier_n->array));
+          gal_data_free(outlier_n);
+
+          /* See description below, it just includes a negative outlier. */
+          for(i=0;i<first->size;++i)
+            arr1[i] = arr1[i]<o_p ? (arr1[i]>o_n ? arr1[i] : NAN) : NAN;
+        }
+      else
+          /* Just note that we have blank (NaN) values, so to avoid doing a
+             NaN check with 'isnan', we will check if the value is below
+             the quantile, if it succeeds (isn't NaN and is below the
+             quantile), then we'll put it's actual value, otherwise, a
+             NaN. */
+        for(i=0;i<first->size;++i)
+          arr1[i] = arr1[i]<o_p ? arr1[i] : NAN;
     }
+  else
+    if(outlier_n)
+      {
+        o_n = *((float *)(outlier_n->array));
+        for(i=0;i<first->size;++i)
+          arr1[i] = arr1[i]>o_n ? arr1[i] : NAN;
+        gal_data_free(outlier_n);
+      }
 
   /* Second quantile threshold. We are finding the outliers independently
      on each dataset to later remove any tile that is blank in atleast one
      of them. */
   arr2=second->array;
   nbs=gal_statistics_no_blank_sorted(second, 0);
-  outlier=gal_statistics_outlier_positive(nbs, nbs->size, outliersigma,
-                                          outliersclip[0], outliersclip[1],
-                                          0, 1);
+  outlier_p=gal_statistics_outlier_bydistance(1, nbs, nbs->size,
+                                              outliersigma, outliersclip[0],
+                                              outliersclip[1], 1, 1);
+  outlier_n=gal_statistics_outlier_bydistance(0, nbs, nbs->size,
+                                              outliersigma, outliersclip[0],
+                                              outliersclip[1], 1, 1);
   gal_data_free(nbs);
-  if(outlier)
+  if(outlier_p)
     {
-      o = *((float *)(outlier->array));
-      for(i=0;i<second->size;++i)
-        arr2[i] = arr2[i]<=o ? arr2[i] : NAN;
-      gal_data_free(outlier);
+      o_p = *((float *)(outlier_p->array));
+      gal_data_free(outlier_p);
+      if(outlier_n)
+        {
+          o_n = *((float *)(outlier_n->array));
+          for(i=0;i<first->size;++i)
+            arr2[i] = arr2[i]<o_p ? (arr2[i]>o_n ? arr2[i] : NAN) : NAN;
+          gal_data_free(outlier_n);
+        }
+      else
+        for(i=0;i<first->size;++i)
+          arr2[i] = arr2[i]<o_p ? arr2[i] : NAN;
     }
+  else
+    if(outlier_n)
+      {
+        o_n = *((float *)(outlier_n->array));
+        for(i=0;i<first->size;++i)
+          arr2[i] = arr2[i]>o_n ? arr2[i] : NAN;
+        gal_data_free(outlier_n);
+      }
 
   /* The third (if it exists). */
   if(third)
     {
       arr3=third->array;
       nbs=gal_statistics_no_blank_sorted(third, 0);
-      outlier=gal_statistics_outlier_positive(nbs, nbs->size/2,
-                                              outliersigma,
-                                              outliersclip[0],
-                                              outliersclip[1], 0, 1);
+      outlier_p=gal_statistics_outlier_bydistance(1, nbs, nbs->size/2,
+                                                  outliersigma,
+                                                  outliersclip[0],
+                                                  outliersclip[1], 1, 1);
+      outlier_n=gal_statistics_outlier_bydistance(0, nbs, nbs->size/2,
+                                                  outliersigma,
+                                                  outliersclip[0],
+                                                  outliersclip[1], 1, 1);
       gal_data_free(nbs);
-      if(outlier)
+      if(outlier_p)
         {
-          o = *((float *)(outlier->array));
-          for(i=0;i<third->size;++i)
-            arr3[i] = arr3[i]<=o ? arr3[i] : NAN;
-          gal_data_free(outlier);
+          o_p = *((float *)(outlier_p->array));
+          gal_data_free(outlier_p);
+          if(outlier_n)
+            {
+              o_n = *((float *)(outlier_n->array));
+              for(i=0;i<first->size;++i)
+                arr3[i] = arr3[i]<o_p ? (arr3[i]>o_n ? arr3[i] : NAN) : NAN;
+              gal_data_free(outlier_n);
+            }
+          else
+            for(i=0;i<first->size;++i)
+              arr3[i] = arr3[i]<o_p ? arr3[i] : NAN;
         }
+      else
+        if(outlier_n)
+          {
+            o_n = *((float *)(outlier_n->array));
+            for(i=0;i<first->size;++i)
+              arr3[i] = arr3[i]>o_n ? arr3[i] : NAN;
+            gal_data_free(outlier_n);
+          }
     }
 
   /* Make sure all three have the same NaN pixels. */
@@ -170,7 +252,7 @@ gal_tileinternal_no_outlier(gal_data_t *first, gal_data_t 
*second,
 
   /* A small sanity check: */
   if(first->size!=tl->tottiles)
-    error(EXIT_FAILURE, 0, "%s: `first->size' and `tl->tottiles' must have "
+    error(EXIT_FAILURE, 0, "%s: 'first->size' and 'tl->tottiles' must have "
           "the same value, but they don't: %zu, %zu", __func__, first->size,
           tl->tottiles);
 
@@ -195,3 +277,453 @@ gal_tileinternal_no_outlier(gal_data_t *first, gal_data_t 
*second,
         }
     }
 }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*************************************************************/
+/************        Local outlier removal        ************/
+/*************************************************************/
+#define TILEINTERNAL_OUTLIER_FLAGS_NO           0
+#define TILEINTERNAL_OUTLIER_FLAGS_NGB_CHECKED  0x1
+#define TILEINTERNAL_OUTLIER_FLAGS_BLANK        0x2
+
+struct tileinternal_outlier_local
+{
+  gal_data_t                    *input;
+  gal_data_t                  *measure;
+  gal_data_t                   *blanks;
+  size_t                  numneighbors;
+  uint8_t                *thread_flags;
+  gal_list_void_t            *ngb_vals;
+  float (*metric)(size_t *, size_t *, size_t );
+
+  struct gal_tile_two_layer_params *tl;
+};
+
+
+
+
+
+/* Run the interpolation on many threads. */
+static void *
+gal_tileinternal_no_outlier_local_on_thread(void *in_prm)
+{
+  /* Low-level variables that others depend on. */
+  struct gal_threads_params *tprm=(struct gal_threads_params *)in_prm;
+  struct tileinternal_outlier_local *prm=
+    (struct tileinternal_outlier_local *)(tprm->params);
+
+  /* Higher-level variables. */
+  struct gal_tile_two_layer_params *tl=prm->tl;
+  int correct_index=(tl && tl->totchannels>1 && !tl->workoverch);
+  gal_data_t *input=prm->input;
+
+  /* Rest of variables. */
+  void *nv;
+  uint8_t *b, *bf, *bb;
+  gal_list_void_t *tvll;
+  size_t ngb_counter, pind;
+  gal_list_dosizet_t *lQ, *sQ;
+  gal_data_t *tin, *tnear, *nearest=NULL;
+  float dist, pdist, *tnarr, *marr=prm->measure->array;
+  size_t i, index, fullind, chstart=0, ndim=input->ndim;
+  size_t size = (correct_index ? tl->tottilesinch : input->size);
+  size_t *dsize = (correct_index ? tl->numtilesinch : input->dsize);
+  size_t *icoord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
+                                      "icoord");
+  size_t *ncoord=gal_pointer_allocate(GAL_TYPE_SIZE_T, ndim, 0, __func__,
+                                      "ncoord");
+  uint8_t *flag, *fullflag=&prm->thread_flags[tprm->id*input->size];
+
+  /* Based on the above. */
+  size_t *dinc=gal_dimension_increment(ndim, dsize);
+
+  /* Initialize the flags array. We need two flags during this processing:
+     1) to see if there are blanks. 2) to see if a neighbor has been
+     checked. These are both binary (0 or 1). So to avoid wasting space, we
+     will use bits to store them. We start with only setting the blank flag
+     once for the whole thread. Then for each interpolated pixel, we reset
+     the neighbor-check flag. */
+  flag=fullflag;
+  bb=prm->blanks->array;
+  bf=(b=fullflag)+input->size;
+  do *b = *bb++ ? TILEINTERNAL_OUTLIER_FLAGS_BLANK : 0; while(++b<bf);
+
+
+  /* Put the allocated space to keep the neighbor values into a structure
+     for easy processing. */
+  tin=input;
+  for(tvll=prm->ngb_vals; tvll!=NULL; tvll=tvll->next)
+    {
+      nv=gal_pointer_increment(tvll->v, tprm->id*prm->numneighbors,
+                               input->type);
+      gal_list_data_add_alloc(&nearest, nv, tin->type, 1,
+                              &prm->numneighbors, NULL, 0, -1, 1,
+                              NULL, NULL, NULL);
+      tin=tin->next;
+    }
+  gal_list_data_reverse(&nearest);
+
+
+  /* Go over all the points given to this thread. */
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
+    {
+      /* For easy reading. */
+      fullind=tprm->indexs[i];
+
+
+      /* If we are on a blank element, then ignore this pixel. */
+      if( (fullflag[fullind] & TILEINTERNAL_OUTLIER_FLAGS_BLANK) )
+        { marr[fullind]=NAN; continue; }
+
+
+      /* Correct the index (if necessary). When the values come from a
+         tiled dataset, the caller might want to interpolate the values of
+         each channel separately (not mix values from different
+         channels). In such a case, the tiles of each channel (and their
+         values in 'input' are contiguous. So we need to correct
+         'tprm->indexs[i]' (which is the index over the whole tessellation,
+         including all channels). */
+      if(correct_index)
+        {
+          /* Index of this tile in its channel. */
+          index = fullind % tl->tottilesinch;
+
+          /* Index of the first tile in this channel. */
+          chstart = (fullind / tl->tottilesinch) * tl->tottilesinch;
+
+          /* Set the channel's starting pointer for the flags. */
+          flag = gal_pointer_increment(fullflag, chstart, GAL_TYPE_UINT8);
+        }
+      else
+        {
+          chstart=0;
+          index=fullind;
+        }
+
+
+      /* Reset all checked bits in the flags array to 0. */
+      ngb_counter=0;
+      bf=(b=flag)+size;
+      do *b &= ~(TILEINTERNAL_OUTLIER_FLAGS_NGB_CHECKED); while(++b<bf);
+
+
+      /* Get the coordinates of this pixel (to be interpolated). */
+      gal_dimension_index_to_coord(index, ndim, dsize, icoord);
+
+
+      /* Start parsing the neighbors. We will use a two-way ordered linked
+         list structure. To start from the nearest and go out to the
+         farthest. */
+      lQ=sQ=NULL;
+      gal_list_dosizet_add(&lQ, &sQ, index, 0.0f);
+      while(sQ)
+        {
+          /* Pop-out (p) an index from the queue: */
+          pind=gal_list_dosizet_pop_smallest(&lQ, &sQ, &pdist);
+
+          /* If this isn't a blank value then add its values to the list of
+             neighbor values. Note that we didn't check whether the values
+             were blank or not when adding this pixel to the queue. */
+          if( !(flag[pind] & TILEINTERNAL_OUTLIER_FLAGS_BLANK) )
+            {
+              tin=input;
+              for(tnear=nearest; tnear!=NULL; tnear=tnear->next)
+                {
+                  memcpy(gal_pointer_increment(tnear->array, ngb_counter,
+                                               tin->type),
+                         gal_pointer_increment(tin->array, chstart+pind,
+                                               tin->type),
+                         gal_type_sizeof(tin->type));
+                  tin=tin->next;
+                }
+
+              /* If we have filled all the elements clean up the linked
+                 list and break out. */
+              if(++ngb_counter>=prm->numneighbors)
+                {
+                  if(lQ) gal_list_dosizet_free(lQ);
+                  break;
+                }
+            }
+
+          /* Go over all the neighbors of this popped pixel and add them to
+             the list of neighbors to be checked. */
+          GAL_DIMENSION_NEIGHBOR_OP(pind, ndim, dsize, 1, dinc,
+           {
+             /* Only look at neighbors that have not been checked. VERY
+                IMPORTANT: we must not check for blank values here,
+                otherwise we won't be able to parse over extended blank
+                regions. */
+             if( !(flag[nind] & TILEINTERNAL_OUTLIER_FLAGS_NGB_CHECKED) )
+               {
+                 /* Get the coordinates of this neighbor. */
+                 gal_dimension_index_to_coord(nind, ndim, dsize, ncoord);
+
+                 /* Distance of this neighbor to the one to be filled. */
+                 dist=prm->metric(icoord, ncoord, ndim);
+
+                 /* Add this neighbor to the list. */
+                 gal_list_dosizet_add(&lQ, &sQ, nind, dist);
+
+                 /* Flag this neighbor as checked. */
+                 flag[nind] |= TILEINTERNAL_OUTLIER_FLAGS_NGB_CHECKED;
+               }
+           } );
+
+          /* If there are no more meshes to add to the queue, then this
+             shows, there were not enough points for
+             interpolation. Normally, this loop should only be exited
+             through the 'currentnum>=numnearest' check above. */
+          if(sQ==NULL)
+            error(EXIT_FAILURE, 0, "%s: only %zu neighbors found while "
+                  "you had asked to use %zu neighbors for close neighbor "
+                  "interpolation", __func__, ngb_counter,
+                  prm->numneighbors);
+        }
+
+      /* Calculate the desired statistic, and write it in the output. */
+      for(tnear=nearest; tnear!=NULL; tnear=tnear->next)
+        {
+          /* First, reset the sorting flags (which remain from the last
+             time). */
+          tnear->flag &= ~(GAL_DATA_FLAG_SORT_CH | GAL_DATA_FLAG_BLANK_CH);
+
+          /* For a check on the values.
+          { size_t i; float *f=tnear->array;
+            for(i=0;i<tnear->size;++i) printf("%f\n", f[i]); } */
+
+          /* Sort the elements, then find the difference between the
+             maximium and the value that is just after the minimum. We are
+             doing this because the scatter in the minimum can be large. */
+          tnarr=tnear->array;
+          gal_statistics_sort_increasing(tnear);
+          marr[fullind] = tnarr[tnear->size-1]-tnarr[1];
+        }
+    }
+
+
+  /* Clean up. */
+  for(tnear=nearest; tnear!=NULL; tnear=tnear->next) tnear->array=NULL;
+  gal_list_data_free(nearest);
+  free(icoord);
+  free(ncoord);
+  free(dinc);
+
+
+  /* Wait for all the other threads to finish and return. */
+  if(tprm->b) pthread_barrier_wait(tprm->b);
+  return NULL;
+}
+
+
+
+
+
+void
+gal_tileinternal_no_outlier_local(gal_data_t *input, gal_data_t *second,
+                                  gal_data_t *third,
+                                  struct gal_tile_two_layer_params *tl,
+                                  uint8_t metric, size_t numneighbors,
+                                  size_t numthreads, double *outliersclip,
+                                  double outliersigma, char *filename)
+{
+  gal_data_t *othresh;
+  float *base, *f, *ff, thresh;
+  struct tileinternal_outlier_local prm;
+  size_t owindow, ngbvnum=numthreads*numneighbors;
+  int permute=(tl && tl->totchannels>1 && tl->workoverch);
+
+
+  /* Sanity checks. */
+  if(numneighbors<=3)
+    error(EXIT_FAILURE, 0, "interpnumngb has to be larger than 3, but "
+          "is currently %zu", numneighbors);
+  if(input->type!=GAL_TYPE_FLOAT32)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+          "the problem. The input to this function (not NoiseChisel) "
+          "should be in 32-bit floating point, but it is %s", __func__,
+          PACKAGE_BUGREPORT, gal_type_name(input->type, 1));
+  if(second && second->type!=GAL_TYPE_FLOAT32)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+          "the problem. The 'second' argument to this function (not "
+          "NoiseChisel) should be in 32-bit floating point, but it is "
+          "%s", __func__, PACKAGE_BUGREPORT, gal_type_name(input->type, 1));
+  if(third && third->type!=GAL_TYPE_FLOAT32)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+          "the problem. The 'third' argument to this function (not "
+          "NoiseChisel) should be in 32-bit floating point, but it is "
+          "%s", __func__, PACKAGE_BUGREPORT, gal_type_name(input->type, 1));
+  if(second && gal_dimension_is_different(input, second) )
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+          "the problem. The 'second' argument to this function (not "
+          "NoiseChisel) doesn't have the same size as the input",
+          __func__, PACKAGE_BUGREPORT);
+  if(third && gal_dimension_is_different(input, third) )
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+          "the problem. The 'third' argument to this function (not "
+          "NoiseChisel) doesn't have the same size as the input",
+          __func__, PACKAGE_BUGREPORT);
+
+
+  /* Initialize the constant parameters. */
+  prm.tl           = tl;
+  prm.ngb_vals     = NULL;
+  prm.input        = input;
+  prm.numneighbors = numneighbors;
+
+
+  /* Set the distance metric. */
+  switch(metric)
+    {
+    case GAL_INTERPOLATE_NEIGHBORS_METRIC_RADIAL:
+      prm.metric=gal_dimension_dist_radial;
+      break;
+    case GAL_INTERPOLATE_NEIGHBORS_METRIC_MANHATTAN:
+      prm.metric=gal_dimension_dist_manhattan;
+      break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: %d is not a valid metric identifier",
+            __func__, metric);
+    }
+
+
+  /* Flag the blank values. */
+  prm.blanks=gal_blank_flag(input);
+
+
+  /* If the input is from a tile structure and the user has asked to ignore
+     channels, then re-order the values. */
+  if(permute)
+    {
+      /* Prepare the permutation (if necessary/not already defined). */
+      gal_tile_full_permutation(tl);
+
+      /* Re-order values to ignore channels (if necessary). */
+      gal_permutation_apply(input, tl->permutation);
+      gal_permutation_apply(prm.blanks, tl->permutation);
+    }
+
+
+  /* Necessary allocations and basic checks. if we are given a list of
+     datasets, make the necessary allocations. The reason we are doing this
+     after a check of 'aslinkedlist' is that the 'input' might have a
+     'next' element, but the caller might not have called
+     'aslinkedlist'. */
+  prm.measure=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, input->ndim,
+                             input->dsize, input->wcs, 0, input->minmapsize,
+                             input->quietmmap, NULL, input->unit, NULL);
+  gal_list_void_add(&prm.ngb_vals,
+                    gal_pointer_allocate(input->type, ngbvnum, 0,
+                                         __func__, "prm.ngb_vals"));
+
+
+  /* Allocate space for all the flag values of all the threads here (memory
+     in each thread is limited) and this is cleaner. */
+  prm.thread_flags=gal_pointer_allocate(GAL_TYPE_UINT8,
+                                        numthreads*input->size, 0,
+                                        __func__, "prm.thread_flags");
+
+
+  /* Spin off the threads. */
+  gal_threads_spin_off(gal_tileinternal_no_outlier_local_on_thread,
+                       &prm, input->size, numthreads, input->minmapsize,
+                       input->quietmmap);
+
+
+  /* Find the outliers in the distribution, we will start from the first
+     third of the cases to find the first outlier. Note that this should
+     not be done in-place because we need the 'measure' arrray
+     afterwards. */
+  owindow=(prm.measure->size - gal_blank_number(prm.measure, 1))/3;
+  othresh=gal_statistics_outlier_bydistance(1, prm.measure, owindow,
+                                            outliersigma, outliersclip[0],
+                                            outliersclip[1], 0, 1);
+
+
+  /* If an outlier threshold was actually found, then mask all the tiles
+     larger than that value. */
+  if(othresh)
+    {
+      base=prm.measure->array;
+      ff=(f=input->array)+input->size;
+      thresh=((float *)(othresh->array))[0];
+      do { *f = isnan(*f) ? *f : (*base>thresh ? NAN : *f); ++base; }
+      while(++f<ff);
+    }
+
+
+  /* For a check.
+  printf("measure-threshold: %f\n", thresh);
+  if(permute)
+    gal_permutation_apply_inverse(prm.measure, tl->permutation);
+  gal_tile_full_values_write(prm.measure, tl, 1, "measure.fits",
+                             NULL, NULL);
+  */
+
+
+  /* If the values were permuted for the interpolation, then re-order the
+     values back to their original location (so they correspond to their
+     tile indexs. */
+  if(permute)
+    gal_permutation_apply_inverse(input, tl->permutation);
+
+
+  /* If the other arrays are given, set all the blank elements here to
+     blank there too. */
+  if(second)
+    {
+      base=input->array; ff=(f=second->array)+second->size;
+      do { *f = isnan(*base++) ? NAN : *f ;} while(++f<ff);
+    }
+  if(third)
+    {
+      base=input->array; ff=(f=third->array)+third->size;
+      do { *f = isnan(*base++) ? NAN : *f ;} while(++f<ff);
+    }
+
+
+  /* Write the check images if necessary. */
+  if(filename)
+    {
+      input->name="VALUE1_NO_OUTLIER";
+      gal_tile_full_values_write(input, tl, 1, filename, NULL, NULL);
+      input->name=NULL;
+      if(second)
+        {
+          second->name="VALUE2_NO_OUTLIER";
+          gal_tile_full_values_write(second, tl, 1, filename, NULL, NULL);
+          second->name=NULL;
+        }
+      if(third)
+        {
+          third->name="VALUE3_NO_OUTLIER";
+          gal_tile_full_values_write(third, tl, 1, filename, NULL, NULL);
+          third->name=NULL;
+        }
+    }
+
+
+  /* Clean up and return. */
+  gal_data_free(othresh);
+  free(prm.thread_flags);
+  gal_data_free(prm.blanks);
+  gal_data_free(prm.measure);
+  gal_list_void_free(prm.ngb_vals, 1);
+}
diff --git a/lib/tile.c b/lib/tile.c
index ad827cf..a79808c 100644
--- a/lib/tile.c
+++ b/lib/tile.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -76,18 +76,18 @@ gal_tile_start_coord(gal_data_t *tile, size_t *start_coord)
 
 
 /* Put the starting and ending (end point is not inclusive) coordinates of
-   a tile into the `start_end' array. It is assumed that a space of
-   `2*tile->ndim' has been already allocated (static or dynamic) before
+   a tile into the 'start_end' array. It is assumed that a space of
+   '2*tile->ndim' has been already allocated (static or dynamic) before
    this function is called.
 
-   `rel_block' (or relative-to-block) is only relevant when the tile has an
+   'rel_block' (or relative-to-block) is only relevant when the tile has an
    intermediate tile between it and the allocated space (like a channel,
-   see `gal_tile_full_two_layers'). If it doesn't (`tile->block' points the
-   allocated dataset), then the value to `rel_block' is irrelevant.
+   see 'gal_tile_full_two_layers'). If it doesn't ('tile->block' points the
+   allocated dataset), then the value to 'rel_block' is irrelevant.
 
-   However, when `tile->block' is its self a larger block and `rel_block'
+   However, when 'tile->block' is its self a larger block and 'rel_block'
    is set to 0, then the starting and ending positions will be based on the
-   position within `tile->block', not the allocated space. */
+   position within 'tile->block', not the allocated space. */
 void
 gal_tile_start_end_coord(gal_data_t *tile, size_t *start_end, int rel_block)
 {
@@ -129,8 +129,8 @@ gal_tile_start_end_coord(gal_data_t *tile, size_t 
*start_end, int rel_block)
 
 
 /* Put the indexs of the first/start and last/end pixels (inclusive) in a
-   tile into the `start_end' array (that has two elements). It will then
-   return the pointer to the start of the tile in the `work' data
+   tile into the 'start_end' array (that has two elements). It will then
+   return the pointer to the start of the tile in the 'work' data
    structure. */
 void *
 gal_tile_start_end_ind_inclusive(gal_data_t *tile, gal_data_t *work,
@@ -144,8 +144,8 @@ gal_tile_start_end_ind_inclusive(gal_data_t *tile, 
gal_data_t *work,
                                              __func__, "end_coord");
 
 
-  /* The starting index can be found from the distance of the `tile->array'
-     pointer and `block->array' pointer. IMPORTANT: with the type of the
+  /* The starting index can be found from the distance of the 'tile->array'
+     pointer and 'block->array' pointer. IMPORTANT: with the type of the
      block array.  */
   start_end_inc[0]=gal_pointer_num_between(block->array, tile->array,
                                            block->type);
@@ -157,7 +157,7 @@ gal_tile_start_end_ind_inclusive(gal_data_t *tile, 
gal_data_t *work,
                               start_coord);
 
 
-  /* `end_coord' is one unit ahead of the last element in the tile in every
+  /* 'end_coord' is one unit ahead of the last element in the tile in every
      dimension. To have less potential for bugs, we will remove that extra
      value, so we get the coordinates of the last pixel in the tile
      (inclusive). We will finally, increment that value by one to get to
@@ -216,7 +216,7 @@ gal_tile_start_end_ind_inclusive(gal_data_t *tile, 
gal_data_t *work,
 /***********************************************************************/
 /* Construct a list of tile(s) given positional minimum(s) and maximum(s).
    The output is an allocated an allocated array that can later be freed
-   with `gal_data_array_free'. The minimum and maximums are assumed to be
+   with 'gal_data_array_free'. The minimum and maximums are assumed to be
    inclusive.
 
    The array keeping the minmium and maximum coordinates for each label
@@ -300,7 +300,7 @@ gal_tile_series_from_minmax(gal_data_t *block, size_t 
*minmax, size_t number)
 /***********************************************************************/
 /* When you are working on an array, it important to know the size of the
    allocated space in each dimension. This simple function will just follow
-   the block pointer and return the `dsize' element of lowest-level
+   the block pointer and return the 'dsize' element of lowest-level
    structure. */
 gal_data_t *
 gal_tile_block(gal_data_t *tile)
@@ -320,14 +320,14 @@ gal_tile_block(gal_data_t *tile)
    for higher dimensions can be alittle more complicated, So we will go
    over some examples. The notations below are:
 
-       `n'     number of dimensions (same in tile and block).
-       `t[]'   size of the tile in each dimension.
-       `b[]'   size of the allocated block in each dimension.
+       'n'     number of dimensions (same in tile and block).
+       't[]'   size of the tile in each dimension.
+       'b[]'   size of the allocated block in each dimension.
 
    It is just important to see the output of this function as an increment
    from the the last patch of contiguous memory associated with the
-   tile. So when the increment number is `t[n-1]' (the first 2D slice of
-   the tile has been parsed), simply incrementing by `b[n-2] * b[n-1]' will
+   tile. So when the increment number is 't[n-1]' (the first 2D slice of
+   the tile has been parsed), simply incrementing by 'b[n-2] * b[n-1]' will
    take us to the last row of
 
   num_increment      coord         increment
@@ -407,21 +407,21 @@ gal_tile_block_increment(gal_data_t *block, size_t *tsize,
    Arguments
    ---------
 
-     `tilevalues': This must be an array that has the same number of
-        elements as that in `tilesll' and in the same order that `tilesll'
+     'tilevalues': This must be an array that has the same number of
+        elements as that in 'tilesll' and in the same order that 'tilesll'
         elements are parsed (from first to last). As a result the
         dimensionality of this array is irrelevant. Note that unlike
-        `tiles', `tilevalues' must be an array.
+        'tiles', 'tilevalues' must be an array.
 
-     `tilesll': This will be parsed as a linked list (using the `next'
+     'tilesll': This will be parsed as a linked list (using the 'next'
         element). Internally, it might be stored as an array, but this
         function doesn't care! The position of the tile over its block will
-        be determined according to the `block' element and the pointer of
-        its `array' as fully described in `gnuastro/data.h'. This function
+        be determined according to the 'block' element and the pointer of
+        its 'array' as fully described in 'gnuastro/data.h'. This function
         will not pop/free the list, it will only parse it from start to
         end.
 
-     `initialize': Initialize the allocated space with blank values before
+     'initialize': Initialize the allocated space with blank values before
         writing in the constant values. This can be useful when the tiles
         don't cover the full allocated block. */
 gal_data_t *
@@ -436,8 +436,8 @@ gal_tile_block_write_const_value(gal_data_t *tilevalues, 
gal_data_t *tilesll,
   /* A small sanity check. */
   for(tile=tilesll; tile!=NULL; tile=tile->next) ++nt;
   if(nt!=nv)
-    error(EXIT_FAILURE, 0, "%s: the number of elements in `tilevalues' (%zu) "
-          "and `tilesll' (%zu) must be the same", __func__, nv, nt);
+    error(EXIT_FAILURE, 0, "%s: the number of elements in 'tilevalues' (%zu) "
+          "and 'tilesll' (%zu) must be the same", __func__, nv, nt);
 
   /* Allocate the output array. */
   tofill=gal_data_alloc(NULL, type, block->ndim, block->dsize, block->wcs,
@@ -445,7 +445,7 @@ gal_tile_block_write_const_value(gal_data_t *tilevalues, 
gal_data_t *tilesll,
                         tilevalues->name, tilevalues->unit,
                         tilevalues->comment);
 
-  /* If requested, initialize `tofill', otherwise it is assumed that the
+  /* If requested, initialize 'tofill', otherwise it is assumed that the
      full area of the output is covered by the tiles. */
   if(withblank || initialize) gal_blank_initialize(tofill);
   else
@@ -462,14 +462,14 @@ gal_tile_block_write_const_value(gal_data_t *tilevalues, 
gal_data_t *tilesll,
         }
     }
 
-  /* Go over the tiles and write the values in. Recall that `tofill' has
-     the same type as `tilevalues'. So we are using memcopy. */
+  /* Go over the tiles and write the values in. Recall that 'tofill' has
+     the same type as 'tilevalues'. So we are using memcopy. */
   tile_ind=0;
   for(tile=tilesll; tile!=NULL; tile=tile->next)
     {
-      /* Set the pointer to use as input. The `if(o)' statement is set
+      /* Set the pointer to use as input. The 'if(o)' statement is set
          because GCC 7.1.1 complained about the possiblity of the first
-         argument of `memcpy' being NULL. Recall that `o' is a pointer. */
+         argument of 'memcpy' being NULL. Recall that 'o' is a pointer. */
       in=gal_pointer_increment(tilevalues->array, tile_ind++, type);
       GAL_TILE_PARSE_OPERATE( tile, tofill, 1, withblank, {
           if(o) memcpy(o, in, gal_type_sizeof(type));
@@ -484,8 +484,8 @@ gal_tile_block_write_const_value(gal_data_t *tilevalues, 
gal_data_t *tilesll,
 
 
 /* Make a copy of the memory block and fill it with the index of each tile
-   in `tilesll' (counting from 0). The non-filled areas will have blank
-   values. The output dataset will have a type of `GAL_TYPE_INT32'. */
+   in 'tilesll' (counting from 0). The non-filled areas will have blank
+   values. The output dataset will have a type of 'GAL_TYPE_INT32'. */
 gal_data_t *
 gal_tile_block_check_tiles(gal_data_t *tilesll)
 {
@@ -530,7 +530,7 @@ gal_tile_block_relative_to_other(gal_data_t *tile, 
gal_data_t *other)
 
 
 
-/* To use within `gal_tile_full_blank_flag'. */
+/* To use within 'gal_tile_full_blank_flag'. */
 static void *
 tile_block_blank_flag(void *in_prm)
 {
@@ -562,7 +562,8 @@ gal_tile_block_blank_flag(gal_data_t *tile_ll, size_t 
numthreads)
 {
   /* Go over all the tiles and update their blank flag. */
   gal_threads_spin_off(tile_block_blank_flag, tile_ll,
-                       gal_list_data_number(tile_ll), numthreads);
+                       gal_list_data_number(tile_ll), numthreads,
+                       tile_ll->minmapsize, tile_ll->quietmmap);
 }
 
 
@@ -599,7 +600,7 @@ gal_tile_block_blank_flag(gal_data_t *tile_ll, size_t 
numthreads)
         |           |           |           |           |       |
         ---------------------------------------------------------
 
-   The remainder of the scenario above will always be smaller than `tile
+   The remainder of the scenario above will always be smaller than 'tile
    size' (can be even 1-pixel wide). So, we will merge the first tile size
    with the remainder.  In this way, the size of the first tile will always
    be between between one and two times the size of the regular tile:
@@ -611,7 +612,7 @@ gal_tile_block_blank_flag(gal_data_t *tile_ll, size_t 
numthreads)
    When there is only a small remainder (for example one or two pixels),
    then this layout is fine. But when the remainder is significant compared
    to the regular tile size (like the example above), then it will make
-   more sense to cut the first tile into two halfs (`f-half' and `l-half')
+   more sense to cut the first tile into two halfs ('f-half' and 'l-half')
    and put them at the start and end of the full length:
 
 
@@ -656,7 +657,7 @@ gal_tile_full_regular_first(gal_data_t *parent, size_t 
*regular,
                   tsize[i]  = dsize[i]/regular[i] + 1 ;
 
                   /* If we only have one tile along the dimension, then
-                     `first[i]==dsize[i]'. In this case, the first and last
+                     'first[i]==dsize[i]'. In this case, the first and last
                      tiles are the same and must have the same size. */
                   last[i]   = ( first[i]==dsize[i]
                                 ? first[i]
@@ -689,41 +690,41 @@ gal_tile_full_regular_first(gal_data_t *parent, size_t 
*regular,
 
 
 /* Cover the full dataset with (mostly) identical tiles. The regular tile
-   size is determined from the `size' array. If the input data's size is
-   not an exact multiple of `size' for each dimension, then the tiles
+   size is determined from the 'size' array. If the input data's size is
+   not an exact multiple of 'size' for each dimension, then the tiles
    touching the edges in that dimension will have a different size to fully
    cover every element of the input. For a full description of tiling in
-   `gal_data_t', please see `data.h'.
+   'gal_data_t', please see 'data.h'.
 
    Inputs
    ------
 
-     `input' is the gal_data_t which you want to tile (only used for its
+     'input' is the gal_data_t which you want to tile (only used for its
         sizes).
 
-     `regular' is the size of the regular tiles along each of the input's
+     'regular' is the size of the regular tiles along each of the input's
         dimensions. So it must have the same number of elements as the
-        dimensions of `input'.
+        dimensions of 'input'.
 
-     `remainderfrac' is the significant fraction of the remainder space if
+     'remainderfrac' is the significant fraction of the remainder space if
         the width of the input isn't an exact multiple of the tile size
-        along a dimension, see `gal_tile_full_regular_first'.
+        along a dimension, see 'gal_tile_full_regular_first'.
 
-     `out' is the pointer to the array of data structures that is to keep
-        the tile parameters. If `*out==NULL', then the necessary space will
+     'out' is the pointer to the array of data structures that is to keep
+        the tile parameters. If '*out==NULL', then the necessary space will
         be allocated. If it is not NULL, then all the tile information will
-        be filled from the given element, see `multiple' for more.
+        be filled from the given element, see 'multiple' for more.
 
-     `multiple': When the `*out' array is to be allocated, allocate
-        `multiple' times the necessary space. This can be very useful when
-        you have several more identically sized `inputs', and you want all
+     'multiple': When the '*out' array is to be allocated, allocate
+        'multiple' times the necessary space. This can be very useful when
+        you have several more identically sized 'inputs', and you want all
         their tiles to be allocated (and thus indexed) together, even
-        though they have different `block' datasets (that then link to one
-        allocated space).  See the `gal_tile_full_two_layers' below.
+        though they have different 'block' datasets (that then link to one
+        allocated space).  See the 'gal_tile_full_two_layers' below.
 
-     `firsttsize': The size of the first tile along every dimension. This
-        is only different from the regular tile size when `regular' is not
-        an exact multiple of `input''s length along every dimension. This
+     'firsttsize': The size of the first tile along every dimension. This
+        is only different from the regular tile size when 'regular' is not
+        an exact multiple of 'input''s length along every dimension. This
         array is allocated internally by this function.
 
    Output
@@ -741,10 +742,10 @@ gal_tile_full_regular_first(gal_data_t *parent, size_t 
*regular,
      we need the following sizes. If the input array has no parent/block,
      then both these sizes are equal to it's own size:
 
-        1. block-size (or `bsize'), which is the size of the allocated
+        1. block-size (or 'bsize'), which is the size of the allocated
            block in each dimension.
 
-        2. parent-size (or `psize') which is the size of the parent in each
+        2. parent-size (or 'psize') which is the size of the parent in each
            dimension (we don't want to go out of the paren't range). */
 size_t *
 gal_tile_full(gal_data_t *input, size_t *regular,
@@ -777,7 +778,7 @@ gal_tile_full(gal_data_t *input, size_t *regular,
   else     *out = tiles = gal_data_array_calloc(numtiles*multiple);
 
 
-  /* It is possible that the `input' dataset is its-self a larger tile over
+  /* It is possible that the 'input' dataset is its-self a larger tile over
      a region of the allocated block. In that case, we need to account for
      the block's dimensions when calculating the position of this block. */
   if(input->block)
@@ -801,13 +802,13 @@ gal_tile_full(gal_data_t *input, size_t *regular,
       for(d=0;d<input->ndim;++d)
         {
           /* Convert the tile coordinates to pixel coordinates within
-             `input'. See the comments above `gal_tile_full_regular_first':
+             'input'. See the comments above 'gal_tile_full_regular_first':
              The first tile in every dimension can be different from the
              regular tile size. */
           coord[d] = tcoord[d] ? first[d] + (tcoord[d]-1)*regular[d] : 0;
 
-          /* When the `input' data structure (that is to be tiled here) was
-             itself a tile over a larger allocated array, a `start' array
+          /* When the 'input' data structure (that is to be tiled here) was
+             itself a tile over a larger allocated array, a 'start' array
              has been allocated to correct the coordinates so they refer to
              a physical position on the allocated block of memory. */
           if(start)
@@ -819,7 +820,7 @@ gal_tile_full(gal_data_t *input, size_t *regular,
       tind=gal_dimension_coord_to_index(block->ndim, block->dsize, coord);
 
       /* Now that we have the index of this tile's starting point compared
-         to the allocated block, put it in to the tile's `array'
+         to the allocated block, put it in to the tile's 'array'
          pointer. */
       tiles[i].array=gal_pointer_increment(block->array, tind, block->type);
 
@@ -832,9 +833,9 @@ gal_tile_full(gal_data_t *input, size_t *regular,
       for(d=0;d<input->ndim;++d)
         {
           /* The size of the first and last tiles can be different from the
-             majority of the `regular' tiles that have the same size. When
+             majority of the 'regular' tiles that have the same size. When
              a tile is on the edge in one of the dimensions, then its
-             `tcoord[d]' will be either 0 or the last. */
+             'tcoord[d]' will be either 0 or the last. */
           if( first[d] != regular[d]
               && ( tcoord[d]==0 || tcoord[d]==tsize[d]-1 ) )
             {
@@ -848,9 +849,9 @@ gal_tile_full(gal_data_t *input, size_t *regular,
           tiles[i].size *= tiles[i].dsize[d];
         }
 
-      /* Set the block structure for this tile to the `input', and set the
+      /* Set the block structure for this tile to the 'input', and set the
          next pointer as the next tile. Note that only when we are dealing
-         with the last tile should the `next' pointer be set to NULL.*/
+         with the last tile should the 'next' pointer be set to NULL.*/
       tiles[i].flag  = 0;
       tiles[i].block = input;
       tiles[i].next  = i==numtiles-1 ? NULL : &tiles[i+1];
@@ -869,17 +870,17 @@ gal_tile_full(gal_data_t *input, size_t *regular,
   free(tcoord);
   *firsttsize=first;
   if(start) free(start);
-  tsize[input->ndim]=-1; /* `tsize' had ndim+1 values, we will mark the  */
+  tsize[input->ndim]=-1; /* 'tsize' had ndim+1 values, we will mark the  */
   return tsize;          /* extra space with the largest possible value: */
-}                        /* -1, see `gal_tile_full_sanity_check'.        */
+}                        /* -1, see 'gal_tile_full_sanity_check'.        */
 
 
 
 
 
-/* Make sure that the input parameters (in `tl', short for two-layer) fit
+/* Make sure that the input parameters (in 'tl', short for two-layer) fit
    with the input dataset. The filename and HDU are only required for error
-   messages. Also, allocate and fill the `channelsize' array. */
+   messages. Also, allocate and fill the 'channelsize' array. */
 void
 gal_tile_full_sanity_check(char *filename, char *hdu, gal_data_t *input,
                            struct gal_tile_two_layer_params *tl)
@@ -892,7 +893,7 @@ gal_tile_full_sanity_check(char *filename, char *hdu, 
gal_data_t *input,
     {
       /* Not equal to zero. */
       if(tl->tilesize[i]==0)
-        error(EXIT_FAILURE, 0, "`--tilesize' must be larger than zero, "
+        error(EXIT_FAILURE, 0, "'--tilesize' must be larger than zero, "
               "the given value for dimension %zu was zero", ndim-i);
 
       /* If the tile size is larger than the dataset size in this
@@ -907,7 +908,7 @@ gal_tile_full_sanity_check(char *filename, char *hdu, 
gal_data_t *input,
      the dataset's dimensions). */
   if(i!=ndim)
     error(EXIT_FAILURE, 0, "%s (hdu: %s): has %zu dimensions, but only %zu "
-          "value(s) given for the tile size (`--tilesize' option).",
+          "value(s) given for the tile size ('--tilesize' option).",
           filename, hdu, ndim, i);
 
 
@@ -936,20 +937,20 @@ gal_tile_full_sanity_check(char *filename, char *hdu, 
gal_data_t *input,
          image. Note that the reported dimension must be in FITS format.*/
       if( input->dsize[i] < tl->numchannels[i] )
         error(EXIT_FAILURE, 0, "the number of channels in dimension %zu "
-              "(%zu) is more than the size of the `%s' (hdu: %s) in that "
+              "(%zu) is more than the size of the '%s' (hdu: %s) in that "
               "dimension", ndim-i, tl->numchannels[i], filename, hdu);
 
       /* Also check the tile size. */
       if( input->dsize[i] < tl->tilesize[i] )
         error(EXIT_FAILURE, 0, "the tile size in dimension %zu (%zu) is "
-              "more than the size of the `%s' (hdu: %su) in that dimension",
+              "more than the size of the '%s' (hdu: %su) in that dimension",
               ndim-i, tl->tilesize[i], filename, hdu);
 
       /* First check. */
       d=(double)input->dsize[i]/(double)(tl->numchannels[i]);
       if(ceil(d)!=d)
         error(EXIT_FAILURE, 0, "%zu (number of channels along dimension "
-              "%zu) is not exactly divisible by %zu (the length of `%s' "
+              "%zu) is not exactly divisible by %zu (the length of '%s' "
               "(hdu: %s) that dimension). The channels cover the input "
               "dataset, hence, they must be identical", tl->numchannels[i],
               ndim-i, input->dsize[i], filename, hdu);
@@ -966,10 +967,10 @@ gal_tile_full_sanity_check(char *filename, char *hdu, 
gal_data_t *input,
 /* A dataset can be tiled with two layers that are related:
 
       Channels: A tesselation of larger tile sizes that all have the same
-           size (`channel_size' must be an exact multiple of `input's size
+           size ('channel_size' must be an exact multiple of 'input's size
            along every dimension. In astronomy images, this can be seen as
            CCD amplifiers, that cover large parts of the image. If
-           `*channels!=NULL' then it is assumed to be already present and
+           '*channels!=NULL' then it is assumed to be already present and
            will not be allocated.
 
       Tiles: A combined tesselation of each channel with smaller
@@ -1006,9 +1007,9 @@ gal_tile_full_two_layers(gal_data_t *input,
   tl->tottilesinch = gal_dimension_total_size(ndim, tl->numtilesinch);
   for(i=1; i<tl->totchannels; ++i)
     {
-      /* Set the first tile in this channel. Then use it it fill the `next'
-         pointer of the previous channel's tiles. Note that `gal_tile_full'
-         set this `next' element to NULL. */
+      /* Set the first tile in this channel. Then use it it fill the 'next'
+         pointer of the previous channel's tiles. Note that 'gal_tile_full'
+         set this 'next' element to NULL. */
       t = tl->tiles + i * tl->tottilesinch;
       tl->tiles[ i * tl->tottilesinch - 1 ].next = t;
 
@@ -1039,7 +1040,7 @@ gal_tile_full_two_layers(gal_data_t *input,
 
    Make a permutation to allow the conversion of tile location in memory to
    its location in the full input dataset and put it in the input's
-   `permutation' element. If a permutation has already been defined for the
+   'permutation' element. If a permutation has already been defined for the
    tessellation, this function will not do anythin. If permutation won't be
    necessary, then this function will just return (the permutation must
    have been initialized to NULL). */
@@ -1265,7 +1266,7 @@ gal_tile_full_free_contents(struct 
gal_tile_two_layer_params *tl)
   if(tl->permutation)   free(tl->permutation);
   if(tl->firsttsize)    free(tl->firsttsize);
 
-  /* Free the arrays of `gal_data_t' for each tile and channel. */
+  /* Free the arrays of 'gal_data_t' for each tile and channel. */
   if(tl->tiles)    gal_data_array_free(tl->tiles,    tl->tottiles,    0);
   if(tl->channels) gal_data_array_free(tl->channels, tl->totchannels, 0);
 }
diff --git a/lib/timing.c b/lib/timing.c
index 23dfd0d..dc5208f 100644
--- a/lib/timing.c
+++ b/lib/timing.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -30,13 +30,13 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 
 /* Micro-second based timer, which can be used to generate random numbers.
-   The type of `tv_sec' and `tv_usec' is `long int' (from the GNU C Library
+   The type of 'tv_sec' and 'tv_usec' is 'long int' (from the GNU C Library
    manual). But the expected type used by GSL's random number generator is
-   `unsigned long int'. Since the only random number generator that is
-   currently in Gnuastro is GSL's (and it asks for seeds of type `unsigned
-   long int'), this function will return in `unsigned long int'. Note that
-   `unsigned long' will be able to hold any positive `long' integer, which
-   is the case for `tv_sec' and `tv_usec': they are both positive, while
+   'unsigned long int'. Since the only random number generator that is
+   currently in Gnuastro is GSL's (and it asks for seeds of type 'unsigned
+   long int'), this function will return in 'unsigned long int'. Note that
+   'unsigned long' will be able to hold any positive 'long' integer, which
+   is the case for 'tv_sec' and 'tv_usec': they are both positive, while
    the opposite isn't true. */
 unsigned long int
 gal_timing_time_based_rng_seed()
diff --git a/lib/txt.c b/lib/txt.c
index d9182ba..7f2c942 100644
--- a/lib/txt.c
+++ b/lib/txt.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -33,6 +33,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #include <gnuastro/txt.h>
 #include <gnuastro/list.h>
+#include <gnuastro/units.h>
 #include <gnuastro/blank.h>
 #include <gnuastro/table.h>
 
@@ -66,7 +67,7 @@ enum txt_formats_code
 
 
 
-/* Return one of the `txt_line_stat' constant values. */
+/* Return one of the 'txt_line_stat' constant values. */
 int
 gal_txt_line_stat(char *line)
 {
@@ -93,8 +94,8 @@ gal_txt_line_stat(char *line)
 
 /* Remove the spaces around the values, and if the final/trimmed string has
    no length, return NULL. */
-static char *
-txt_trim_space(char *str)
+char *
+gal_txt_trim_space(char *str)
 {
   char *end;
 
@@ -104,11 +105,10 @@ txt_trim_space(char *str)
   /* Remove the spaces before the start of the string. */
   while(isspace(*str)) ++str;
 
-  /* If there was nothing in the string, then just return the ending `\0'
-     character. */
+  /* If there was nothing in the string, return NULL. */
   if(*str=='\0') return NULL;
 
-  /* Remove the spaces at the end, and write a possibly new `\0'. */
+  /* Remove the spaces at the end, and write a possibly new '\0'. */
   end = str + strlen(str) - 1;
   while(end>str && isspace(*end)) --end;
   *(end+1)='\0';
@@ -122,7 +122,7 @@ txt_trim_space(char *str)
 
 
 /* Each information comment should have a format like this (replace
-   `Column' with `Image' for 2D arrays):
+   'Column' with 'Image' for 2D arrays):
 
       # Column N: NAME [UNITS, TYPE, BLANK] COMMENT
 
@@ -132,17 +132,17 @@ txt_trim_space(char *str)
   set. But if there are comments, then the brackets themselves are required
   to separate the name from the comments.
 
-  Any white space characters before or after the delimiters (`:', `[', `]',
-  `,') is ignored, but spaces within the values are kept. For example, in
-  the two following lines, NAME will be set to `col name' (even though
+  Any white space characters before or after the delimiters (':', '[', ']',
+  ',') is ignored, but spaces within the values are kept. For example, in
+  the two following lines, NAME will be set to 'col name' (even though
   there are extra spaces in the second line, The column unit will be
-  set to `col unit'.
+  set to 'col unit'.
 
       # Column 2: col name
       # Column 2 :  col name     [ col unit, type ] Column comments.
 
   When the column type is a string, the number of characters in the string
-  is also necessary, for example `str10'. Without an integer attached, the
+  is also necessary, for example 'str10'. Without an integer attached, the
   line will be ignored.
 
   In the case of an error or mis-match, the line will be ignored.
@@ -164,13 +164,13 @@ txt_info_from_comment(char *in_line, gal_data_t **datall, 
char *comm_start,
   char *number=NULL, *name=NULL, *comment=NULL;
   char *inbrackets=NULL, *unit=NULL, *typestr=NULL, *blank=NULL;
 
-  /* Make a copy of the input line if `inplace==0'. */
+  /* Make a copy of the input line if 'inplace==0'. */
   if(inplace) line=aline=in_line;
   else
     {
-      /* Because the `line' pointer will change, we need a pointer to the
+      /* Because the 'line' pointer will change, we need a pointer to the
          start of the originally allocated lines. This is the purpose of
-         `aline' (allocated-line). */
+         'aline' (allocated-line). */
       gal_checkset_allocate_copy(in_line, &aline);
       line=aline;
     }
@@ -179,7 +179,7 @@ txt_info_from_comment(char *in_line, gal_data_t **datall, 
char *comm_start,
   /* Only read this comment line if it follows the convention: */
   if( !strncmp(line, comm_start, len) )
     {
-      /* Set `name', `inbrackets', and `comment' in the first pass through
+      /* Set 'name', 'inbrackets', and 'comment' in the first pass through
          the line. */
       number=line+len;
       while(*line!='\0')
@@ -213,11 +213,11 @@ txt_info_from_comment(char *in_line, gal_data_t **datall, 
char *comm_start,
       if(*tailptr!='\0' || index<=0) return;
 
 
-      /* If there was no name (the line is just `# Column N:'), then ignore
+      /* If there was no name (the line is just '# Column N:'), then ignore
          the line. Relying on the column count from the first line is more
          robust and less prone to human error, for example typing a number
          larger than the total number of columns.  */
-      name=txt_trim_space(name);
+      name=gal_txt_trim_space(name);
       if(name==NULL) return;
 
 
@@ -244,8 +244,8 @@ txt_info_from_comment(char *in_line, gal_data_t **datall, 
char *comm_start,
         }
 
 
-      /* If `typestr' was given, then check if this is a standard type. If
-         `typestr' wasn't specified, then the default double type code will
+      /* If 'typestr' was given, then check if this is a standard type. If
+         'typestr' wasn't specified, then the default double type code will
          be used (see the variable definitions above). If the given type
          isn't a standard type then ignore the line. Just note that if we
          are dealing with the string type, we have to pull out the number
@@ -253,7 +253,7 @@ txt_info_from_comment(char *in_line, gal_data_t **datall, 
char *comm_start,
          the line. */
       if(typestr && *typestr!='\0')
         {
-          typestr=txt_trim_space(typestr);
+          typestr=gal_txt_trim_space(typestr);
           if( !strncmp(typestr, "str", 3) )
             {
               type=GAL_TYPE_STRING;
@@ -271,12 +271,12 @@ txt_info_from_comment(char *in_line, gal_data_t **datall, 
char *comm_start,
       /* Add this column's information into the columns linked list. We
          will define the data structur's array to have zero dimensions (no
          array) by default. If there is a blank value its value will be put
-         into the array by `gal_table_read_blank'. To keep the name, unit,
+         into the array by 'gal_table_read_blank'. To keep the name, unit,
          and comment strings, trim the white space before and after each
          before using them here.  */
       gal_list_data_add_alloc(datall, NULL, type, 0, NULL, NULL, 0, -1, 1,
-                              name, txt_trim_space(unit),
-                              txt_trim_space(comment) );
+                              name, gal_txt_trim_space(unit),
+                              gal_txt_trim_space(comment) );
 
 
       /* Put the number of this column into the status variable of the data
@@ -287,8 +287,12 @@ txt_info_from_comment(char *in_line, gal_data_t **datall, 
char *comm_start,
 
 
       /* Write the blank value into the array. Note that this is not the
-         final column, we are just collecting information now. */
-      gal_tableintern_read_blank(*datall, txt_trim_space(blank));
+         final column, we are just collecting information now. If the blank
+         value wasn't interpretted into the given type the 'flag' element
+         of the dataset will be set and the contents of the 'blank' string
+         will be copied into the 'array' element (so it should be
+         interpretted as a 'char *'). */
+      gal_tableintern_read_blank(*datall, gal_txt_trim_space(blank));
     }
 
   /* Clean up. */
@@ -320,7 +324,7 @@ txt_info_from_first_row(char *in_line, gal_data_t **datall, 
int format,
   else
     {
       gal_checkset_allocate_copy(in_line, &line);
-      aline=line; /* We are going to change `line' during this function. */
+      aline=line; /* We are going to change 'line' during this function. */
     }
   end=line+strlen(line);
 
@@ -357,7 +361,7 @@ txt_info_from_first_row(char *in_line, gal_data_t **datall, 
int format,
 
 
       /* If there is information for this column, then check if it is a
-         string, and if so, don't use `strtok_r' (because it might have
+         string, and if so, don't use 'strtok_r' (because it might have
          delimiters). So manually go ahead in the line till you get to the
          start of the string, then increment the line until the end of the
          space set for the strings. */
@@ -373,9 +377,9 @@ txt_info_from_first_row(char *in_line, gal_data_t **datall, 
int format,
 
               /* If we haven't reached the end of the line, then set a NULL
                  character where the string ends, so we can use the
-                 token. VERY IMPORTANT: this should not be `<=end'. If the
+                 token. VERY IMPORTANT: this should not be '<=end'. If the
                  given width is larger than line, there is no problem, the
-                 `\0' of the line will also be used to end this last
+                 '\0' of the line will also be used to end this last
                  column.*/
               if(line<end)
                 {
@@ -402,7 +406,7 @@ txt_info_from_first_row(char *in_line, gal_data_t **datall, 
int format,
              no information, then set its status value to the column
              number. So, for a table, this should be done on every
              column. But for an image, this should only be done once (when
-             `datall' has not been defined yet, for example in the column
+             'datall' has not been defined yet, for example in the column
              information). */
           if( *datall==NULL || format==TXT_FORMAT_TABLE )
             {
@@ -414,16 +418,16 @@ txt_info_from_first_row(char *in_line, gal_data_t 
**datall, int format,
     }
 
 
-  /* When looking at a text table, `n' is the number of columns (elements
+  /* When looking at a text table, 'n' is the number of columns (elements
      in the linked list). But when looking at an image, it is the size of
      the second dimension. To unify things from this step forwards, we will
-     thus keep the value of `n' until this point in another variable (that
-     will be returned finally), and for an image, change `n' to 1. This is
+     thus keep the value of 'n' until this point in another variable (that
+     will be returned finally), and for an image, change 'n' to 1. This is
      necsesary in case the user has for example given two column
      information comments on an image plain text file.
 
-     Note that `n' counts from 1, so the total number of tokens is one less
-     than `n'.*/
+     Note that 'n' counts from 1, so the total number of tokens is one less
+     than 'n'.*/
   numtokens=n-1;
   if(format==TXT_FORMAT_IMAGE) n=1;
 
@@ -442,7 +446,7 @@ txt_info_from_first_row(char *in_line, gal_data_t **datall, 
int format,
               /* This column has to be removed/freed. But we have to make
                  some corrections before freeing it:
 
-                  - When `prev==NULL', then we still haven't got to the
+                  - When 'prev==NULL', then we still haven't got to the
                     first valid element yet and must free this one, but if
                     we do that, then the main pointer to the start of the
                     list will be lost (we will loose all connections with
@@ -450,7 +454,7 @@ txt_info_from_first_row(char *in_line, gal_data_t **datall, 
int format,
                     that to the next element.
 
                   - When there actually was a previous element
-                    (`prev!=NULL'), then we must correct it's next
+                    ('prev!=NULL'), then we must correct it's next
                     pointer. Otherwise we will break up the chain.*/
               if(prev) prev->next=col->next; else *datall=col->next;
               tmp=col->next;
@@ -497,7 +501,7 @@ txt_infoll_to_array(gal_data_t *datall, size_t *numdata)
   /* First find the total number of columns. */
   for(data=datall; data!=NULL; data=data->next) ++numc;
 
-  /* Conversion to an arry is only necessary when there is more than one
+  /* Conversion to an array is only necessary when there is more than one
      element in the list. */
   if(numc>1)
     {
@@ -510,14 +514,15 @@ txt_infoll_to_array(gal_data_t *datall, size_t *numdata)
           /* Pop the top element. */
           data=gal_list_data_pop(&datall);
 
-          /* The `status' value is the number of the column (counting from
+          /* The 'status' value is the number of the column (counting from
              1, not 0). */
           ind=data->status-1;
 
-          /* Put all the information from `data' into the respective part
+          /* Put all the information from 'data' into the respective part
              of the array. About the pointers, instead of having to
              allocate them again, we will just set them to NULL so
-             `gal_data_free' doesn't remove them.*/
+             'gal_data_free' doesn't remove them.*/
+          dataarr[ind].flag       = data->flag;    data->flag=0;
           dataarr[ind].name       = data->name;    data->name=NULL;
           dataarr[ind].unit       = data->unit;    data->unit=NULL;
           dataarr[ind].array      = data->array;   data->array=NULL;
@@ -548,7 +553,8 @@ txt_infoll_to_array(gal_data_t *datall, size_t *numdata)
 
 static void
 txt_get_info_line(char *line, gal_data_t **datall, char *comm_start,
-                  int *firstlinedone, int format, size_t *dsize, int inplace)
+                  int *firstlinedone, int format, size_t *dsize,
+                  int inplace)
 {
   size_t numtokens;
 
@@ -590,12 +596,12 @@ txt_get_info(char *filename, gal_list_str_t *lines, int 
format,
   gal_data_t *datall=NULL;
   int test, firstlinedone=0;
   char *line, *format_err="empty", *comm_start;
-  size_t linelen=10; /* `linelen' will be increased by `getline'. */
+  size_t linelen=10; /* 'linelen' will be increased by 'getline'. */
 
-  /* `filename' and `lines' cannot both be non-NULL. */
+  /* 'filename' and 'lines' cannot both be non-NULL. */
   test = (filename!=NULL) + (lines!=NULL);
   if( test!=1 )
-    error(EXIT_FAILURE, 0, "%s: one of the `filename' and `lines' "
+    error(EXIT_FAILURE, 0, "%s: one of the 'filename' and 'lines' "
           "arguments must be NULL, but they are both %s", __func__,
           test==2 ? "non-NULL" : "NULL");
 
@@ -609,7 +615,7 @@ txt_get_info(char *filename, gal_list_str_t *lines, int 
format,
             __func__, format);
     }
 
-  /* Initialize the first `dsize' element. */
+  /* Initialize the first 'dsize' element. */
   dsize[0]=0;
 
   /* Parse the file or go over the lines. */
@@ -620,12 +626,12 @@ txt_get_info(char *filename, gal_list_str_t *lines, int 
format,
       fp=fopen(filename, "r");
       if(fp==NULL)
         error(EXIT_FAILURE, errno, "%s: couldn't open to read as a plain "
-              "text %s (from Gnuastro's `%s')", filename, format_err,
+              "text %s (from Gnuastro's '%s')", filename, format_err,
               __func__);
 
 
       /* Allocate the space necessary to keep each line as we parse
-         it. Note that `getline' is going to later `realloc' this space to
+         it. Note that 'getline' is going to later 'realloc' this space to
          fit the line length. */
       errno=0;
       line=malloc(linelen*sizeof *line);
@@ -715,7 +721,7 @@ static void
 txt_read_token(gal_data_t *data, gal_data_t *info, char *token,
                size_t i, char *filename, size_t lineno, size_t colnum)
 {
-  char   *tailptr;
+  char   *tailptr, emptystr[1]="\0";
   char     **str = data->array, **strb;
   uint8_t    *uc = data->array,   *ucb;
   int8_t      *c = data->array,    *cb;
@@ -728,94 +734,165 @@ txt_read_token(gal_data_t *data, gal_data_t *info, char 
*token,
   float       *f = data->array,    *fb;
   double      *d = data->array,    *db;
 
-  /* Read the proper token into the column. */
-  switch(data->type)
+  /* See if this token is blank. */
+  int isblankstr = ( info->flag==GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING
+                     ? ( strcmp(info->array,token)==0 ? 1 : 0 )
+                     : 0);
+
+  /* If the string is equal to the given blank string, then just write
+     blank and don't bother parsing the token. */
+  if(isblankstr)
     {
-    case GAL_TYPE_STRING:
-      gal_checkset_allocate_copy(txt_trim_space(token), &str[i]);
-      if( (strb=info->array) && !strcmp( *strb, str[i] ) )
+      switch(data->type)
         {
+        case GAL_TYPE_STRING:
           free(str[i]);
-          gal_checkset_allocate_copy(GAL_BLANK_STRING, &str[i]);
+          gal_checkset_allocate_copy(GAL_BLANK_STRING, &str[i]); break;
+        case GAL_TYPE_UINT8:  uc[i] = GAL_BLANK_UINT8;   break;
+        case GAL_TYPE_INT8:    c[i] = GAL_BLANK_INT8;    break;
+        case GAL_TYPE_UINT16: us[i] = GAL_BLANK_UINT16;  break;
+        case GAL_TYPE_INT16:   s[i] = GAL_BLANK_INT16;   break;
+        case GAL_TYPE_UINT32: ui[i] = GAL_BLANK_UINT32;  break;
+        case GAL_TYPE_INT32:  ii[i] = GAL_BLANK_INT32;   break;
+        case GAL_TYPE_UINT64: ul[i] = GAL_BLANK_UINT64;  break;
+        case GAL_TYPE_INT64:   l[i] = GAL_BLANK_INT64;   break;
+        case GAL_TYPE_FLOAT32: f[i] = GAL_BLANK_FLOAT32; break;
+        case GAL_TYPE_FLOAT64: d[i] = GAL_BLANK_FLOAT64; break;
+        default:
+          error(EXIT_FAILURE, 0, "%s: type code %d not recognized in "
+                "'blankstr' switch", __func__, data->type);
         }
-      break;
+    }
 
-    case GAL_TYPE_UINT8:
-      uc[i]=strtol(token, &tailptr, 0);
-      if( (ucb=info->array) && *ucb==uc[i] )
-        uc[i]=GAL_BLANK_UINT8;
-      break;
+  /* Parse the token into the column's dataset. */
+  else
+    {
+      switch(data->type)
+        {
+        case GAL_TYPE_STRING:
+          gal_checkset_allocate_copy(gal_txt_trim_space(token), &str[i]);
+          if( (strb=info->array) && !strcmp( *strb, str[i] ) )
+            {
+              free(str[i]);
+              gal_checkset_allocate_copy(GAL_BLANK_STRING, &str[i]);
+            }
+          break;
 
-    case GAL_TYPE_INT8:
-      c[i]=strtol(token, &tailptr, 0);
-      if( (cb=info->array) && *cb==c[i] )
-        c[i]=GAL_BLANK_INT8;
-      break;
+        case GAL_TYPE_UINT8:
+          uc[i]=strtol(token, &tailptr, 10);
+          if( (ucb=info->array) && *ucb==uc[i] )
+            uc[i]=GAL_BLANK_UINT8;
+          break;
 
-    case GAL_TYPE_UINT16:
-      us[i]=strtol(token, &tailptr, 0);
-      if( (usb=info->array) && *usb==us[i] )
-        us[i]=GAL_BLANK_UINT16;
-      break;
+        case GAL_TYPE_INT8:
+          c[i]=strtol(token, &tailptr, 10);
+          if( (cb=info->array) && *cb==c[i] )
+            c[i]=GAL_BLANK_INT8;
+          break;
 
-    case GAL_TYPE_INT16:
-      s[i]=strtol(token, &tailptr, 0);
-      if( (sb=info->array) && *sb==s[i] )
-        s[i]=GAL_BLANK_INT16;
-      break;
+        case GAL_TYPE_UINT16:
+          us[i]=strtol(token, &tailptr, 10);
+          if( (usb=info->array) && *usb==us[i] )
+            us[i]=GAL_BLANK_UINT16;
+          break;
 
-    case GAL_TYPE_UINT32:
-      ui[i]=strtol(token, &tailptr, 0);
-      if( (uib=info->array) && *uib==ui[i] )
-        ui[i]=GAL_BLANK_UINT32;
-      break;
+        case GAL_TYPE_INT16:
+          s[i]=strtol(token, &tailptr, 10);
+          if( (sb=info->array) && *sb==s[i] )
+            s[i]=GAL_BLANK_INT16;
+          break;
 
-    case GAL_TYPE_INT32:
-      ii[i]=strtol(token, &tailptr, 0);
-      if( (ib=info->array) && *ib==ii[i] )
-        ii[i]=GAL_BLANK_INT32;
-      break;
+        case GAL_TYPE_UINT32:
+          ui[i]=strtol(token, &tailptr, 10);
+          if( (uib=info->array) && *uib==ui[i] )
+            ui[i]=GAL_BLANK_UINT32;
+          break;
 
-    case GAL_TYPE_UINT64:
-      ul[i]=strtoul(token, &tailptr, 0);
-      if( (ulb=info->array) && *ulb==ul[i] )
-        ul[i]=GAL_BLANK_UINT64;
-      break;
+        case GAL_TYPE_INT32:
+          ii[i]=strtol(token, &tailptr, 10);
+          if( (ib=info->array) && *ib==ii[i] )
+            ii[i]=GAL_BLANK_INT32;
+          break;
 
-    case GAL_TYPE_INT64:
-      l[i]=strtol(token, &tailptr, 0);
-      if( (lb=info->array) && *lb==l[i] )
-        l[i]=GAL_BLANK_INT64;
-      break;
+        case GAL_TYPE_UINT64:
+          ul[i]=strtoul(token, &tailptr, 10);
+          if( (ulb=info->array) && *ulb==ul[i] )
+            ul[i]=GAL_BLANK_UINT64;
+          break;
 
-      /* For the blank value of floating point types, we need to make
-         sure it isn't a NaN, because a NaN value will fail on any
-         condition check (even `=='). If it isn't NaN, then we can
-         compare the values. */
-    case GAL_TYPE_FLOAT32:
-      f[i]=strtod(token, &tailptr);
-      if( (fb=info->array)
-          && ( (isnan(*fb) && isnan(f[i])) || *fb==f[i] ) )
-        f[i]=GAL_BLANK_FLOAT64;
-      break;
+        case GAL_TYPE_INT64:
+          l[i]=strtol(token, &tailptr, 10);
+          if( (lb=info->array) && *lb==l[i] )
+            l[i]=GAL_BLANK_INT64;
+          break;
 
-    case GAL_TYPE_FLOAT64:
-      d[i]=strtod(token, &tailptr);
-      if( (db=info->array)
-          && ( (isnan(*db) && isnan(d[i])) || *db==d[i] ) )
-        d[i]=GAL_BLANK_FLOAT64;
-      break;
+          /* For the blank value of floating point types, we need to make
+             sure it isn't a NaN, because a NaN value will fail on any
+             condition check (even '=='). If it isn't NaN, then we can
+             compare the values. */
+        case GAL_TYPE_FLOAT32:
+          f[i]=strtod(token, &tailptr);
+          if( (*tailptr=='h' || *tailptr=='d') && isdigit(*(tailptr+1)) )
+            {
+              f[i] = ( *tailptr=='h'
+                       ? gal_units_ra_to_degree(token)
+                       : gal_units_dec_to_degree(token) );
+              if( !isnan(f[i]) ) tailptr=emptystr;
+            }
+          if( (fb=info->array)
+              && ( (isnan(*fb) && isnan(f[i])) || *fb==f[i] ) )
+            f[i]=GAL_BLANK_FLOAT32;
+          break;
 
-    default:
-      error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
-            __func__, data->type);
-    }
+        /* In astronomical datasets, it can happen that a column is in the
+           format of __h__m__s or __d__m__s (where every '_' is a digit),
+           in these cases, they are actually coordinates (RA for first, Dec
+           for second). */
+        case GAL_TYPE_FLOAT64:
+          d[i]=strtod(token, &tailptr);
+          if( (*tailptr=='h' || *tailptr=='d') && isdigit(*(tailptr+1)) )
+            {
+              d[i] = ( *tailptr=='h'
+                       ? gal_units_ra_to_degree(token)
+                       : gal_units_dec_to_degree(token) );
+              if( !isnan(d[i]) ) tailptr=emptystr;
+            }
+          if( (db=info->array)
+              && ( (isnan(*db) && isnan(d[i])) || *db==d[i] ) )
+            d[i]=GAL_BLANK_FLOAT64;
+          break;
 
-  /* If a number couldn't be read properly, then report an error. */
-  if(data->type!=GAL_TYPE_STRING && *tailptr!='\0')
-    error_at_line(EXIT_FAILURE, 0, filename, lineno, "column %zu "
-                  "(`%s') couldn't be read as a `%s' number",
-                  colnum, token, gal_type_name(data->type, 1) );
+        default:
+          error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
+                __func__, data->type);
+        }
+
+      /* If a number couldn't be read properly, then report an error. */
+      if(data->type!=GAL_TYPE_STRING && *tailptr!='\0')
+        {
+          if( tailptr!=token
+              && isdigit(*(tailptr-1))
+              && *tailptr==':'
+              && isdigit(*(tailptr+1)) )
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "column %zu "
+                          "('%s') couldn't be read as a '%s' number.\n\n"
+                          "If it was meant to be celestial coordinates (RA "
+                          "or Dec), please use the '_h_m_s' format for RA "
+                          "or '_d_m_s' for Dec. The '_:_:_' format is "
+                          "ambiguous (can be used for both RA and Dec). "
+                          "Alternatively, you can use the column arithmetic "
+                          "operators 'ra-to-degree' or 'dec-to-degree' of "
+                          "'asttable' which also accept the '_:_:_' "
+                          "format. For more, please run this command\n\n"
+                          "   $ info gnuastro \"column arithmetic\"",
+                          colnum, token,
+                          gal_type_name(data->type, 1) );
+          else
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "column %zu "
+                          "('%s') couldn't be read as a '%s' number",
+                          colnum, token, gal_type_name(data->type, 1) );
+        }
+    }
 }
 
 
@@ -823,30 +900,32 @@ txt_read_token(gal_data_t *data, gal_data_t *info, char 
*token,
 
 
 static void
-txt_fill(char *in_line, char **tokens, size_t maxcolnum, gal_data_t *info,
-         gal_data_t *out, size_t rowind, char *filename, size_t lineno,
-         int inplace, int format)
+txt_fill(char *in_line, char **tokens, size_t maxcolnum,
+         gal_data_t *colinfo, gal_data_t *out, size_t rowind,
+         char *filename, size_t lineno, int inplace, int format)
 {
-  size_t i, n=0;
   gal_data_t *data;
   int notenoughcols=0;
-  char *end, *line, *aline=NULL;
+  size_t i, n=0, strwidth;
+  char *end, *line, *tmpstr, *aline=NULL;
 
   /* Make a copy of the input line if necessary. */
   if(inplace) line=in_line;
   else
     {
       gal_checkset_allocate_copy(in_line, &line);
-      aline=line; /* We are going to change `line' during this function. */
+      aline=line; /* We are going to change 'line' during this function. */
     }
   end=line+strlen(line);
 
-  /* See explanations in `txt_info_from_first_row'. */
+  /* Remove the new-line character from the line. For more, see the top the
+     explanations in 'txt_info_from_first_row': 13 is the ASCII code for
+     the carriage return. */
   if( end>line+2 && *(end-2)==13 ) *(end-2)='\0';
   else if( *(end-1)=='\n' )        *(end-1)='\0';
 
-  /* Start parsing the line. Note that `n' and `maxcolnum' start from
-     one. */
+  /* Start parsing the line. Note that 'n' and 'maxcolnum' start from one
+     when entering this loop on the first time. */
   while(++n)
     {
       /* Break out of the parsing if we don't need the columns any
@@ -855,10 +934,10 @@ txt_fill(char *in_line, char **tokens, size_t maxcolnum, 
gal_data_t *info,
       if(n>maxcolnum) break;
 
       /* Set the pointer to the start of this token/column. See
-         explanations in `txt_info_from_first_row'. Note that an image has
-         a single `info' element for the whole array, while a table has one
+         explanations in 'txt_info_from_first_row'. Note that an image has
+         a single 'info' element for the whole array, while a table has one
          for each column. */
-      if( info[format==TXT_FORMAT_TABLE ? n-1 : 0].type == GAL_TYPE_STRING )
+      if( colinfo[format==TXT_FORMAT_TABLE ? n-1 : 0].type == GAL_TYPE_STRING )
         {
           /* Remove any delimiters and stop at the first non-delimiter. If
              we have reached the end of the line then its an error, because
@@ -866,16 +945,31 @@ txt_fill(char *in_line, char **tokens, size_t maxcolnum, 
gal_data_t *info,
           while(isspace(*line) || *line==',') ++line;
           if(*line=='\0') {notenoughcols=1; break;}
 
-          /* Everything is good, set the pointer and increment the line to
-             the end of the allocated space for this string. */
-          line = (tokens[n]=line) + info[n-1].disp_width;
-          if(line<end) *line++='\0';
+          /* We are at the start of the string. Allocate space for, and
+             copy the necessary number of characters into the 'tmpstr'
+             string. We need to allocate this because the string column may
+             be immediately (next character) followed by the next
+             column. This leaves us no space to put the '\0' character. */
+          strwidth=colinfo[n-1].disp_width;
+          errno=0;
+          tmpstr=malloc(strwidth+1);
+          if(tmpstr==NULL)
+            error(EXIT_FAILURE, errno, "%s: %zu bytes couldn't be allocated "
+                  "for variable 'tmpstr'", __func__, strwidth+1);
+          if(line+strwidth<end) strncpy(tmpstr, line, strwidth);
+          else                  strncpy(tmpstr, line, end-line);
+          tmpstr[strwidth]='\0';
+          tokens[n]=tmpstr;
+
+          /* Increment the line pointer beyond to the next token.*/
+          line += strwidth;
         }
       else
         {
-          /* If we have reached the end of the line, then `strtok_r' will
+          /* If we have reached the end of the line, then 'strtok_r' will
              return a NULL pointer. */
-          tokens[n]=strtok_r(n==1?line:NULL, GAL_TXT_DELIMITERS, &line);
+          tmpstr=strtok_r(n==1?line:NULL, GAL_TXT_DELIMITERS, &line);
+          gal_checkset_allocate_copy(tmpstr, &tokens[n]);
           if(tokens[n]==NULL) {notenoughcols=1; break;}
         }
     }
@@ -885,7 +979,7 @@ txt_fill(char *in_line, char **tokens, size_t maxcolnum, 
gal_data_t *info,
     error_at_line(EXIT_FAILURE, 0, filename, lineno, "not enough columns in "
                   "this line. Previous (uncommented) lines in this file had "
                   "%zu columns, but this line has %zu columns", maxcolnum,
-                  n-1); /* This must be `n-1' (since n starts from 1). */
+                  n-1); /* This must be 'n-1' (since n starts from 1). */
 
   /* For a sanity check:
   printf("row: %zu: ", rowind+1);
@@ -895,19 +989,20 @@ txt_fill(char *in_line, char **tokens, size_t maxcolnum, 
gal_data_t *info,
 
   /* Read the desired tokens into the columns that need them. Note that
      when a blank value is defined for the column, the column's array
-     pointer (`info[col->status-1]') is not NULL and points to the blank
-     value. For strings, this will actually be a string. */
+     pointer ('colinfo[col->status-1]') is not NULL and points to the blank
+     value. For strings (or when the blank value is actually a string),
+     this will actually be a string. */
   switch(out->ndim)
     {
     case 1:
       for(data=out; data!=NULL; data=data->next)
-        txt_read_token(data, &info[data->status-1], tokens[data->status],
+        txt_read_token(data, &colinfo[data->status-1], tokens[data->status],
                        rowind, filename, lineno, data->status);
       break;
 
     case 2:
       for(i=0;i<out->dsize[1];++i)
-        txt_read_token(out, info, tokens[i+1], rowind * out->dsize[1] + i,
+        txt_read_token(out, colinfo, tokens[i+1], rowind * out->dsize[1] + i,
                        filename, lineno, i+1);
       break;
 
@@ -916,6 +1011,11 @@ txt_fill(char *in_line, char **tokens, size_t maxcolnum, 
gal_data_t *info,
             "datasets acceptable", __func__);
     }
 
+  /* Clean up the strings of each token within the tokens array, and set
+     the freed pointers to NULL. */
+  for(i=0;i<maxcolnum+1;++i)
+    if(tokens[i]) {free(tokens[i]); tokens[i]=NULL;}
+
   /* Clean up. */
   if(inplace==0) free(aline);
 }
@@ -937,22 +1037,22 @@ txt_read(char *filename, gal_list_str_t *lines, size_t 
*dsize,
   gal_data_t *out=NULL;
   gal_list_sizet_t *ind;
   size_t one=1, maxcolnum=0, rowind=0, lineno=0, ndim;
-  size_t linelen=10;        /* `linelen' will be increased by `getline'. */
+  size_t linelen=10;        /* 'linelen' will be increased by 'getline'. */
 
-  /* `filename' and `lines' cannot both be non-NULL. */
+  /* 'filename' and 'lines' cannot both be non-NULL. */
   test = (filename!=NULL) + (lines!=NULL);
   if( test!=1 )
-    error(EXIT_FAILURE, 0, "%s: one of the `filename' and `lines' "
+    error(EXIT_FAILURE, 0, "%s: one of the 'filename' and 'lines' "
           "arguments must be NULL, but they are both %s", __func__,
           test==2 ? "non-NULL" : "NULL");
 
   /* Allocate the space necessary to keep a copy of each line as we parse
-     it. Note that `getline' is going to later `realloc' this space to fit
+     it. Note that 'getline' is going to later 'realloc' this space to fit
      the line length. */
   errno=0;
   line=malloc(linelen*sizeof *line);
   if(line==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `line'",
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'line'",
           __func__, linelen*sizeof *line);
 
   /* Allocate all the desired columns for output. We will be reading the
@@ -994,7 +1094,7 @@ txt_read(char *filename, gal_list_str_t *lines, size_t 
*dsize,
     case TXT_FORMAT_IMAGE:
       if(info->next)
         error(EXIT_FAILURE, 0, "%s: currently reading only one image (2d "
-              "array) from a text file is possible, the `info' input has "
+              "array) from a text file is possible, the 'info' input has "
               "more than one element", __func__);
       ndim=2;
       maxcolnum=dsize[1];
@@ -1011,13 +1111,13 @@ txt_read(char *filename, gal_list_str_t *lines, size_t 
*dsize,
 
   /* Allocate the space to keep the pointers to each token in the
      line. This is done here to avoid having to allocate/free this array
-     for each line in `txt_fill_columns'. Note that the column numbers are
+     for each line in 'txt_fill_columns'. Note that the column numbers are
      counted from one (unlike indexes that are counted from zero), so we
-     need `maxcolnum+1' elements in the array of tokens.*/
+     need 'maxcolnum+1' elements in the array of tokens.*/
   errno=0;
-  tokens=malloc((maxcolnum+1)*sizeof *tokens);
+  tokens=calloc(maxcolnum+1, sizeof *tokens);
   if(tokens==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `tokens'",
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'tokens'",
           __func__, (maxcolnum+1)*sizeof *tokens);
 
   if(filename)
@@ -1113,13 +1213,13 @@ txt_stdin_has_contents(long timeout_microsec)
   tv.tv_sec  = 0;
   tv.tv_usec = timeout_microsec;
 
-  /* Initialize `fd_set'. */
+  /* Initialize 'fd_set'. */
   FD_ZERO(&fds);
 
   /* Set standard input (STDIN_FILENO is 0) as the FD that must be read. */
   FD_SET(STDIN_FILENO, &fds);
 
-  /* `select' takes the last file descriptor value + 1 in the fdset to
+  /* 'select' takes the last file descriptor value + 1 in the fdset to
      check, the fdset for reads, writes, and errors.  We are only passing
      in reads.  the last parameter is the timeout.  select will return if
      an FD is ready or the timeout has occurred. */
@@ -1138,22 +1238,22 @@ gal_txt_stdin_read(long timeout_microsec)
 {
   char *line;
   gal_list_str_t *out=NULL;
-  size_t lineno=0, linelen=10;/* `linelen' will be increased by `getline'. */
+  size_t lineno=0, linelen=10;/* 'linelen' will be increased by 'getline'. */
 
   /* If there is nothing  */
   if( txt_stdin_has_contents(timeout_microsec) )
     {
       /* Allocate the space necessary to keep a copy of each line as we
-         parse it. Note that `getline' is going to later `realloc' this
+         parse it. Note that 'getline' is going to later 'realloc' this
          space to fit the line length. */
       errno=0;
       line=malloc(linelen*sizeof *line);
       if(line==NULL)
-        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `line'",
+        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'line'",
               __func__, linelen*sizeof *line);
 
       /* Read the whole standard input. We are using getline because it can
-         deal with a `NULL' in the input, while also handing allocation
+         deal with a 'NULL' in the input, while also handing allocation
          issues while reading (allocating by line, not by a fixed buffer
          size). */
       while( getline(&line, &linelen, stdin) != -1 )
@@ -1261,7 +1361,7 @@ make_fmts_for_printf(gal_data_t *datall, int leftadjust, 
size_t *len)
 
       /* Print the result into the allocated string and add its length to
          the final length of the overall format statement. The space in the
-         end of `fmts[i*2]' is to ensure that the columns don't merge, even
+         end of 'fmts[i*2]' is to ensure that the columns don't merge, even
          if the printed string is larger than the expected width. */
       if(data->disp_precision > 0)
         *len += 1 + sprintf(fmts[i*FMTS_COLS], "%%%s%d.%d%s%s ",
@@ -1354,15 +1454,15 @@ txt_write_metadata(FILE *fp, gal_data_t *datall, char 
**fmts)
 
 
   /* When there are more than 9 columns, we don't want to have cases
-     like `# Column 1 :' (note the space between `1' and `:', this
+     like '# Column 1 :' (note the space between '1' and ':', this
      space won't exist for the 2 digit colum numbers).
 
      To do this, we are first allocating and printing a string long
-     enough to keep the final column's `N:'. Then, for each column, we
-     print only the number into the allocated space and put the `:' in
+     enough to keep the final column's 'N:'. Then, for each column, we
+     print only the number into the allocated space and put the ':' in
      manually immediately after the number. Note that the initial
-     `asprintf' put a `\0' in the allocated space, so we can safely
-     over-write the one that `sprintf' puts with a `:' for the columns
+     'asprintf' put a '\0' in the allocated space, so we can safely
+     over-write the one that 'sprintf' puts with a ':' for the columns
      that have the same number of digits as the final column. */
   i=0;
   if( asprintf(&nstr, "%zu:", num)<0 )
@@ -1370,7 +1470,7 @@ txt_write_metadata(FILE *fp, gal_data_t *datall, char 
**fmts)
   nlen=strlen(nstr);
   for(data=datall; data!=NULL; data=data->next)
     {
-      /* Print the number into the number string, then add the `:'
+      /* Print the number into the number string, then add the ':'
          immediately after the number. */
       sprintf(nstr, "%zu", i+1);
       for(j=1;j<nlen;++j)
@@ -1396,8 +1496,77 @@ txt_write_metadata(FILE *fp, gal_data_t *datall, char 
**fmts)
 
 
 
+static void
+txt_write_keys(FILE *fp, struct gal_fits_list_key_t **keylist)
+{
+  char *ending;
+  char *valuestr;
+  gal_fits_list_key_t *tmp, *ttmp;
+
+  tmp=*keylist;
+  while(tmp!=NULL)
+    {
+      /* If a title is requested, only put a title. */
+      if(tmp->title)
+        {
+          fprintf(fp, "# -------------\n# %s\n# -------------\n",
+                  tmp->title);
+          if(tmp->tfree) free(tmp->title);
+        }
+      else
+        {
+          /* For a string type, we need to return a pointer to the
+             string. */
+          valuestr = ( tmp->type==GAL_TYPE_STRING
+                       ? tmp->value
+                       : gal_type_to_string(tmp->value, tmp->type, 1) );
+
+          /* If a comment is requested, parepare it. */
+          ending=NULL;
+          if(tmp->unit)
+            {
+              if( asprintf(&ending, " / [%s] %s", tmp->unit,
+                           tmp->comment?tmp->comment:"")==-1 )
+                error(EXIT_FAILURE, errno, "%s: asprintf error for name",
+                      __func__);
+            }
+          else if(tmp->comment)
+            {
+              if( asprintf(&ending, " / %s", tmp->comment)==-1 )
+                error(EXIT_FAILURE, errno, "%s: asprintf error for name",
+                      __func__);
+            }
+
+          /* Write the keyword value. */
+          fprintf(fp, "# [key] %s: %s%s\n", tmp->keyname,
+                  valuestr, ending?ending:"");
+
+          /* Clean up. */
+          if(ending)     free(ending);
+          if(tmp->kfree) free(tmp->keyname);
+          if(tmp->vfree) free(tmp->value);
+          if(tmp->cfree) free(tmp->comment);
+          if(tmp->ufree) free(tmp->unit);
+        }
+
+      /* Keep the pointer to the next keyword and free the allocated
+         space for this keyword.*/
+      ttmp=tmp->next;
+      free(tmp);
+      tmp=ttmp;
+    }
+
+  /* Set it to NULL so it isn't mistakenly used later. */
+  *keylist=NULL;
+}
+
+
+
+
+
 void
-gal_txt_write(gal_data_t *input, gal_list_str_t *comment, char *filename,
+gal_txt_write(gal_data_t *input, struct gal_fits_list_key_t **keylist,
+              gal_list_str_t *comment, char *filename,
               uint8_t colinfoinstdout)
 {
   FILE *fp;
@@ -1438,7 +1607,7 @@ gal_txt_write(gal_data_t *input, gal_list_str_t *comment, 
char *filename,
          elements. */
       if( input!=data && gal_dimension_is_different(input, data) )
         error(EXIT_FAILURE, 0, "%s: the input list of datasets must have the "
-              "same sizes (dimentionality and length along each dimension)",
+              "same sizes (dimensions and length along each dimension)",
               __func__);
     }
 
@@ -1468,6 +1637,9 @@ gal_txt_write(gal_data_t *input, gal_list_str_t *comment, 
char *filename,
       /* Write the comments if there were any. */
       for(strt=comment; strt!=NULL; strt=strt->next)
         fprintf(fp, "# %s\n", strt->v);
+
+      /* Write the keywords */
+      if(keylist) txt_write_keys(fp, keylist);
     }
   else
     fp=stdout;
@@ -1480,14 +1652,26 @@ gal_txt_write(gal_data_t *input, gal_list_str_t 
*comment, char *filename,
   switch(input->ndim)
     {
     case 1:
-      for(i=0;i<input->size;++i)                        /* Row.    */
-        {
-          j=0;
-          for(data=input;data!=NULL;data=data->next)    /* Column. */
-            txt_print_value(fp, data->array, data->type, i,
+      /* When the dataset is bring printed on standard output and its a
+         single number, don't print the column structure, because it will
+         add white-space characters which can be annoying when used in an
+         automatic script. */
+      if(fp==stdout && input->size==1 && input->next==NULL)
+        fprintf(fp, "%s\n",
+                gal_type_to_string(input->array, input->type, 0));
+
+      /* Dataset has more than one row AND more than one column, so follow
+         the basic text formatting (like extra white space to keep the
+         columns under each other). */
+      else
+        for(i=0;i<input->size;++i)                        /* Row.    */
+          {
+            j=0;
+            for(data=input;data!=NULL;data=data->next)    /* Column. */
+              txt_print_value(fp, data->array, data->type, i,
                             fmts[j++ * FMTS_COLS]);
-          fprintf(fp, "\n");
-        }
+            fprintf(fp, "\n");
+          }
       break;
 
 
@@ -1508,7 +1692,6 @@ gal_txt_write(gal_data_t *input, gal_list_str_t *comment, 
char *filename,
     }
 
 
-
   /* Clean up. */
   for(i=0;i<num;++i)
     {
diff --git a/lib/type.c b/lib/type.c
index af21dec..f70c58d 100644
--- a/lib/type.c
+++ b/lib/type.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2016-2019, Free Software Foundation, Inc.
+Copyright (C) 2016-2021, 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
@@ -57,7 +57,7 @@ gal_type_sizeof(uint8_t type)
 
       /* The parenthesis after sizeof is not a function, it is actually a
          type cast, so we have put a space between size of and the
-         parenthesis to highlight this. In C, `sizeof' is an operator, not
+         parenthesis to highlight this. In C, 'sizeof' is an operator, not
          a function.*/
     case GAL_TYPE_UINT8:     return sizeof (uint8_t);
     case GAL_TYPE_INT8:      return sizeof (int8_t);
@@ -70,25 +70,25 @@ gal_type_sizeof(uint8_t type)
 
     case GAL_TYPE_FLOAT32:
       if( sizeof (float) != 4 )
-        error(EXIT_FAILURE, 0, "%s: `float' is not 32 bits on this machine",
+        error(EXIT_FAILURE, 0, "%s: 'float' is not 32 bits on this machine",
               __func__);
       return sizeof (float);
 
     case GAL_TYPE_FLOAT64:
       if( sizeof (double) != 8 )
-        error(EXIT_FAILURE, 0, "%s: `double' is not 64 bits on this machine",
+        error(EXIT_FAILURE, 0, "%s: 'double' is not 64 bits on this machine",
               __func__);
       return sizeof (double);
 
     case GAL_TYPE_COMPLEX32:
       if( sizeof (float) != 4 )
-        error(EXIT_FAILURE, 0, "%s: `float' is not 32 bits on this machine",
+        error(EXIT_FAILURE, 0, "%s: 'float' is not 32 bits on this machine",
               __func__);
       return sizeof (gsl_complex_float);
 
     case GAL_TYPE_COMPLEX64:
       if( sizeof (double) != 8 )
-        error(EXIT_FAILURE, 0, "%s: `double` is not 64 bits on this machine",
+        error(EXIT_FAILURE, 0, "%s: 'double' is not 64 bits on this machine",
               __func__);
       return sizeof (gsl_complex);
 
@@ -237,7 +237,7 @@ gal_type_from_name(char *str)
 
 
 
-/* Put the minimum (or maximum for the `gal_data_type_max') value for the
+/* Put the minimum (or maximum for the 'gal_data_type_max') value for the
    type in the space (that must already be allocated before the call to
    this function) pointed to by in.  */
 void
@@ -351,8 +351,8 @@ gal_type_out(int first_type, int second_type)
 /*************************************************************
  **************         To/from string         ***************
  *************************************************************/
-/* Write the bit (0 or 1) contents of `in' into a string ready for
-   printing. `size' is used to determine the number of bytes to print. The
+/* Write the bit (0 or 1) contents of 'in' into a string ready for
+   printing. 'size' is used to determine the number of bytes to print. The
    output string will be dynamically allocated within this function. This
    can be useful for easy checking of bit flag values, for example in an
    expression like below:
@@ -384,8 +384,8 @@ gal_type_bit_string(void *in, size_t size)
 
 
 
-/* Write the contents of memory that `ptr' points to as a string of type
-   `type'.*/
+/* Write the contents of memory that 'ptr' points to as a string of type
+   'type'.*/
 #define TO_STRING(CTYPE, FMT) {                                         \
   if( asprintf(&str, FMT, *(CTYPE *)ptr)<0 )                            \
     error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__); }
@@ -442,18 +442,18 @@ gal_type_to_string(void *ptr, uint8_t type, int 
quote_if_str_has_space)
 
 
 /* Read a string as a given data type and put a the pointer to it in
-   *out. When the input `*out!=NULL', then it is assumed to be allocated
-   and the value will be simply put there. If `*out==NULL', then space will
+   *out. When the input '*out!=NULL', then it is assumed to be allocated
+   and the value will be simply put there. If '*out==NULL', then space will
    be allocated for the given type and the string's value (in the given
    type) will be stored there.
 
-   Note that when we are dealing with a string type, `*out' should be
-   interpretted as `char **' (one element in an array of pointers to
-   different strings). In other words, `out' should be `char ***'.
+   Note that when we are dealing with a string type, '*out' should be
+   interpretted as 'char **' (one element in an array of pointers to
+   different strings). In other words, 'out' should be 'char ***'.
 
    This function can be used to fill in arrays of numbers from strings (in
    an already allocated data structure), or add nodes to a linked list. For
-   an array, you have to pass the pointer to the `i'th element where you
+   an array, you have to pass the pointer to the 'i'th element where you
    want the value to be stored, for example &(array[i]).
 
    If parsing was successful, it will return a 0. If there was a problem,
@@ -487,7 +487,7 @@ gal_type_from_string(void **out, char *string, uint8_t type)
       break;
 
     /* String, just allocate and copy the string and keep its pointer in
-       the place `*out' points to (for strings, `*out' is `char **'). */
+       the place '*out' points to (for strings, '*out' is 'char **'). */
     case GAL_TYPE_STRING:
       gal_checkset_allocate_copy(string, value);
       break;
@@ -502,8 +502,8 @@ gal_type_from_string(void **out, char *string, uint8_t type)
         status=1;
       else
         {
-          if(type==GAL_TYPE_FLOAT32) *(float *) value=d;
-          else                            *(double *) value=d;
+          if(type==GAL_TYPE_FLOAT32) *(float  *) value=d;
+          else                       *(double *) value=d;
         }
       break;
 
@@ -516,10 +516,10 @@ gal_type_from_string(void **out, char *string, uint8_t 
type)
         switch(type)
           {
           /* The signed values can easily be put in. */
-          case GAL_TYPE_INT8:         *(int8_t *)    value = l; break;
-          case GAL_TYPE_INT16:        *(int16_t *)   value = l; break;
-          case GAL_TYPE_INT32:        *(int32_t *)   value = l; break;
-          case GAL_TYPE_INT64:        *(int64_t *)   value = l; break;
+          case GAL_TYPE_INT8:  *(int8_t  *) value = l; break;
+          case GAL_TYPE_INT16: *(int16_t *) value = l; break;
+          case GAL_TYPE_INT32: *(int32_t *) value = l; break;
+          case GAL_TYPE_INT64: *(int64_t *) value = l; break;
 
           /* For the unsigned types, the value has to be positive, so if
              the input was negative, then just return a status of one and
@@ -530,10 +530,10 @@ gal_type_from_string(void **out, char *string, uint8_t 
type)
             else
               switch(type)
                 {
-                case GAL_TYPE_UINT8:  *(uint8_t *)   value=l;   break;
-                case GAL_TYPE_UINT16: *(uint16_t *)  value=l;   break;
-                case GAL_TYPE_UINT32: *(uint32_t *)  value=l;   break;
-                case GAL_TYPE_UINT64: *(uint64_t *)  value=l;   break;
+                case GAL_TYPE_UINT8:  *(uint8_t  *) value=l;   break;
+                case GAL_TYPE_UINT16: *(uint16_t *) value=l;   break;
+                case GAL_TYPE_UINT32: *(uint32_t *) value=l;   break;
+                case GAL_TYPE_UINT64: *(uint64_t *) value=l;   break;
                 default:
                   error(EXIT_FAILURE, 0, "%s: type code %d not recognized",
                         __func__, type);
@@ -561,9 +561,9 @@ void *
 gal_type_string_to_number(char *string, uint8_t *type)
 {
   void *ptr, *out;
-  int fnz=-1, lnz=0;     /* `F'irst (or `L'ast) `N'on-`Z'ero. */
-  char *tailptr, *cp;
+  int fnz=-1, lnz=0;     /* 'F'irst (or 'L'ast) 'N'on-'Z'ero. */
   uint8_t forcedfloat=0;
+  char *c, *tailptr, *cp;
 
   /* Define initial spaces to keep the value. */
   uint8_t   u8;   int8_t   i8;      uint16_t u16;   int16_t i16;
@@ -575,6 +575,12 @@ gal_type_string_to_number(char *string, uint8_t *type)
   if(*tailptr=='f') { if(tailptr[1]=='\0') forcedfloat=1; else return NULL; }
   else if (*tailptr!='\0')  return NULL;
 
+  /* The number has been parsed successfully as a number. But if it
+     contains a '.', then it must a "forced" float also. This won't be a
+     problem in scenarios like '.2', but people may use '2.' or '2.0' to
+     force a float and this loop is necessary in such cases. */
+  for(c=string; *c!='\0'; ++c) if(*c=='.') { forcedfloat=1; break; }
+
   /* See if the number is actually an integer: */
   if( forcedfloat==0 && ceil(d) == d )
     {
@@ -592,11 +598,11 @@ gal_type_string_to_number(char *string, uint8_t *type)
         {
           /* Note that the blank values are set to the maximum values in
              unsigned types. A blank value should be given as a blank
-             string to this function (`GAL_BLANK_STRING'). So, to avoid
+             string to this function ('GAL_BLANK_STRING'). So, to avoid
              confusing situations (for example when the user gives 255), if
              the value is equal to the given maximum of the given type,
              we'll assign it to a larger type. In other words, we won't be
-             using the `<=MAX', but `<MAX'. */
+             using the '<=MAX', but '<MAX'. */
           if     (d<UINT8_MAX)  { u8=d;  ptr=&u8;  *type=GAL_TYPE_UINT8;  }
           else if(d<UINT16_MAX) { u16=d; ptr=&u16; *type=GAL_TYPE_UINT16; }
           else if(d<UINT32_MAX) { u32=d; ptr=&u32; *type=GAL_TYPE_UINT32; }
@@ -617,13 +623,13 @@ gal_type_string_to_number(char *string, uint8_t *type)
          store the value as a double precision.
 
          Note that the number can have non-digit characters that we don't
-         want, like: `.', `e', `E', `,'. */
+         want, like: '.', 'e', 'E', ','. */
       for(cp=string;*cp!='\0';++cp)
         if(isdigit(*cp) && *cp!='0' && fnz==-1)
           fnz=cp-string;
 
-      /* In the previous loop, we went to the end of the string, so `cp'
-         now points to its `\0'. We just have to iterate backwards! */
+      /* In the previous loop, we went to the end of the string, so 'cp'
+         now points to its '\0'. We just have to iterate backwards! */
       for(;cp!=string;--cp)
         if(isdigit(*cp) && *cp!='0')
           {
diff --git a/lib/units.c b/lib/units.c
new file mode 100644
index 0000000..20146e2
--- /dev/null
+++ b/lib/units.c
@@ -0,0 +1,333 @@
+/*********************************************************************
+Units -- Convert data from one unit to other.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Kartik Ohri <kartikohri13@gmail.com>
+Contributing author(s):
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Copyright (C) 2020-2021, 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 <math.h>
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gnuastro/type.h>
+#include <gnuastro/pointer.h>
+
+
+
+/**********************************************************************/
+/****************      Functions to parse strings     *****************/
+/**********************************************************************/
+/* Parse the input string consisting of numbers separated by given
+   delimiter into an array. */
+int
+gal_units_extract_decimal(char *convert, const char *delimiter,
+                          double *args, size_t n)
+{
+  size_t i = 0;
+  char *copy, *token, *end;
+
+  /* Create a copy of the string to be parsed and parse it. This is because
+     it will be modified during the parsing. */
+  copy=strdup(convert);
+  do
+    {
+      /* Check if the required number of arguments are passed */
+      if(i==n+1)
+        {
+          free(copy);
+          error(0, 0, "%s: input '%s' exceeds maximum number of arguments "
+                "(%zu)", __func__, convert, n);
+          return 0;
+        }
+
+      /* Extract the substring till the next delimiter */
+      token=strtok(i==0?copy:NULL, delimiter);
+      if(token)
+        {
+          /* Parse extracted string as a number, and check if it worked. */
+          args[i++] = strtod (token, &end);
+          if (*end && *end != *delimiter)
+            {
+              /* In case a warning is necessary
+              error(0, 0, "%s: unable to parse element %zu in '%s'\n",
+                    __func__, i, convert);
+              */
+              free(copy);
+              return 0;
+            }
+        }
+    }
+  while(token && *token);
+  free (copy);
+
+  /* Check if the number of elements parsed. */
+  if (i != n)
+    {
+      /* In case a warning is necessary
+      error(0, 0, "%s: input '%s' must contain %lu numbers, but has "
+            "%lu numbers\n", __func__, convert, n, i);
+      */
+      return 0;
+    }
+
+  /* Numbers are written, return successfully. */
+  return 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**********************************************************************/
+/****************      Convert string to decimal      *****************/
+/**********************************************************************/
+
+/* Parse the right ascension input as a string in form of hh:mm:ss to a
+ * single decimal value calculated by (hh + mm / 60 + ss / 3600 ) * 15. */
+double
+gal_units_ra_to_degree(char *convert)
+{
+  double val[3];
+  double decimal=0.0;
+
+  /* Check whether the string is successfully parsed */
+  if(gal_units_extract_decimal(convert, ":hms", val, 3))
+    {
+      /* Check whether the first value is in within limits, and add it. */
+      if(val[0]<0.0 || val[0]>24.0) return NAN;
+      decimal += val[0];
+
+      /* Check whether value of minutes is within limits, and add it. */
+      if(val[1]<0.0 || val[1]>60.0) return NAN;
+      decimal += val[1] / 60;
+
+      /* Check whether value of seconds is in within limits, and add it. */
+      if(val[2]<0.0 || val[2]>60.0) return NAN;
+      decimal += val[2] / 3600;
+
+      /* Convert value to degrees and return. */
+      decimal *= 15.0;
+      return decimal;
+    }
+  else return NAN;
+
+  /* Control shouldn't reach this point. If it does, its a bug! */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+        "problem. Control should not reach the end of this function",
+        __func__, PACKAGE_BUGREPORT);
+  return NAN;
+}
+
+
+
+
+
+/* Parse the declination input as a string in form of dd:mm:ss to a decimal
+ * calculated by (dd + mm / 60 + ss / 3600 ). */
+double
+gal_units_dec_to_degree(char *convert)
+{
+  int sign;
+  double val[3], decimal=0.0;
+
+  /* Parse the values in the input string. */
+  if(gal_units_extract_decimal(convert, ":dms", val, 3))
+    {
+      /* Check whether the first value is in within limits. */
+      if(val[0]<-90.0 || val[0]>90.0) return NAN;
+
+      /* If declination is negative, the first value in the array will be
+         negative and all other values will be positive. In that case, we
+         set sign equal to -1. Therefore, we multiply the first value by
+         sign to make it positive. The final answer is again multiplied by
+         sign to make its sign same as original. */
+      sign = val[0]<0.0 ? -1 : 1;
+      decimal += val[0] * sign;
+
+      /* Check whether value of arc-minutes is in within limits. */
+      if(val[1]<0.0 || val[1]>60.0) return NAN;
+      decimal += val[1] / 60;
+
+      /* Check whether value of arc-seconds is in within limits */
+      if (val[2] < 0.0 || val[2] > 60.0) return NAN;
+      decimal += val[2] / 3600;
+
+      /* Make the sign of the decimal value same as input and return. */
+      decimal *= sign;
+      return decimal;
+    }
+  else return NAN;
+
+  /* Control shouldn't reach this point. If it does, its a bug! */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+        "problem. Control should not reach the end of this function",
+        __func__, PACKAGE_BUGREPORT);
+  return NAN;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**********************************************************************/
+/****************      Convert decimal to string      *****************/
+/**********************************************************************/
+
+/* Max-length of output string. */
+#define UNITS_RADECSTR_MAXLENGTH 50
+
+/* Parse the right ascension input as a decimal to a string in form of
+   hh:mm:ss.ss . */
+char *
+gal_units_degree_to_ra(double decimal, int usecolon)
+{
+  size_t nchars;
+  int hours=0, minutes=0;
+  float seconds=0.0; /* For sub-second accuracy */
+
+  /* Allocate a long string which is large enough for string of format
+     hh:mm:ss.ss and sign */
+  char *ra=gal_pointer_allocate(GAL_TYPE_UINT8, UNITS_RADECSTR_MAXLENGTH,
+                                0, __func__, "ra");
+
+  /* Check if decimal value is within bounds otherwise return error */
+  if (decimal<0 || decimal>360)
+    {
+      error (0, 0, "%s: value of decimal should be between be 0 and 360, "
+             "but is %g\n", __func__, decimal);
+      return NULL;
+    }
+
+  /* Divide decimal value by 15 and extract integer part of decimal value
+     to obtain hours */
+  decimal /= 15.0;
+  hours = (int)decimal;
+
+  /* Subtract hours from decimal and multiply remaining value by 60 to
+     obtain minutes. */
+  minutes = (int)((decimal - hours) * 60);
+
+  /* Subtract hours and minutes from decimal and multiply remaining value
+     by 3600 to obtain seconds. */
+  seconds = (decimal - hours - minutes / 60.0) * 3600;
+
+  /* Format the extracted hours, minutes and seconds as a string with
+     leading zeros if required, in hh:mm:ss format */
+  nchars = snprintf(ra, UNITS_RADECSTR_MAXLENGTH-1,
+                    usecolon ? "%02d:%02d:%g" : "%02dh%02dm%gs",
+                    hours, minutes, seconds);
+  if(nchars>UNITS_RADECSTR_MAXLENGTH)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to address "
+          "the problem. The output string has an unreasonable length of "
+          "%zu characters", __func__, PACKAGE_BUGREPORT, nchars);
+
+  /* Return the final string. */
+  return ra;
+}
+
+
+
+
+
+/* Parse the declination input as a decimal to a string in form of dd:mm:ss*/
+char *
+gal_units_degree_to_dec(double decimal, int usecolon)
+{
+  size_t nchars;
+  float arc_seconds=0.0;
+  int sign, degrees=0, arc_minutes=0;
+
+  /* Allocate string of fixed length which is large enough for string of
+   * format hh:mm:ss.ss and sign */
+  char *dec=gal_pointer_allocate(GAL_TYPE_UINT8, UNITS_RADECSTR_MAXLENGTH,
+                                 0, __func__, "ra");
+
+  /* Check if decimal value is within bounds otherwise return error */
+  if(decimal<-90 || decimal>90)
+    {
+      error (0, 0, "%s: value of decimal should be between be -90 and 90, "
+             "but is %g\n", __func__, decimal);
+      return NULL;
+    }
+
+  /* If declination is negative, we set 'sign' equal to -1. We multiply the
+     decimal by to make sure it is positive. We then extract degrees,
+     arc-minutes and arc-seconds from the decimal. Finally, we add a minus
+     sign in beginning of string if input was negative. */
+  sign = decimal<0.0 ? -1 : 1;
+  decimal *= sign;
+
+  /* Extract integer part of decimal value to obtain degrees. */
+  degrees=(int)decimal;
+
+  /* Subtract degrees from decimal and multiply remaining value by 60 to
+     obtain arc-minutes. */
+  arc_minutes=(int)( (decimal - degrees) * 60 );
+
+  /* Subtract degrees and arc-minutes from decimal and multiply remaining
+     value by 3600 to obtain arc-seconds. */
+  arc_seconds = (decimal - degrees - arc_minutes / 60.0) * 3600;
+
+  /* Format the extracted degrees, arc-minutes and arc-seconds as a string
+     with leading zeros if required, in hh:mm:ss format with correct
+     sign. */
+  nchars = snprintf(dec, UNITS_RADECSTR_MAXLENGTH-1,
+                    usecolon ? "%s%02d:%02d:%g" : "%s%02dd%02dm%gs",
+                    sign<0?"-":"+", degrees, arc_minutes, arc_seconds);
+  if(nchars>UNITS_RADECSTR_MAXLENGTH)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to address "
+          "the problem. The output string has an unreasonable length of "
+          "%zu characters", __func__, PACKAGE_BUGREPORT, nchars);
+
+  /* Return the final string. */
+  return dec;
+}
diff --git a/lib/wcs.c b/lib/wcs.c
index d0f9a77..0d2a508 100644
--- a/lib/wcs.c
+++ b/lib/wcs.c
@@ -5,7 +5,7 @@ This is part of GNU Astronomy Utilities (Gnuastro) package.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
@@ -32,16 +32,30 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <assert.h>
 
 #include <gsl/gsl_linalg.h>
+#include <wcslib/wcsmath.h>
 
 #include <gnuastro/wcs.h>
 #include <gnuastro/tile.h>
 #include <gnuastro/fits.h>
 #include <gnuastro/pointer.h>
 #include <gnuastro/dimension.h>
+#include <gnuastro/statistics.h>
 #include <gnuastro/permutation.h>
 
+#include <gnuastro-internal/checkset.h>
+
+#if GAL_CONFIG_HAVE_WCSLIB_DIS_H
+#include <wcslib/dis.h>
+#include <gnuastro-internal/wcsdistortion.h>
+#endif
+
+
+
 
 
+/* Static functions on for this file. */
+static void
+gal_wcs_to_cd(struct wcsprm *wcs);
 
 
 
@@ -50,6 +64,73 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /*************************************************************
  ***********               Read WCS                ***********
  *************************************************************/
+/* It may happen that both the PC+CDELT and CD matrices are present in a
+   file. But in some cases, they may not result in the same rotation
+   matrix. So we need to let the user know about this problem with the FITS
+   file, and as a default behavior, we'll disable the PC matrix (which also
+   needs a CDELT matrix (that may not have been written). */
+static void
+wcs_read_correct_pc_cd(struct wcsprm *wcs)
+{
+  int removepc=0;
+  size_t i, j, naxis=wcs->naxis;
+  double *cdfrompc=gal_pointer_allocate(GAL_TYPE_FLOAT64, naxis*naxis, 0,
+                                        __func__, "cdfrompc");
+
+  /* A small sanity check. */
+  if(wcs->cdelt==NULL)
+    error(EXIT_FAILURE, 0, "%s: the WCS structure has no 'cdelt' array, "
+          "please contact us at %s to see what the problem is", __func__,
+          PACKAGE_BUGREPORT);
+
+  /* Multiply the PC matrix with the CDELT matrix. */
+  for(i=0;i<naxis;++i)
+    for(j=0;j<naxis;++j)
+      cdfrompc[i*naxis+j] = wcs->cdelt[i] * wcs->pc[i*naxis+j];
+
+  /* Make sure the file's CD matrix is the same as the CD matrix that is
+     derived from the PC+CDELT matrix above. We'll divide the difference by
+     the samller value and if the result is larger than 1e-6, then we'll
+     consider it different. */
+  for(i=0;i<naxis*naxis;++i)
+    if( fabs( wcs->cd[i] - cdfrompc[i] )
+        / ( fabs(wcs->cd[i]) < fabs(cdfrompc[i])
+            ? fabs(wcs->cd[i])
+            : fabs(cdfrompc[i]) )
+        > 1e-5 )
+      { removepc=1; break; }
+
+  /* If the two matrices are different, then print the warning and remove
+     the PC+CDELT matrices to only keep the CD matrix. */
+  if(removepc)
+    {
+      /* Let the user know that there is a problem in the file. */
+      error(EXIT_SUCCESS, 0, "the WCS structure has both the PC matrix "
+            "and CD matrix. However, the two don't match and there is "
+            "no way to know which one was intended by the creator of "
+            "this file. THIS PROGRAM WILL ASSUME THE CD MATRIX AND "
+            "CONTINUE. BUT THIS MAY BE WRONG! To avoid confusion and "
+            "wrong results, its best to only use one of them in your "
+            "FITS file. You can use Gnuastro's 'astfits' program to "
+            "remove any that you want (please run 'info astfits' for "
+            "more). For example if you want to delete the PC matrix "
+            "you can use this command: 'astfits file.fits --delete=PC1_1 "
+            "--delete=PC1_2 --delete=PC2_1 --delete=PC2_2'");
+
+      /* Set the PC matrix to be equal to the CD matrix, and set the CDELTs
+         to 1. */
+      for(i=0;i<naxis;++i) wcs->cdelt[i] = 1.0f;
+      for(i=0;i<naxis*naxis;++i) wcs->pc[i] = wcs->cd[i];
+    }
+
+  /* Clean up. */
+  free(cdfrompc);
+}
+
+
+
+
+
 /* Read the WCS information from the header. Unfortunately, WCS lib is
    not thread safe, so it needs a mutex. In case you are not using
    multiple threads, just pass a NULL pointer as the mutex.
@@ -72,12 +153,18 @@ gal_wcs_read_fitsptr(fitsfile *fptr, size_t hstartwcs, 
size_t hendwcs,
                      int *nwcs)
 {
   /* Declaratins: */
+  int sumcheck;
+  size_t i, fulllen;
   int nkeys=0, status=0;
   struct wcsprm *wcs=NULL;
   char *fullheader, *to, *from;
+  int fixstatus[NWCSFIX]={0};/* For the various wcsfix checks.          */
   int relax    = WCSHDR_all; /* Macro: use all informal WCS extensions. */
   int ctrl     = 0;          /* Don't report why a keyword wasn't used. */
   int nreject  = 0;          /* Number of keywords rejected for syntax. */
+  int fixctrl  = 1;          /* Correct non-standard units in wcsfix.   */
+  void *fixnaxis = NULL;     /* For now disable cylfix() with this      */
+                             /* (because it depends on image size).     */
 
   /* CFITSIO function: */
   if( fits_hdr2str(fptr, 1, NULL, 0, &fullheader, &nkeys, &status) )
@@ -113,7 +200,6 @@ gal_wcs_read_fitsptr(fitsfile *fptr, size_t hstartwcs, 
size_t hendwcs,
       /*******************************************************/
     }
 
-
   /* WCSlib function to parse the FITS headers. */
   status=wcspih(fullheader, nkeys, relax, ctrl, &nreject, nwcs, &wcs);
   if(status)
@@ -124,14 +210,45 @@ gal_wcs_read_fitsptr(fitsfile *fptr, size_t hstartwcs, 
size_t hendwcs,
               status, wcs_errmsg[status]);
       wcs=NULL; *nwcs=0;
     }
-  if (fits_free_memory(fullheader, &status) )
-    gal_fits_io_error(status, "problem in fitsarrayvv.c for freeing "
-                           "the memory used to keep all the headers");
-
 
   /* Set the internal structure: */
   if(wcs)
     {
+      /* It may happen that the WCS-related keyword values are stored as
+         strings (they have single-quotes around them). In this case,
+         WCSLIB will read the CRPIX and CRVAL values as zero. When this
+         happens do a small check and abort, while informing the user about
+         the problem. */
+      sumcheck=0;
+      for(i=0;i<wcs->naxis;++i)
+        {sumcheck += (wcs->crval[i]==0.0f) + (wcs->crpix[i]==0.0f);}
+      if(sumcheck==wcs->naxis*2)
+        {
+          /* We only care about the first set of characters in each
+             80-character row, so we don't need to parse the last few
+             characters anyway. */
+          fulllen=strlen(fullheader)-12;
+          for(i=0;i<fulllen;++i)
+            if( strncmp(fullheader+i, "CRVAL1  = '", 11) == 0 )
+              fprintf(stderr, "WARNING: WCS Keyword values are not "
+                      "numbers.\n\n"
+                      "WARNING: The values to the WCS-related keywords are "
+                      "enclosed in single-quotes. In the FITS standard "
+                      "this is how string values are stored, therefore "
+                      "WCSLIB is unable to read them AND WILL PUT ZERO IN "
+                      "THEIR PLACE (creating a wrong WCS in the output). "
+                      "Please update the respective keywords of the input "
+                      "to be numbers (see next line).\n\n"
+                      "WARNING: You can do this with Gnuastro's 'astfits' "
+                      "program and the '--update' option. The minimal WCS "
+                      "keywords that need a numerical value are: 'CRVAL1', "
+                      "'CRVAL2', 'CRPIX1', 'CRPIX2', 'EQUINOX' and "
+                      "'CD%%_%%' (or 'PC%%_%%', where the %% are integers), "
+                      "please see the FITS standard, and inspect your FITS "
+                      "file to identify the full set of keywords that you "
+                      "need correct (for example PV%%_%% keywords).\n\n");
+        }
+
       /* CTYPE is a mandatory WCS keyword, so if it hasn't been given (its
          '\0'), then the headers didn't have a WCS structure. However,
          WCSLIB still fills in the basic information (for example the
@@ -144,6 +261,73 @@ gal_wcs_read_fitsptr(fitsfile *fptr, size_t hstartwcs, 
size_t hendwcs,
         }
       else
         {
+          /* For a check (we can't use 'wcsprt(wcs)' because this WCS isn't
+             yet initialized).
+          printf("flag: %d\n", wcs->flag);
+          printf("NAXIS: %d\n", wcs->naxis);
+          printf("CRPIX: ");
+          for(i=0;i<wcs->naxis;++i)
+            { printf("%g, ", wcs->crpix[i]); } printf("\n");
+          printf("PC: ");
+          for(i=0;i<wcs->naxis*wcs->naxis;++i)
+            { printf("%g, ", wcs->pc[i]); } printf("\n");
+          printf("CDELT: ");
+          for(i=0;i<wcs->naxis;++i)
+            { printf("%g, ", wcs->cdelt[i]);} printf("\n");
+          printf("CD: ");
+          for(i=0;i<wcs->naxis*wcs->naxis;++i)
+            { printf("%g, ", wcs->cd[i]); } printf("\n");
+          printf("CRVAL: ");
+          for(i=0;i<wcs->naxis;++i)
+            { printf("%g, ", wcs->crval[i]); } printf("\n");
+          printf("CUNIT: ");
+          for(i=0;i<wcs->naxis;++i)
+            { printf("%s, ", wcs->cunit[i]); } printf("\n");
+          printf("CTYPE: ");
+          for(i=0;i<wcs->naxis;++i)
+            { printf("%s, ", wcs->ctype[i]); } printf("\n");
+          printf("LONPOLE: %f\n", wcs->lonpole);
+          printf("LATPOLE: %f\n", wcs->latpole);
+          */
+
+          /* Some datasets may use 'angstroms' (not case-sensitive) in the
+             third dimension instead of the standard 'angstrom' (note the
+             differing 's'). In this case WCSLIB (atleast until version
+             7.3) will not recognize it. We will therefore manually remove
+             the 's' before feeding the WCS structure to WCSLIB. */
+          if( wcs->naxis==3
+              && strlen(wcs->cunit[2])==9
+              && !strncasecmp(wcs->cunit[2], "angstroms", 9) )
+            wcs->cunit[2][8]='\0';
+
+          /* Fix non-standard WCS features. */
+          if( wcsfix(fixctrl, fixnaxis, wcs, fixstatus) )
+            {
+              if(fixstatus[CDFIX])
+                error(0, 0, "%s: (warning) wcsfix status for cdfix: %d",
+                      __func__, fixstatus[CDFIX]);
+              if(fixstatus[DATFIX])
+                error(0, 0, "%s: (warning) wcsfix status for datfix: %d",
+                      __func__, fixstatus[DATFIX]);
+#if GAL_CONFIG_HAVE_WCSLIB_OBSFIX
+              if(fixstatus[OBSFIX])
+                error(0, 0, "%s: (warning) wcsfix status for obsfix: %d",
+                      __func__, fixstatus[OBSFIX]);
+#endif
+              if(fixstatus[UNITFIX])
+                error(0, 0, "%s: (warning) wcsfix status for unitfix: %d",
+                      __func__, fixstatus[UNITFIX]);
+              if(fixstatus[SPCFIX])
+                error(0, 0, "%s: (warning) wcsfix status for spcfix: %d",
+                      __func__, fixstatus[SPCFIX]);
+              if(fixstatus[CELFIX])
+                error(0, 0, "%s: (warning) wcsfix status for celfix: %d",
+                      __func__, fixstatus[CELFIX]);
+              if(fixstatus[CYLFIX])
+                error(0, 0, "%s: (warning) wcsfix status for cylfix: %d",
+                      __func__, fixstatus[CYLFIX]);
+            }
+
           /* Set the WCS structure. */
           status=wcsset(wcs);
           if(status)
@@ -156,19 +340,32 @@ gal_wcs_read_fitsptr(fitsfile *fptr, size_t hstartwcs, 
size_t hendwcs,
               wcs=NULL;
               *nwcs=0;
             }
+          /* A correctly useful WCS is present. */
           else
-            /* A correctly useful WCS is present. When no PC matrix
-               elements were present in the header, the default PC matrix
-               (a unity matrix) is used. In this case WCSLIB doesn't set
-               `altlin' (and gives it a value of 0). In Gnuastro, later on,
-               we might need to know the type of the matrix used, so in
-               such a case, we will set `altlin' to 1. */
-            if(wcs->altlin==0) wcs->altlin=1;
+            {
+              /* According to WCSLIB in discussing 'altlin': "If none of
+                 these bits are set the PCi_ja representation results, i.e.
+                 wcsprm::pc and wcsprm::cdelt will be used as given". In
+                 effect it will also set the PC matrix to unity. So we can
+                 safely set it to '1' here because some parts of Gnuastro
+                 will later look into this. */
+              if(wcs->altlin==0) wcs->altlin=1;
+
+              /* If both the PC and CD matrix have been given, the first
+                 two bits of 'altlin' will be '1'. We need to make sure
+                 they are the same matrix, and let the user know if they
+                 aren't. */
+              if( (wcs->altlin & 1) && (wcs->altlin & 2) )
+                wcs_read_correct_pc_cd(wcs);
+            }
         }
     }
 
-
-  /* Return the WCS structure. */
+  /* Clean up and return. */
+  status=0;
+  if (fits_free_memory(fullheader, &status) )
+    gal_fits_io_error(status, "problem in freeing the memory used to "
+                      "keep all the headers");
   return wcs;
 }
 
@@ -204,6 +401,470 @@ gal_wcs_read(char *filename, char *hdu, size_t hstartwcs,
 
 
 
+struct wcsprm *
+gal_wcs_create(double *crpix, double *crval, double *cdelt,
+               double *pc, char **cunit, char **ctype, size_t ndim)
+{
+  size_t i;
+  int status;
+  struct wcsprm *wcs;
+  double equinox=2000.0f;
+
+  /* Allocate the memory necessary for the wcsprm structure. */
+  errno=0;
+  wcs=malloc(sizeof *wcs);
+  if(wcs==NULL)
+    error(EXIT_FAILURE, errno, "%zu for wcs in preparewcs", sizeof *wcs);
+
+  /* Initialize the structure (allocate all its internal arrays). */
+  wcs->flag=-1;
+  if( (status=wcsini(1, ndim, wcs)) )
+    error(EXIT_FAILURE, 0, "wcsini error %d: %s",
+          status, wcs_errmsg[status]);
+
+  /* Fill in all the important WCS structure parameters. */
+  wcs->altlin  = 0x1;
+  wcs->equinox = equinox;
+  for(i=0;i<ndim;++i)
+    {
+      wcs->crpix[i] = crpix[i];
+      wcs->crval[i] = crval[i];
+      wcs->cdelt[i] = cdelt[i];
+      if(cunit[i]) strcpy(wcs->cunit[i], cunit[i]);
+      if(ctype[i]) strcpy(wcs->ctype[i], ctype[i]);
+    }
+  for(i=0;i<ndim*ndim;++i) wcs->pc[i]=pc[i];
+
+  /* Set up the wcs structure with the constants defined above. */
+  status=wcsset(wcs);
+  if(status)
+    error(EXIT_FAILURE, 0, "wcsset error %d: %s", status,
+          wcs_errmsg[status]);
+
+  /* Return the output WCS. */
+  return wcs;
+}
+
+
+
+
+
+/* Extract the dimension name from CTYPE. */
+char *
+gal_wcs_dimension_name(struct wcsprm *wcs, size_t dimension)
+{
+  size_t i;
+  char *out;
+
+  /* Make sure a WCS pointer actually exists. */
+  if(wcs==NULL) return NULL;
+
+  /* Make sure the requested dimension is not larger than the number of
+     dimensions in the WCS. */
+  if(dimension >= wcs->naxis) return NULL;
+
+  /* Make a copy of the CTYPE value and set the first occurance of '-' to
+     '\0', to avoid the projection type. */
+  gal_checkset_allocate_copy(wcs->ctype[dimension], &out);
+  for(i=0;i<strlen(out);++i) if(out[i]=='-') out[i]='\0';
+
+  /* Return the output array. */
+  return out;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*************************************************************
+ ***********               Write WCS               ***********
+ *************************************************************/
+void
+gal_wcs_write_in_fitsptr(fitsfile *fptr, struct wcsprm *wcs)
+{
+  char *wcsstr;
+  int tpvdist, status=0, nkeyrec;
+
+  /* Prepare the main rotation matrix. Note that for TPV distortion, WCSLIB
+     versions 7.3 and before couldn't deal with the CDELT keys, so to be
+     safe, in such cases, we'll remove the effect of CDELT in the
+     'gal_wcs_to_cd' function. */
+  tpvdist=wcs->lin.disseq && !strcmp(wcs->lin.disseq->dtype[1], "TPV");
+  if( tpvdist ) gal_wcs_to_cd(wcs);
+  else          gal_wcs_decompose_pc_cdelt(wcs);
+
+  /* Clean up small errors in the PC matrix and CDELT values. */
+  gal_wcs_clean_errors(wcs);
+
+  /* Convert the WCS information to text. */
+  status=wcshdo(WCSHDO_safe, wcs, &nkeyrec, &wcsstr);
+  if(status)
+    error(0, 0, "%s: WARNING: WCSLIB error, no WCS in output.\n"
+          "wcshdu ERROR %d: %s", __func__, status, wcs_errmsg[status]);
+  else
+    {
+      gal_fits_key_write_wcsstr(fptr, wcs, wcsstr, nkeyrec);
+      free(wcsstr);
+    }
+  status=0;
+
+   /* WCSLIB is going to write PC+CDELT keywords in any case. But when we
+      have a TPV distortion, it is cleaner to use a CD matrix. Also,
+      including and before version 7.3, WCSLIB wouldn't convert coordinates
+      properly if the PC matrix is used with the TPV distortion. So to help
+      users with WCSLIB 7.3 or earlier, we need to conver the PC matrix to
+      CD. 'gal_wcs_to_cd' function already made sure that CDELT=1, so
+      effectively the CD matrix and PC matrix are equivalent, we just need
+      to convert the keyword names and delete the CDELT keywords. Note that
+      zero-valued PC/CD elements may not be present, so we'll manually set
+      'status' to zero and not let CFITSIO crash.*/
+  if(wcs->altlin==2)
+    {
+      status=0; fits_modify_name(fptr, "PC1_1", "CD1_1", &status);
+      status=0; fits_modify_name(fptr, "PC1_2", "CD1_2", &status);
+      status=0; fits_modify_name(fptr, "PC2_1", "CD2_1", &status);
+      status=0; fits_modify_name(fptr, "PC2_2", "CD2_2", &status);
+      status=0; fits_delete_str(fptr, "CDELT1", &status);
+      status=0; fits_delete_str(fptr, "CDELT2", &status);
+      status=0;
+      fits_write_comment(fptr, "The CD matrix is used instead of the "
+                         "PC+CDELT due to conflicts with TPV distortion "
+                         "in WCSLIB 7.3 (released on 2020/06/03) and "
+                         "ealier. By default Gnuastro will write "
+                         "PC+CDELT matrices because the rotation (PC) and "
+                         "pixel-scale (CDELT) are separate; providing "
+                         "more physically relevant metadata for human "
+                         "readers (PC+CDELT is also the default format "
+                         "of WCSLIB).", &status);
+    }
+}
+
+
+
+
+
+void
+gal_wcs_write(struct wcsprm *wcs, char *filename,
+              char *extname, gal_fits_list_key_t *headers,
+              char *program_string)
+{
+  int status=0;
+  size_t ndim=0;
+  fitsfile *fptr;
+  long *naxes=NULL;
+
+  /* Small sanity checks */
+  if(wcs==NULL)
+    error(EXIT_FAILURE, 0, "%s: input WCS is NULL", __func__);
+  if( gal_fits_name_is_fits(filename)==0 )
+    error(EXIT_FAILURE, 0, "%s: not a FITS suffix", filename);
+
+  /* Open the file for writing */
+  fptr=gal_fits_open_to_write(filename);
+
+  /* Create the FITS file. */
+  fits_create_img(fptr, gal_fits_type_to_bitpix(GAL_TYPE_UINT8),
+                  ndim, naxes, &status);
+  gal_fits_io_error(status, NULL);
+
+  /* Remove the two comment lines put by CFITSIO. Note that in some cases,
+     it might not exist. When this happens, the status value will be
+     non-zero. We don't care about this error, so to be safe, we will just
+     reset the status variable after these calls. */
+  fits_delete_key(fptr, "COMMENT", &status);
+  fits_delete_key(fptr, "COMMENT", &status);
+  status=0;
+
+  /* If an extension name was requested, add it. */
+  if(extname)
+    fits_write_key(fptr, TSTRING, "EXTNAME", extname, "", &status);
+
+  /* Write the WCS structure. */
+  gal_wcs_write_in_fitsptr(fptr, wcs);
+
+  /* Write all the headers and the version information. */
+  gal_fits_key_write_version_in_ptr(&headers, program_string, fptr);
+
+  /* Close the FITS file. */
+  fits_close_file(fptr, &status);
+  gal_fits_io_error(status, NULL);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*************************************************************
+ ***********              Distortions              ***********
+ *************************************************************/
+int
+gal_wcs_distortion_from_string(char *distortion)
+{
+  if(      !strcmp(distortion,"TPD") ) return GAL_WCS_DISTORTION_TPD;
+  else if( !strcmp(distortion,"SIP") ) return GAL_WCS_DISTORTION_SIP;
+  else if( !strcmp(distortion,"TPV") ) return GAL_WCS_DISTORTION_TPV;
+  else if( !strcmp(distortion,"DSS") ) return GAL_WCS_DISTORTION_DSS;
+  else if( !strcmp(distortion,"WAT") ) return GAL_WCS_DISTORTION_WAT;
+  else
+    error(EXIT_FAILURE, 0, "WCS distortion name '%s' not recognized, "
+          "currently recognized names are 'TPD', 'SIP', 'TPV', 'DSS' and "
+          "'WAT'", distortion);
+
+  /* Control should not reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+        "problem. Control should not reach the end of this function",
+        __func__, PACKAGE_BUGREPORT);
+  return GAL_WCS_DISTORTION_INVALID;
+}
+
+
+
+
+
+char *
+gal_wcs_distortion_to_string(int distortion)
+{
+  /* Return the proper literal string. */
+  switch(distortion)
+    {
+    case GAL_WCS_DISTORTION_TPD: return "TPD";
+    case GAL_WCS_DISTORTION_SIP: return "SIP";
+    case GAL_WCS_DISTORTION_TPV: return "TPV";
+    case GAL_WCS_DISTORTION_DSS: return "DSS";
+    case GAL_WCS_DISTORTION_WAT: return "WAT";
+    default:
+      error(EXIT_FAILURE, 0, "WCS distortion id '%d' isn't recognized",
+            distortion);
+    }
+
+  /* Control should not reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+        "problem. Control should not reach the end of this function",
+        __func__, PACKAGE_BUGREPORT);
+  return NULL;
+}
+
+
+
+
+
+/* Check the type of distortion present and return the appropriate
+   integer based on `enum gal_wcs_distortion`.
+
+   Parameters:
+    struct wcsprm *wcs - The wcs parameters of the fits file.
+
+   Return:
+    int out_distortion - The type of distortion present. */
+int
+gal_wcs_distortion_identify(struct wcsprm *wcs)
+{
+#if GAL_CONFIG_HAVE_WCSLIB_DIS_H
+  struct disprm *dispre=NULL;
+  struct disprm *disseq=NULL;
+
+  /* Small sanity check. */
+  if(wcs==NULL) return GAL_WCS_DISTORTION_INVALID;
+
+  /* To help in reading. */
+  disseq=wcs->lin.disseq;
+  dispre=wcs->lin.dispre;
+
+  /* Check if distortion present. */
+  if( disseq==NULL && dispre==NULL ) return GAL_WCS_DISTORTION_INVALID;
+
+  /* Check the type of distortion.
+
+     As mentioned in the WCS paper IV section 2.4.2 available at
+     https://www.atnf.csiro.au/people/mcalabre/WCS/dcs_20040422.pdf, the
+     DPja and DQia keywords are used to record the parameters required by
+     the prior and sequent distortion functions respectively.
+
+     Now, as mentioned in dis.h file reference section in WCSLIB manual
+     given here
+     https://www.atnf.csiro.au/people/mcalabre/WCS/wcslib/dis_8h.html, TPV,
+     DSS, and WAT are sequent polynomial distortions, while SIP is prior
+     polynomial distortion. TPD is a superset of all these distortions and
+     hence can be used both as a prior and sequent distortion polynomial.
+
+     References and citations:
+     "Representations of distortions in FITS world coordinate systems",
+     Calabretta, M.R. et al. (WCS Paper IV, draft dated 2004/04/22)
+      */
+
+  if( dispre != NULL )
+    {
+      if(      !strcmp(*dispre->dtype, "SIP") ) return GAL_WCS_DISTORTION_SIP;
+      else if( !strcmp(*dispre->dtype, "TPD") ) return GAL_WCS_DISTORTION_TPD;
+      else
+        error(EXIT_FAILURE, 0, "%s: distortion '%s' isn't recognized in "
+              "the 'dispre' structure of the given 'wcsprm'", __func__,
+              *dispre->dtype);
+    }
+  else if( disseq != NULL )
+    {
+      if(      !strcmp(*disseq->dtype, "TPV") ) return GAL_WCS_DISTORTION_TPV;
+      else if( !strcmp(*disseq->dtype, "TPD") ) return GAL_WCS_DISTORTION_TPD;
+      else if( !strcmp(*disseq->dtype, "DSS") ) return GAL_WCS_DISTORTION_DSS;
+      else if( !strcmp(*disseq->dtype, "WAT") ) return GAL_WCS_DISTORTION_WAT;
+      else
+        error(EXIT_FAILURE, 0, "%s: distortion '%s' isn't recognized in "
+              "the 'disseq' structure of the given 'wcsprm'", __func__,
+              *dispre->dtype);
+    }
+
+  /* Control should not reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' to fix "
+        "the problem. Control should not reach the end of this function",
+        __func__, PACKAGE_BUGREPORT);
+#else
+  /* The 'wcslib/dis.h' isn't present. */
+  error(EXIT_FAILURE, 0, "%s: the installed version of WCSLIB on this "
+        "system doesn't have the 'dis.h' header! Thus Gnuastro can't do "
+        "distortion-related operations on the world coordinate system "
+        "(WCS). To use these features, please upgrade your version "
+        "of WCSLIB and re-build Gnuastro", __func__);
+#endif
+  return GAL_WCS_DISTORTION_INVALID;
+}
+
+
+
+
+
+
+
+
+
+
+/* Convert a given distrotion type to other.
+
+  Parameters:
+    struct wcsprm *wcs - The wcs parameters of the fits file.
+    int out_distortion - The desired output distortion.
+    size_t* fitsize    - The size of the array along each dimension.
+
+  Return:
+    struct wcsprm *outwcs - The transformed wcs parameters in the
+                            required distortion type. */
+struct wcsprm *
+gal_wcs_distortion_convert(struct wcsprm *inwcs, int outdisptype,
+                           size_t *fitsize)
+{
+#if GAL_CONFIG_HAVE_WCSLIB_DIS_H
+  struct wcsprm *outwcs=NULL;
+  int indisptype=gal_wcs_distortion_identify(inwcs);
+
+  /* Make sure we have a PC+CDELT structure in the input WCS. */
+  gal_wcs_decompose_pc_cdelt(inwcs);
+
+  /* If the input and output types are the same, just copy the input,
+     otherwise, do the conversion. */
+  if(indisptype==outdisptype) outwcs=gal_wcs_copy(inwcs);
+  else
+    switch(indisptype)
+      {
+      /* If there is no distortion in the input, just return a
+         newly-allocated copy. */
+      case GAL_WCS_DISTORTION_INVALID: outwcs=gal_wcs_copy(inwcs); break;
+
+      /* Input's distortion is SIP. */
+      case GAL_WCS_DISTORTION_SIP:
+        switch(outdisptype)
+          {
+          case GAL_WCS_DISTORTION_TPV:
+            outwcs=gal_wcsdistortion_sip_to_tpv(inwcs);
+            break;
+          default:
+            error(EXIT_FAILURE, 0, "%s: conversion from %s to %s is not yet "
+                  "supported. Please contact us at '%s'", __func__,
+                  gal_wcs_distortion_to_string(indisptype),
+                  gal_wcs_distortion_to_string(outdisptype),
+                  PACKAGE_BUGREPORT);
+              }
+        break;
+
+      /* Input's distortion is TPV. */
+      case GAL_WCS_DISTORTION_TPV:
+        switch(outdisptype)
+          {
+          case GAL_WCS_DISTORTION_SIP:
+            if(fitsize==NULL)
+              error(EXIT_FAILURE, 0, "%s: the size array is necessary "
+                    "for this conversion", __func__);
+            outwcs=gal_wcsdistortion_tpv_to_sip(inwcs, fitsize);
+            break;
+          default:
+            error(EXIT_FAILURE, 0, "%s: conversion from %s to %s is not yet "
+                  "supported. Please contact us at '%s'", __func__,
+                  gal_wcs_distortion_to_string(indisptype),
+                  gal_wcs_distortion_to_string(outdisptype),
+                  PACKAGE_BUGREPORT);
+          }
+        break;
+
+      /* Input's distortion is not yet supported.. */
+      case GAL_WCS_DISTORTION_TPD:
+      case GAL_WCS_DISTORTION_DSS:
+      case GAL_WCS_DISTORTION_WAT:
+        error(EXIT_FAILURE, 0, "%s: input %s distortion is not yet "
+              "supported. Please contact us at '%s'", __func__,
+              gal_wcs_distortion_to_string(indisptype),
+              PACKAGE_BUGREPORT);
+
+      /* A bug! This distortion is not yet recognized. */
+      default:
+        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
+              "the problem. The identifier '%d' is not recognized as a "
+              "distortion", __func__, PACKAGE_BUGREPORT, indisptype);
+      }
+
+  /* Return the converted WCS. */
+  return outwcs;
+#else
+  /* The 'wcslib/dis.h' isn't present. */
+  error(EXIT_FAILURE, 0, "%s: the installed version of WCSLIB on this "
+        "system doesn't have the 'dis.h' header! Thus Gnuastro can't do "
+        "distortion-related operations on the world coordinate system "
+        "(WCS). To use these features, please upgrade your version "
+        "of WCSLIB and re-build Gnuastro", __func__);
+  return NULL;
+#endif
+}
+
+
+
+
+
+
 
 
 
@@ -235,7 +896,7 @@ gal_wcs_copy(struct wcsprm *wcs)
       errno=0;
       out=malloc(sizeof *out);
       if(out==NULL)
-        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `out'",
+        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'out'",
               __func__, sizeof *out);
 
       /* Initialize the allocated WCS structure. The WCSLIB manual says "On
@@ -259,7 +920,7 @@ gal_wcs_copy(struct wcsprm *wcs)
 
 
 /* Remove the algorithm part of CTYPE (anything after, and including, a
-   `-') if necessary. */
+   '-') if necessary. */
 static void
 wcs_ctype_noalgorithm(char *str)
 {
@@ -323,7 +984,7 @@ gal_wcs_remove_dimension(struct wcsprm *wcs, size_t fitsdim)
   for(i=0;i<naxis;++i)
     {
       /* The dimensions are in FITS order, but counting starts from 0, so
-         we'll have to subtract 1 from `fitsdim'. */
+         we'll have to subtract 1 from 'fitsdim'. */
       if(i>fitsdim-1)
         {
           /* 1-D arrays. */
@@ -380,17 +1041,17 @@ gal_wcs_remove_dimension(struct wcsprm *wcs, size_t 
fitsdim)
   naxis = wcs->naxis -= 1;
 
 
-  /* The `TAN' algorithm needs two dimensions. So we need to remove it when
+  /* The 'TAN' algorithm needs two dimensions. So we need to remove it when
      it can cause confusion. */
   switch(naxis)
     {
-    /* The `TAN' algorithm cannot be used for any single-dimensional
+    /* The 'TAN' algorithm cannot be used for any single-dimensional
        dataset. So we'll have to remove it if it exists. */
     case 1:
       wcs_ctype_noalgorithm(wcs->ctype[0]);
       break;
 
-    /* For any other dimensionality, `TAN' should be kept only when exactly
+    /* For any other dimensionality, 'TAN' should be kept only when exactly
        two dimensions have it. */
     default:
 
@@ -480,7 +1141,7 @@ gal_wcs_on_tile(gal_data_t *tile)
    final matrix irrespective of the type of storage in the WCS
    structure. Recall that the FITS standard has several methods to store
    the matrix, which is up to this function to account for and return the
-   final matrix. The output is an allocated DxD matrix where `D' is the
+   final matrix. The output is an allocated DxD matrix where 'D' is the
    number of dimensions. */
 double *
 gal_wcs_warp_matrix(struct wcsprm *wcs)
@@ -492,7 +1153,7 @@ gal_wcs_warp_matrix(struct wcsprm *wcs)
   errno=0;
   out=malloc(size*sizeof *out);
   if(out==NULL)
-    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `out'",
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'out'",
           __func__, size*sizeof *out);
 
   /* Fill in the array. */
@@ -525,7 +1186,7 @@ gal_wcs_warp_matrix(struct wcsprm *wcs)
 
          Just note that the equations of the link above convert CROTAi to
          PC. But here we want the "final" matrix (after multiplication by
-         the `CDELT' values). So to speed things up, we won't bother
+         the 'CDELT' values). So to speed things up, we won't bother
          dividing and then multiplying by the same CDELT values in the
          off-diagonal elements. */
       crota2=wcs->crota[1];
@@ -545,63 +1206,158 @@ gal_wcs_warp_matrix(struct wcsprm *wcs)
 
 
 
+/* Clean up small/negligible errros that are clearly caused by measurement
+   errors in the PC and CDELT elements. */
+void
+gal_wcs_clean_errors(struct wcsprm *wcs)
+{
+  double crdcheck=NAN;
+  size_t i, crdnum=0, ndim=wcs->naxis;
+  double mean, crdsum=0, sum=0, min=FLT_MAX, max=0;
+  double *pc=wcs->pc, *cdelt=wcs->cdelt, *crder=wcs->crder;
+
+  /* First clean up CDELT: if the CRDER keyword is set, then we'll use that
+     as a reference, if not, we'll use the absolute floating point error
+     defined in 'GAL_WCS_FLTERR'. */
+  for(i=0; i<ndim; ++i)
+    {
+      sum+=cdelt[i];
+      if(cdelt[i]>max) max=cdelt[i];
+      if(cdelt[i]<min) min=cdelt[i];
+      if(crder[i]!=UNDEFINED) {++crdnum; crdsum=crder[i];}
+    }
+  mean=sum/ndim;
+  crdcheck = crdnum ? crdsum/crdnum : GAL_WCS_FLTERROR;
+  if( (max-min)/mean < crdcheck )
+    for(i=0; i<ndim; ++i)
+      cdelt[i]=mean;
+
+  /* Now clean up the PC elements. If the diagonal elements are too close
+     to 0, 1, or -1, set them to 0 or 1 or -1. */
+  if(pc)
+    for(i=0;i<ndim*ndim;++i)
+      {
+        if(      fabs(pc[i] -  0 ) < GAL_WCS_FLTERROR ) pc[i]=0;
+        else if( fabs(pc[i] -  1 ) < GAL_WCS_FLTERROR ) pc[i]=1;
+        else if( fabs(pc[i] - -1 ) < GAL_WCS_FLTERROR ) pc[i]=-1;
+      }
+}
+
+
+
+
 
-/* According to the FITS standard, in the `PCi_j' WCS formalism, the matrix
-   elements m_{ij} are encoded in the `PCi_j' keywords and the scale
-   factors are encoded in the `CDELTi' keywords. There is also another
-   formalism (the `CDi_j' formalism) which merges the two into one
+/* According to the FITS standard, in the 'PCi_j' WCS formalism, the matrix
+   elements m_{ij} are encoded in the 'PCi_j' keywords and the scale
+   factors are encoded in the 'CDELTi' keywords. There is also another
+   formalism (the 'CDi_j' formalism) which merges the two into one
    matrix.
 
-   However, WCSLIB's internal operations are apparently done in the `PCi_j'
+   However, WCSLIB's internal operations are apparently done in the 'PCi_j'
    formalism. So its outputs are also all in that format by default. When
-   the input is a `CDi_j', WCSLIB will still read the image into the
-   `PCi_j' formalism and the `CDELTi's are set to 1. This function will
-   decompose the two matrices to give a reasonable `CDELTi' and `PCi_j' in
+   the input is a 'CDi_j', WCSLIB will still read the image into the
+   'PCi_j' formalism and the 'CDELTi's are set to 1. This function will
+   decompose the two matrices to give a reasonable 'CDELTi' and 'PCi_j' in
    such cases. */
 void
 gal_wcs_decompose_pc_cdelt(struct wcsprm *wcs)
 {
-  double *ps;
   size_t i, j;
+  double *ps, *warp;
+
+  /* If there is on WCS, then don't do anything. */
+  if(wcs==NULL) return;
+
+  /* Get the pixel scale and full warp matrix. */
+  warp=gal_wcs_warp_matrix(wcs);
+  ps=gal_wcs_pixel_scale(wcs);
+  if(ps==NULL) return;
+
+  /* For a check.
+  printf("pc: %g, %g\n", pc[0], pc[1]);
+  printf("warp: %g, %g, %g, %g\n", warp[0], warp[1],
+         warp[2], warp[3]);
+  */
+
+  /* Set the CDELTs. */
+  for(i=0; i<wcs->naxis; ++i)
+    wcs->cdelt[i] = ps[i];
+
+  /* Write the PC matrix. */
+  for(i=0;i<wcs->naxis;++i)
+    for(j=0;j<wcs->naxis;++j)
+      wcs->pc[i*wcs->naxis+j] = warp[i*wcs->naxis+j]/ps[i];
+
+  /* According to the 'wcslib/wcs.h' header: "In particular, wcsset()
+     resets wcsprm::cdelt to unity if CDi_ja is present (and no
+     PCi_ja).". So apparently, when the input is a 'CDi_j', it might expect
+     the 'CDELTi' elements to be 1.0. But we have changed that here, so we
+     will correct the 'altlin' element of the WCS structure to make sure
+     that WCSLIB only looks into the 'PCi_j' and 'CDELTi' and makes no
+     assumptioins about 'CDELTi'. */
+  wcs->altlin=1;
+
+  /* Clean up. */
+  free(ps);
+  free(warp);
+}
+
+
+
+
+
+/* Set the WCS structure to use the CD matrix. */
+static void
+gal_wcs_to_cd(struct wcsprm *wcs)
+{
+  size_t i, j, naxis;
 
   /* If there is on WCS, then don't do anything. */
   if(wcs==NULL) return;
 
-  /* The correction is only needed when the PC matrix is filled. */
-  if(wcs->pc)
+  /* 'wcs->altlin' identifies which rotation element is being used (PCi_j,
+     CDi_J or CROTAi). For PCi_j, the first bit will be 1 (==1), for CDi_j,
+     the second bit is 1 (==2) and for CROTAi, the third bit is 1 (==4). */
+  naxis=wcs->naxis;
+  switch(wcs->altlin)
     {
-      /* Get the pixel scale. */
-      ps=gal_wcs_pixel_scale(wcs);
-      if(ps==NULL) return;
-
-      /* The PC matrix and the CDELT elements might both contain scale
-         elements (during processing the scalings might be added only to PC
-         elements for example). So to be safe, we first multiply them into
-         one matrix. */
-      for(i=0;i<wcs->naxis;++i)
-        for(j=0;j<wcs->naxis;++j)
-          wcs->pc[i*wcs->naxis+j] *= wcs->cdelt[i];
+   /* PCi_j: Convert it to CDi_j. */
+    case 1:
 
-      /* Set the CDELTs. */
-      for(i=0; i<wcs->naxis; ++i)
-        wcs->cdelt[i] = ps[i];
+      /* Fill in the CD matrix and correct the PC and CDELT arrays. We have
+         to do this because ultimately, WCSLIB will be writing the PC and
+         CDELT keywords, even when 'altlin' is 2. So effectively we have to
+         multiply the PC and CDELT matrices, then set cdelt=1 in all
+         dimensions. This is actually how WCSLIB reads a FITS header with
+         only a CD matrix. */
+      for(i=0;i<naxis;++i)
+        {
+          for(j=0;j<naxis;++j)
+            wcs->cd[i*naxis+j] = wcs->pc[i*naxis+j] *= wcs->cdelt[i];
+          wcs->cdelt[i]=1;
+        }
 
-      /* Correct the PCi_js */
-      for(i=0;i<wcs->naxis;++i)
-        for(j=0;j<wcs->naxis;++j)
-          wcs->pc[i*wcs->naxis+j] /= ps[i];
-
-      /* Clean up. */
-      free(ps);
-
-      /* According to the `wcslib/wcs.h' header: "In particular, wcsset()
-         resets wcsprm::cdelt to unity if CDi_ja is present (and no
-         PCi_ja).". So apparently, when the input is a `CDi_j', it might
-         expect the `CDELTi' elements to be 1.0. But we have changed that
-         here, so we will correct the `altlin' element of the WCS structure
-         to make sure that WCSLIB only looks into the `PCi_j' and `CDELTi'
-         and makes no assumptioins about `CDELTi'. */
-      wcs->altlin=1;
+      /* Set the altlin to be the CD matrix and free the PC matrix. */
+      wcs->altlin=2;
+      break;
+
+    /* CDi_j: No need to do any conversion. */
+    case 2: return; break;
+
+    /* CROTAi: not yet supported. */
+    case 4:
+      error(0, 0, "%s: WARNING: Conversion of 'CROTAi' keywords to the CD "
+            "matrix is not yet supported (for lack of time!), please "
+            "contact us at %s to add this feature. But this may not cause a "
+            "problem at all, so please check if the output's WCS is "
+            "reasonable", __func__, PACKAGE_BUGREPORT);
+      break;
+
+    /* The value isn't supported! */
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix the "
+            "problem. The value %d for wcs->altlin isn't recognized",
+            __func__, PACKAGE_BUGREPORT, wcs->altlin);
     }
 }
 
@@ -655,17 +1411,15 @@ gal_wcs_pixel_scale(struct wcsprm *wcs)
   /* Only continue if a WCS exists. */
   if(wcs==NULL) return NULL;
 
+
   /* Write the full WCS rotation matrix into an array, irrespective of what
-     style it was stored in the wcsprm structure (`PCi_j' style or `CDi_j'
+     style it was stored in the wcsprm structure ('PCi_j' style or 'CDi_j'
      style). */
   a=gal_wcs_warp_matrix(wcs);
 
-  /* A small sanity check (this won't work on a singular matrix, can happen
-     in FITS WCSs!). In this case, we should return NULL.*/
-  n=wcs->naxis;
-  for(i=0;i<n;++i) {if(a[i*n+i]==0.0f) return NULL;}
 
   /* Now that everything is good, we can allocate the necessary memory. */
+  n=wcs->naxis;
   v=gal_pointer_allocate(GAL_TYPE_FLOAT64, n*n, 0, __func__, "v");
   permutation=gal_pointer_allocate(GAL_TYPE_SIZE_T, n, 0, __func__,
                                    "permutation");
@@ -700,26 +1454,29 @@ gal_wcs_pixel_scale(struct wcsprm *wcs)
           }
 
       /* Do the check, print warning and make correction. */
-      if(maxrow!=minrow && maxrow/minrow>1e4 && warning_printed==0)
+      if(maxrow!=minrow
+         && maxrow/minrow>1e5    /* The difference between elements is large */
+         && maxrow/minrow<GAL_WCS_FLTERROR
+         && warning_printed==0)
         {
           fprintf(stderr, "\nWARNING: The input WCS matrix (possibly taken "
-                  "from the FITS header keywords starting with `CD' or `PC') "
+                  "from the FITS header keywords starting with 'CD' or 'PC') "
                   "contains values with very different scales (more than "
-                  "10^4 different). This is probably due to floating point "
+                  "10^5 different). This is probably due to floating point "
                   "errors. These values might bias the pixel scale (and "
                   "subsequent) calculations.\n\n"
                   "You can see the respective matrix with one of the "
                   "following two commands (depending on how the FITS file "
                   "was written). Recall that if the desired extension/HDU "
-                  "isn't the default, you can choose it with the `--hdu' "
-                  "(or `-h') option before the `|' sign in these commands."
+                  "isn't the default, you can choose it with the '--hdu' "
+                  "(or '-h') option before the '|' sign in these commands."
                   "\n\n"
                   "    $ astfits file.fits -p | grep 'PC._.'\n"
                   "    $ astfits file.fits -p | grep 'CD._.'\n\n"
                   "You can delete the ones with obvious floating point "
                   "error values using the following command (assuming you "
-                  "want to delete `CD1_2' and `CD2_1'). Afterwards, you can "
-                  "rerun your original command to remove this warning "
+                  "want to delete 'CD1_2' and 'CD2_1'). Afterwards, you can "
+                  "re-run your original command to remove this warning "
                   "message and possibly correct errors that it might have "
                   "caused.\n\n"
                   "    $ astfits file.fits --delete=CD1_2 --delete=CD2_1\n\n"
@@ -744,10 +1501,10 @@ gal_wcs_pixel_scale(struct wcsprm *wcs)
   /* The raw pixel scale array produced from the singular value
      decomposition above is ordered based on values, not the input. So when
      the pixel scales in all the dimensions aren't the same (the units of
-     the dimensions differ), the order of the values in `pixelscale' will
+     the dimensions differ), the order of the values in 'pixelscale' will
      not necessarily correspond to the input's dimensions.
 
-     To correct the order, we can use the `V' matrix to find the original
+     To correct the order, we can use the 'V' matrix to find the original
      position of the pixel scale values and then use permutation to
      re-order it correspondingly. The column with the largest (absolute)
      value will be taken as the one to be used for each row. */
@@ -800,7 +1557,7 @@ gal_wcs_pixel_area_arcsec2(struct wcsprm *wcs)
   if(wcs->naxis!=2) return NAN;
 
   /* Check if the units of the axis are degrees or not. Currently all FITS
-     images I have worked with use `deg' for degrees. If other alternatives
+     images I have worked with use 'deg' for degrees. If other alternatives
      exist, we can add corrections later. */
   if( strcmp("deg", wcs->cunit[0]) || strcmp("deg", wcs->cunit[1]) )
     return NAN;
@@ -819,6 +1576,159 @@ gal_wcs_pixel_area_arcsec2(struct wcsprm *wcs)
 
 
 
+int
+gal_wcs_coverage(char *filename, char *hdu, size_t *ondim,
+                 double **ocenter, double **owidth, double **omin,
+                 double **omax)
+{
+  fitsfile *fptr;
+  struct wcsprm *wcs;
+  int nwcs=0, type, status=0;
+  char *name=NULL, *unit=NULL;
+  gal_data_t *tmp, *coords=NULL;
+  size_t i, ndim, *dsize=NULL, numrows;
+  double *x=NULL, *y=NULL, *z=NULL, *min, *max, *center, *width;
+
+  /* Read the desired WCS. */
+  wcs=gal_wcs_read(filename, hdu, 0, 0, &nwcs);
+
+  /* If a WCS doesn't exist, return NULL. */
+  if(wcs==NULL) return 0;
+
+  /* Make sure the input HDU is an image. */
+  if( gal_fits_hdu_format(filename, hdu) != IMAGE_HDU )
+    error(EXIT_FAILURE, 0, "%s (hdu %s): is not an image HDU, the "
+          "'--skycoverage' option only applies to image extensions",
+          filename, hdu);
+
+  /* Get the array information of the image. */
+  fptr=gal_fits_hdu_open(filename, hdu, READONLY);
+  gal_fits_img_info(fptr, &type, ondim, &dsize, &name, &unit);
+  fits_close_file(fptr, &status);
+  ndim=*ondim;
+
+  /* Abort if we have more than 3 dimensions. */
+  if(ndim==1 || ndim>3) return 0;
+
+  /* Allocate the output datasets. */
+  center=*ocenter=gal_pointer_allocate(GAL_TYPE_FLOAT64, ndim, 0, __func__,
+                                       "ocenter");
+  width=*owidth=gal_pointer_allocate(GAL_TYPE_FLOAT64, ndim, 0, __func__,
+                                     "owidth");
+  min=*omin=gal_pointer_allocate(GAL_TYPE_FLOAT64, ndim, 0, __func__,
+                                 "omin");
+  max=*omax=gal_pointer_allocate(GAL_TYPE_FLOAT64, ndim, 0, __func__,
+                                 "omax");
+
+  /* Now that we have the number of dimensions in the image, allocate the
+     space needed for the coordinates. */
+  numrows = (ndim==2) ? 5 : 9;
+  for(i=0;i<ndim;++i)
+    {
+      tmp=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &numrows, NULL, 0,
+                         -1, 1, NULL, NULL, NULL);
+      tmp->next=coords;
+      coords=tmp;
+    }
+
+  /* Fill in the coordinate arrays, Note that 'dsize' is ordered in C
+     dimensions, for the WCS conversion, we need to have the dimensions
+     ordered in FITS/Fortran order. */
+  switch(ndim)
+    {
+    case 2:
+      x=coords->array;  y=coords->next->array;
+      x[0] = 1;         y[0] = 1;
+      x[1] = dsize[1];  y[1] = 1;
+      x[2] = 1;         y[2] = dsize[0];
+      x[3] = dsize[1];  y[3] = dsize[0];
+      x[4] = dsize[1]/2 + (dsize[1]%2 ? 1 : 0.5f);
+      y[4] = dsize[0]/2 + (dsize[0]%2 ? 1 : 0.5f);
+      break;
+    case 3:
+      x=coords->array; y=coords->next->array; z=coords->next->next->array;
+      x[0] = 1;        y[0] = 1;              z[0]=1;
+      x[1] = dsize[2]; y[1] = 1;              z[1]=1;
+      x[2] = 1;        y[2] = dsize[1];       z[2]=1;
+      x[3] = dsize[2]; y[3] = dsize[1];       z[3]=1;
+      x[4] = 1;        y[4] = 1;              z[4]=dsize[0];
+      x[5] = dsize[2]; y[5] = 1;              z[5]=dsize[0];
+      x[6] = 1;        y[6] = dsize[1];       z[6]=dsize[0];
+      x[7] = dsize[2]; y[7] = dsize[1];       z[7]=dsize[0];
+      x[8] = dsize[2]/2 + (dsize[2]%2 ? 1 : 0.5f);
+      y[8] = dsize[1]/2 + (dsize[1]%2 ? 1 : 0.5f);
+      z[8] = dsize[0]/2 + (dsize[0]%2 ? 1 : 0.5f);
+      break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+            "fix the problem. 'ndim' of %zu is not recognized.",
+            __func__, PACKAGE_BUGREPORT, ndim);
+    }
+
+  /* For a check:
+  printf("IMAGE COORDINATES:\n");
+  for(i=0;i<numrows;++i)
+    if(ndim==2)
+      printf("%-15g%-15g\n", x[i], y[i]);
+    else
+      printf("%-15g%-15g%-15g\n", x[i], y[i], z[i]);
+  */
+
+  /* Convert to the world coordinate system. */
+  gal_wcs_img_to_world(coords, wcs, 1);
+
+  /* For a check:
+  printf("\nWORLD COORDINATES:\n");
+  for(i=0;i<numrows;++i)
+    if(ndim==2)
+      printf("%-15g%-15g\n", x[i], y[i]);
+    else
+      printf("%-15g%-15g%-15g\n", x[i], y[i], z[i]);
+  */
+
+  /* Get the minimum and maximum values in each dimension. */
+  tmp=gal_statistics_minimum(coords);
+  min[0] = ((double *)(tmp->array))[0];      gal_data_free(tmp);
+  tmp=gal_statistics_maximum(coords);
+  max[0] = ((double *)(tmp->array))[0];      gal_data_free(tmp);
+  tmp=gal_statistics_minimum(coords->next);
+  min[1] = ((double *)(tmp->array))[0];      gal_data_free(tmp);
+  tmp=gal_statistics_maximum(coords->next);
+  max[1] = ((double *)(tmp->array))[0];      gal_data_free(tmp);
+  if(ndim>2)
+    {
+      tmp=gal_statistics_minimum(coords->next->next);
+      min[2] = ((double *)(tmp->array))[0];      gal_data_free(tmp);
+      tmp=gal_statistics_maximum(coords->next->next);
+      max[2] = ((double *)(tmp->array))[0];      gal_data_free(tmp);
+    }
+
+  /* Write the center and width. */
+  switch(ndim)
+    {
+    case 2:
+      center[0]=x[4];         center[1]=y[4];
+      width[0]=max[0]-min[0]; width[1]=max[1]-min[1];
+      break;
+    case 3:
+      center[0]=x[8];         center[1]=y[8];         center[2]=z[8];
+      width[0]=max[0]-min[0]; width[1]=max[1]-min[1]; width[2]=max[2]-min[2];
+      break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to solve the "
+            "problem. The value %zu is not a recognized dimension", __func__,
+            PACKAGE_BUGREPORT, ndim);
+    }
+
+  /* Clean up and return success. */
+  free(dsize);
+  wcsfree(wcs);
+  return 1;
+}
+
+
+
+
 
 
 
@@ -858,7 +1768,7 @@ wcs_convert_sanity_check_alloc(gal_data_t *coords, struct 
wcsprm *wcs,
 
       /* Check the type of the input. */
       if(tmp->type!=GAL_TYPE_FLOAT64)
-        error(EXIT_FAILURE, 0, "%s: input coordinates must have `float64' "
+        error(EXIT_FAILURE, 0, "%s: input coordinates must have 'float64' "
               "type", func);
 
       /* Make sure it has a single dimension. */
@@ -904,7 +1814,7 @@ wcs_convert_sanity_check_alloc(gal_data_t *coords, struct 
wcsprm *wcs,
 
 
 /* In Gnuastro, each column (coordinate for WCS conversion) is treated as a
-   separate array in a `gal_data_t' that are linked through a linked
+   separate array in a 'gal_data_t' that are linked through a linked
    list. But in WCSLIB, the input is a single array (with multiple
    columns). This function will convert between the two. */
 static void
@@ -958,8 +1868,8 @@ wcs_convert_prepare_out(gal_data_t *coords, struct wcsprm 
*wcs, int inplace)
 
 /* Convert world coordinates to image coordinates given the input WCS
    structure. The input must be a linked list of data structures of float64
-   (`double') type. The top element of the linked list must be the first
-   coordinate and etc. If `inplace' is non-zero, then the output will be
+   ('double') type. The top element of the linked list must be the first
+   coordinate and etc. If 'inplace' is non-zero, then the output will be
    written into the input's allocated space. */
 gal_data_t *
 gal_wcs_world_to_img(gal_data_t *coords, struct wcsprm *wcs, int inplace)
@@ -973,12 +1883,13 @@ gal_wcs_world_to_img(gal_data_t *coords, struct wcsprm 
*wcs, int inplace)
                                  &world, &pixcrd, &imgcrd);
   nelem=wcs->naxis; /* We have to make sure a WCS is given first. */
 
+
   /* Write the values from the input list of separate columns into a single
      array (WCSLIB input). */
   wcs_convert_list_to_array(coords, world, stat, wcs->naxis, 1);
 
 
-  /* Use WCSLIB's wcsp2s for the conversion. */
+  /* Use WCSLIB's wcss2p for the conversion. */
   status=wcss2p(wcs, ncoord, nelem, world, phi, theta, imgcrd, pixcrd, stat);
   if(status)
     error(EXIT_FAILURE, 0, "%s: wcss2p ERROR %d: %s", __func__, status,
@@ -1021,7 +1932,7 @@ gal_wcs_world_to_img(gal_data_t *coords, struct wcsprm 
*wcs, int inplace)
 
 
 
-/* Similar to `gal_wcs_world_to_img'. */
+/* Similar to 'gal_wcs_world_to_img'. */
 gal_data_t *
 gal_wcs_img_to_world(gal_data_t *coords, struct wcsprm *wcs, int inplace)
 {
@@ -1047,14 +1958,24 @@ gal_wcs_img_to_world(gal_data_t *coords, struct wcsprm 
*wcs, int inplace)
           wcs_errmsg[status]);
 
 
-  /* For a sanity check.
+  /* For a check.
   {
     size_t i;
-    printf("\n\n%s sanity check:\n", __func__);
+    printf("\n\n%s sanity check (%d dimensions):\n", __func__, nelem);
     for(i=0;i<coords->size;++i)
-      printf("(%g, %g, %g) --> (%g, %g, %g), [stat: %d]\n",
-             pixcrd[i*3], pixcrd[i*3+1], pixcrd[i*3+2],
-             world[i*3],  world[i*3+1],  world[i*3+2], stat[i]);
+      switch(nelem)
+        {
+        case 2:
+          printf("(%-10g %-10g) --> (%-10g %-10g), [stat: %d]\n",
+                 pixcrd[i*2], pixcrd[i*2+1],
+                 world[i*2],  world[i*2+1], stat[i]);
+          break;
+        case 3:
+          printf("(%g, %g, %g) --> (%g, %g, %g), [stat: %d]\n",
+                 pixcrd[i*3], pixcrd[i*3+1], pixcrd[i*3+2],
+                 world[i*3],  world[i*3+1],  world[i*3+2], stat[i]);
+          break;
+        }
   }
   */
 
diff --git a/lib/wcsdistortion.c b/lib/wcsdistortion.c
new file mode 100644
index 0000000..f43454f
--- /dev/null
+++ b/lib/wcsdistortion.c
@@ -0,0 +1,1978 @@
+/*********************************************************************
+wcsdistortion -- Manipulation of distortions in WCS.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Sachin Kumar Singh <sachinkumarsingh092@gmail.com>
+Contributing author(s):
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Copyright (C) 2020-2021, 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 <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <wcslib/wcslib.h>
+
+#include <gsl/gsl_linalg.h>
+#include <gsl/gsl_vector.h>
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_multifit.h>
+
+#include <gnuastro/wcs.h>
+#include <gnuastro/fits.h>
+
+#include <gnuastro-internal/wcsdistortion.h>
+
+
+
+
+
+/* Internally used macro(s) to help in the processing */
+#define wcsdistortion_max(a,b) \
+   ({ __typeof__ (a) _a = (a);  \
+       __typeof__ (b) _b = (b); \
+     _a > _b ? _a : _b; })
+
+/* Declarations to avoid ordering problems. */
+static void
+wcsdistortion_calc_tpveq(struct wcsprm *wcs, double cd[2][2],
+                         double tpvu[8][8], double tpvv[8][8]);
+static double
+wcsdistortion_calcsip(size_t axis, size_t m, size_t n, double tpvu[8][8],
+                      double tpvv[8][8]);
+
+
+
+
+
+
+
+/**************************************************************/
+/**********          Reading utilities             ************/
+/**************************************************************/
+/* Extract the required pv parameters from wcs. */
+static void
+wcsdistortion_get_tpvparams(struct wcsprm *wcs, double cd[2][2],
+                            double *pv1, double *pv2)
+{
+  const char *cp;
+  double *temp_cd=NULL;
+  struct dpkey  *keyp=NULL;
+  size_t i, j, k=0, index=0;
+  struct disprm *disseq=NULL;
+
+  /* Make sure a WCS structure is actually given. */
+  if(wcs==NULL)
+    error(EXIT_FAILURE, 0, "%s: input WCS structure is NULL", __func__);
+
+  /* For easy reading. */
+  disseq=wcs->lin.disseq;
+  keyp=disseq->dp;
+
+  /* Fill the 2-times allocated CD array (cd[][]). Note that the required
+    CD matix is extracted using the `gal_wcs_wrap_matrix` as a single
+    allocated array (temp_cd[]), that is then used to fill cd[][]. */
+  temp_cd=gal_wcs_warp_matrix(wcs);
+  for(i=0; i<2; ++i)
+    for(j=0; j<2; ++j)
+      {
+        /* If a value is present store it, else store 0.*/
+        if(temp_cd[k] != 0)   cd[i][j]=temp_cd[k++];
+        else                  cd[i][j]=0;
+
+        /* For a check:
+        printf("CD%ld_%ld\t%.10E\n", i+1, j+1, cd[i][j]);
+        */
+      }
+
+  for(i=0; i<disseq->ndp; ++i, ++keyp)
+    {
+      /* For axis1. */
+      if (keyp->j == 1)
+        {
+          if ( keyp->field[0] == '\0' ) continue;
+          cp = strchr(keyp->field, '.') + 1;
+          if (strncmp(cp, "TPV.", 4) != 0) continue;
+          cp += 4;
+
+          sscanf(cp, "%zu", &index);
+          pv1[index]=disseq->dp[i].value.f;
+
+          /* For a check
+          printf("PV1_%ld\t%.10f\n", index, pv1[index]);
+          */
+        }
+
+      /* For axis2. */
+      else if (keyp->j == 2)
+        {
+          if ( keyp->field[0] == '\0' ) continue;
+          cp = strchr(keyp->field, '.') + 1;
+          if (strncmp(cp, "TPV.", 4) != 0) continue;
+          cp += 4;
+
+          sscanf(cp, "%zu", &index);
+
+          pv2[index]=disseq->dp[i].value.f;
+
+          /* For a check
+          printf("PV2_%ld\t%.10f\n", index, pv2[index]);
+          */
+        }
+      else
+        {
+          error(EXIT_FAILURE, 0, "%s: No such axis present!", __func__);
+          break;
+        }
+    }
+
+  /* To check a full array:
+     for(i = 0; i < 40; ++i)
+     printf("PV2_%ld\t%.10f\n", i, pv2[i]);
+  */
+
+}
+
+
+
+
+
+
+/* Extract the required sip parameters from wcs. */
+static void
+wcsdistortion_get_sipparams(struct wcsprm *wcs, double cd[2][2],
+                            double a_coeff[5][5], double b_coeff[5][5])
+{
+  const char *cp;
+  double *temp_cd=NULL;
+  struct dpkey  *keyp=NULL;
+  struct disprm *dispre=NULL;
+  size_t i=0, j=0, m=0, n=0, k=0;
+
+  /* Make sure a WCS structure is actually given. Note that 'wcs->lin' is
+     not a pointer, but the full structure. So it is always present. */
+  if(wcs==NULL)
+    error(EXIT_FAILURE, 0, "%s: input WCS structure is NULL", __func__);
+  if(wcs->lin.dispre == NULL)
+    error(EXIT_FAILURE, 0, "%s: input WCS structure's 'lin.dispre' is NULL",
+          __func__);
+
+  /* For easy reading. */
+  dispre=wcs->lin.dispre;
+  keyp=dispre->dp;
+
+  /* Fill the 2-times allocated CD array (cd[][]). Note that the required
+     CD matix is extracted using the `gal_wcs_wrap_matrix` as a single
+     allocated array (temp_cd[]), that is then used to fill cd[][]. */
+  temp_cd=gal_wcs_warp_matrix(wcs);
+  for(i=0,k=0; i<2; ++i)
+    for(j=0; j<2; ++j)
+      {
+        /* If a value is present store it, else store 0.*/
+        if(temp_cd[k] != 0)   cd[i][j]=temp_cd[k++];
+        else                  cd[i][j]=0;
+
+        /* For a check:
+        printf("CD%ld_%ld\t%.10E\n", i+1, j+1, cd[i][j]);
+        */
+      }
+
+  /* Extract the SIP values from the strings and numbers inside the
+     wcsprm. */
+  for(i=0; i<dispre->ndp; ++i, ++keyp)
+    {
+      /* For axis1. */
+      if (keyp->j == 1)
+        {
+          if ( keyp->field[0] == '\0' ) continue;
+          cp = strchr(keyp->field, '.') + 1;
+          if (strncmp(cp, "SIP.FWD.", 8) != 0) continue;
+          cp += 8;
+
+          sscanf(cp, "%zu_%zu", &m, &n);
+          a_coeff[m][n]=dispre->dp[i].value.f;
+
+          /*For a check.
+          printf("A_%ld_%ld =\t %.10E\n", m, n, dispre->dp[i].value.f);
+          */
+        }
+
+      /* For axis2. */
+      else if (keyp->j == 2)
+        {
+          if ( keyp->field[0] == '\0' ) continue;
+          cp = strchr(keyp->field, '.') + 1;
+          if (strncmp(cp, "SIP.FWD.", 8) != 0) continue;
+          cp += 8;
+
+          sscanf(cp, "%zu_%zu", &m, &n);
+
+          b_coeff[m][n]=dispre->dp[i].value.f;
+
+          /*For a check.
+          printf("B_%ld_%ld =\t %.10E\n", m, n, dispre->dp[i].value.f);
+          */
+        }
+      else
+        error(EXIT_FAILURE, 0, "%s: No such axis present!", __func__);
+    }
+}
+
+
+
+
+
+
+/* Extract sip coefficients from the polynomial equation. */
+static void
+wcsdistortion_get_sipcoeff(struct wcsprm *wcs, size_t *a_order,
+                           size_t *b_order, double a_coeff[5][5],
+                           double b_coeff[5][5])
+{
+  size_t m, n;
+  double val=0;
+  size_t i, j, k;
+  double cd[2][2];
+  double tpvu[8][8], tpvv[8][8];
+
+  /* Initialise the 2d matrices. */
+  for(i=0; i<2; ++i) for(j=0; j<2; ++j) cd[i][j]=0;
+  for(i=0; i<8; ++i) for(j=0; j<8; ++j) { tpvu[i][j]=0; tpvv[i][j]=0; }
+
+  /* Calculate the TPV elements. */
+  wcsdistortion_calc_tpveq(wcs, cd, tpvu, tpvv);
+
+  for(i=0,k=0; i<2; ++i)
+    for(j=0; j<2; ++j)
+      {
+        /* If a value is present store it, else store 0.*/
+        if((wcs->cd)[i] != 0)   cd[i][j]=(wcs->cd)[k++];
+        else                    cd[i][j]=0;
+
+        /* For a check:
+        printf("CD%ld_%ld\t%.10lf\n", i+1, j+1, cd[i][j]);
+        */
+      }
+
+  for(m=0; m<=4; ++m)
+    for(n=0; n<=4; ++n)
+      {
+        /*For axis = 1*/
+        val=wcsdistortion_calcsip(1, m, n, tpvu, tpvv);
+        if(val != 0)
+          {
+            a_coeff[m][n]=val;
+            *a_order=wcsdistortion_max(*a_order, wcsdistortion_max(m,n));
+          }
+        else
+          a_coeff[m][n]=0;
+
+        /*For axis = 2*/
+        val=wcsdistortion_calcsip(2, m, n, tpvu, tpvv);
+        if(val != 0)
+          {
+            b_coeff[m][n]=val;
+            *b_order=wcsdistortion_max(*b_order, wcsdistortion_max(m,n));
+          }
+        else
+          b_coeff[m][n]=0;
+      }
+
+  /*For a check.
+  for(m=0;m<=4;++m)
+  for(n=0;n<=4;++n)
+  printf("A_%ld_%ld = %.10E \t B_%ld_%ld = %.10E\n",
+         m, n, a_coeff[m][n], m, n, b_coeff[m][n]);
+  */
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/**********       Intermidiate Equations           ************/
+/**************************************************************/
+/* Intermidiate equations formed during pv->sip conversions. */
+static void
+wcsdistortion_intermidate_tpveq(double cd[2][2], double *pv1, double *pv2,
+                                double k[5][5], double l[5][5])
+{
+  /*Intermidate polynomials, k[i][j] and l[i][j].
+    See Appendix A of the SPIE proceedings paper at
+    http://web.ipac.caltech.edu/staff/shupe/reprints/SIP_to_PV_SPIE2012.pdf
+
+    The work described in that paper is extended to 7th order.
+
+     References and citations:
+     "More flexibility in representing geometric distortion in
+     astronomical images,"
+     Shupe, David L.; Laher, Russ R.; Storrie-Lombardi, Lisa; Surace, Jason;
+     Grillmair, Carl; Levitan, David; Sesar, Branimir, 2012, in Software and
+     Cyberinfrastructure for Astronomy II.
+     Proceedings of the SPIE, Volume 8451, article id. 84511M.
+     */
+
+  k[0][0] = pv1[0];
+  l[0][0] = pv2[0];
+
+  k[0][1] = ( cd[0][1]   * pv1[1]
+             + cd[1][1] * pv1[2] );
+  l[0][1] = ( cd[0][1]   * pv2[2]
+             + cd[1][1] * pv2[1] );
+
+
+  k[1][0]= ( cd[0][0]   * pv1[1]
+             + cd[1][0] * pv1[2] );
+  l[1][0]= ( cd[0][0]   * pv2[2]
+             + cd[1][0] * pv2[1] );
+
+
+  k[0][2] = ( cd[0][1]   * cd[0][1] * pv1[4]
+              + cd[0][1] * cd[1][1] * pv1[5]
+              + cd[1][1] * cd[1][1] * pv1[6] );
+  l[0][2] = ( cd[0][1]   * cd[0][1] * pv2[6]
+              + cd[0][1] * cd[1][1] * pv2[5]
+              + cd[1][1] * cd[1][1] * pv2[4] );
+
+
+  k[1][1] = ( 2*cd[0][0]   * cd[0][1] * pv1[4]
+              + cd[0][0]   * cd[1][1] * pv1[5]
+              + cd[0][1]   * cd[1][0] * pv1[5]
+              + 2*cd[1][0] * cd[1][1] * pv1[6] );
+  l[1][1] = ( 2*cd[0][0]   * cd[0][1] * pv2[6]
+              + cd[0][0]   * cd[1][1] * pv2[5]
+              + cd[0][1]   * cd[1][0] * pv2[5]
+              + 2*cd[1][0] * cd[1][1] * pv2[4] );
+
+
+  k[2][0] = ( cd[0][0]   * cd[0][0] * pv1[4]
+              + cd[0][0] * cd[1][0] * pv1[5]
+              + cd[1][0] * cd[1][0] * pv1[6] );
+  l[2][0] = ( cd[0][0]   * cd[0][0] * pv2[6]
+              + cd[0][0] * cd[1][0] * pv2[5]
+              + cd[1][0] * cd[1][0] * pv2[4] );
+
+
+  k[0][3] = ( cd[0][1]   * cd[0][1] * cd[0][1] * pv1[7]
+              + cd[0][1] * cd[0][1] * cd[1][1] * pv1[8]
+              + cd[0][1] * cd[1][1] * cd[1][1] * pv1[9]
+              + cd[1][1] * cd[1][1] * cd[1][1] * pv1[10] );
+  l[0][3] = ( cd[0][1]   * cd[0][1] * cd[0][1] * pv2[10]
+              + cd[0][1] * cd[0][1] * cd[1][1] * pv2[9]
+              + cd[0][1] * cd[1][1] * cd[1][1] * pv2[8]
+              + cd[1][1] * cd[1][1] * cd[1][1] * pv2[7] );
+
+
+  k[1][2] = ( 3*cd[0][0]   * cd[0][1] * cd[0][1] * pv1[7]
+              + 2*cd[0][0] * cd[0][1] * cd[1][1] * pv1[8]
+              +   cd[0][0] * cd[1][1] * cd[1][1] * pv1[9]
+              +   cd[0][1] * cd[0][1] * cd[1][0] * pv1[8]
+              + 2*cd[0][1] * cd[1][0] * cd[1][1] * pv1[9]
+              + 3*cd[1][0] * cd[1][1] * cd[1][1] * pv1[10] );
+  l[1][2] = ( 3*cd[0][0]   * cd[0][1] * cd[0][1] * pv2[10]
+              + 2*cd[0][0] * cd[0][1] * cd[1][1] * pv2[9]
+              +   cd[0][0] * cd[1][1] * cd[1][1] * pv2[8]
+              +   cd[0][1] * cd[0][1] * cd[1][0] * pv2[9]
+              + 2*cd[0][1] * cd[1][0] * cd[1][1] * pv2[8]
+              + 3*cd[1][0] * cd[1][1] * cd[1][1] * pv2[7] );
+
+
+  k[2][1] = ( 3*cd[0][0]   * cd[0][0] * cd[0][1] * pv1[7]
+              + 2*cd[0][0] * cd[0][1] * cd[1][0] * pv1[8]
+              +   cd[0][0] * cd[0][0] * cd[1][1] * pv1[8]
+              +   cd[0][1] * cd[1][0] * cd[1][0] * pv1[9]
+              + 2*cd[0][0] * cd[1][0] * cd[1][1] * pv1[9]
+              + 3*cd[1][0] * cd[1][0] * cd[1][1] * pv1[10] );
+  l[2][1] = ( 3*cd[0][0]   * cd[0][0] * cd[0][1] * pv2[10]
+              + 2*cd[0][0] * cd[0][1] * cd[1][0] * pv2[9]
+              +   cd[0][0] * cd[0][0] * cd[1][1] * pv2[9]
+              +   cd[0][1] * cd[1][0] * cd[1][0] * pv2[8]
+              + 2*cd[0][0] * cd[1][0] * cd[1][1] * pv2[8]
+              + 3*cd[1][0] * cd[1][0] * cd[1][1] * pv2[7] );
+
+
+  k[3][0] = ( cd[0][0]   * cd[0][0] * cd[0][0] * pv1[7]
+              + cd[0][0] * cd[0][0] * cd[1][0] * pv1[8]
+              + cd[0][0] * cd[1][0] * cd[1][0] * pv1[9]
+              + cd[1][0] * cd[1][0] * cd[1][0] * pv1[10] );
+  l[3][0] = ( cd[0][0]   * cd[0][0] * cd[0][0] * pv2[10]
+              + cd[0][0] * cd[0][0] * cd[1][0] * pv2[9]
+              + cd[0][0] * cd[1][0] * cd[1][0] * pv2[8]
+              + cd[1][0] * cd[1][0] * cd[1][0] * pv2[7] );
+
+
+  k[0][4] = ( cd[0][1]   * cd[0][1] * cd[0][1] * cd[0][1] * pv1[12]
+              + cd[0][1] * cd[0][1] * cd[1][1] * cd[1][1] * pv1[13]
+              + cd[0][1] * cd[0][1] * cd[1][1] * cd[1][1] * pv1[14]
+              + cd[0][1] * cd[1][1] * cd[1][1] * cd[1][1] * pv1[15]
+              + cd[1][1] * cd[1][1] * cd[1][1] * cd[1][1] * pv1[16] );
+  l[0][4] = ( cd[0][1]   * cd[0][1] * cd[0][1] * cd[0][1] * pv2[16]
+              + cd[0][1] * cd[0][1] * cd[1][1] * cd[1][1] * pv2[15]
+              + cd[0][1] * cd[0][1] * cd[1][1] * cd[1][1] * pv2[14]
+              + cd[0][1] * cd[1][1] * cd[1][1] * cd[1][1] * pv2[13]
+              + cd[1][1] * cd[1][1] * cd[1][1] * cd[1][1] * pv2[12] );
+
+
+  k[1][3] = ( 4*cd[0][0]   * cd[0][1] * cd[0][1] * cd[0][1] * pv1[12]
+              + 3*cd[0][0] * cd[0][1] * cd[0][1] * cd[1][1] * pv1[13]
+              + 2*cd[0][0] * cd[0][1] * cd[1][1] * cd[1][1] * pv1[14]
+              +   cd[0][0] * cd[1][1] * cd[1][1] * cd[1][1] * pv1[15]
+              +   cd[0][1] * cd[0][1] * cd[0][1] * cd[1][0] * pv1[13]
+              + 2*cd[0][1] * cd[0][1] * cd[1][0] * cd[1][1] * pv1[14]
+              + 3*cd[0][1] * cd[1][0] * cd[1][1] * cd[1][1] * pv1[15]
+              + 4*cd[1][0] * cd[1][1] * cd[1][1] * cd[1][1] * pv1[16] );
+  l[1][3] = ( 4*cd[0][0]   * cd[0][1] * cd[0][1] * cd[0][1] * pv2[16]
+              + 3*cd[0][0] * cd[0][1] * cd[0][1] * cd[1][1] * pv2[15]
+              + 2*cd[0][0] * cd[0][1] * cd[1][1] * cd[1][1] * pv2[14]
+              +   cd[0][0] * cd[1][1] * cd[1][1] * cd[1][1] * pv2[13]
+              +   cd[0][1] * cd[0][1] * cd[0][1] * cd[1][0] * pv2[15]
+              + 2*cd[0][1] * cd[0][1] * cd[1][0] * cd[1][1] * pv2[14]
+              + 3*cd[0][1] * cd[1][0] * cd[1][1] * cd[1][1] * pv2[13]
+              + 4*cd[1][0] * cd[1][1] * cd[1][1] * cd[1][1] * pv2[12] );
+
+
+  k[2][2] = ( 6*cd[0][0]   * cd[0][0] * cd[0][1] * cd[0][1] * pv1[12]
+              + 3*cd[0][0] * cd[0][0] * cd[0][1] * cd[1][1] * pv1[13]
+              +   cd[0][0] * cd[0][0] * cd[1][1] * cd[1][1] * pv1[14]
+              + 3*cd[0][0] * cd[0][1] * cd[0][1] * cd[1][0] * pv1[13]
+              + 4*cd[0][0] * cd[0][1] * cd[1][0] * cd[1][1] * pv1[14]
+              + 3*cd[0][0] * cd[1][0] * cd[1][1] * cd[1][1] * pv1[15]
+              +   cd[0][1] * cd[0][1] * cd[1][0] * cd[1][0] * pv1[14]
+              + 3*cd[0][1] * cd[1][0] * cd[1][0] * cd[1][1] * pv1[15]
+              + 6*cd[1][0] * cd[1][0] * cd[1][1] * cd[1][1] * pv1[16] );
+  l[2][2] = ( 6*cd[0][0]   * cd[0][0] * cd[0][1] * cd[0][1] * pv2[16]
+              + 3*cd[0][0] * cd[0][0] * cd[0][1] * cd[1][1] * pv2[15]
+              +   cd[0][0] * cd[0][0] * cd[1][1] * cd[1][1] * pv2[14]
+              + 3*cd[0][0] * cd[0][1] * cd[0][1] * cd[1][0] * pv2[15]
+              + 4*cd[0][0] * cd[0][1] * cd[1][0] * cd[1][1] * pv2[14]
+              + 3*cd[0][0] * cd[1][0] * cd[1][1] * cd[1][1] * pv2[15]
+              +   cd[0][1] * cd[0][1] * cd[1][0] * cd[1][0] * pv2[14]
+              + 3*cd[0][1] * cd[1][0] * cd[1][0] * cd[1][1] * pv2[13]
+              + 6*cd[1][0] * cd[1][0] * cd[1][1] * cd[1][1] * pv2[12] );
+
+
+  k[3][1] = ( 4*cd[0][0]   * cd[0][0] * cd[0][0] * cd[0][1] * pv1[12]
+              +   cd[0][0] * cd[0][0] * cd[0][0] * cd[1][1] * pv1[13]
+              + 3*cd[0][0] * cd[0][0] * cd[0][1] * cd[1][0] * pv1[13]
+              + 2*cd[0][0] * cd[0][0] * cd[1][0] * cd[1][1] * pv1[14]
+              + 2*cd[0][0] * cd[0][1] * cd[1][0] * cd[1][0] * pv1[14]
+              + 3*cd[0][0] * cd[1][0] * cd[1][0] * cd[1][1] * pv1[15]
+              +   cd[0][1] * cd[1][0] * cd[1][0] * cd[1][0] * pv1[15]
+              + 4*cd[1][0] * cd[1][0] * cd[1][0] * cd[1][1] * pv1[16] );
+  l[3][1] = ( 4*cd[0][0]   * cd[0][0] * cd[0][0] * cd[0][1] * pv2[16]
+              +   cd[0][0] * cd[0][0] * cd[0][0] * cd[1][1] * pv2[15]
+              + 3*cd[0][0] * cd[0][0] * cd[0][1] * cd[1][0] * pv2[15]
+              + 2*cd[0][0] * cd[0][0] * cd[1][0] * cd[1][1] * pv2[14]
+              + 2*cd[0][0] * cd[0][1] * cd[1][0] * cd[1][0] * pv2[14]
+              + 3*cd[0][0] * cd[1][0] * cd[1][0] * cd[1][1] * pv2[13]
+              +   cd[0][1] * cd[1][0] * cd[1][0] * cd[1][0] * pv2[13]
+              + 4*cd[1][0] * cd[1][0] * cd[1][0] * cd[1][1] * pv2[12] );
+
+
+  k[4][0] = ( cd[0][0]   * cd[0][0] * cd[0][0] * cd[0][0] * pv1[12]
+              + cd[0][0] * cd[0][0] * cd[0][0] * cd[1][0] * pv1[13]
+              + cd[0][0] * cd[0][0] * cd[1][0] * cd[1][0] * pv1[14]
+              + cd[0][0] * cd[1][0] * cd[1][0] * cd[1][0] * pv1[15]
+              + cd[1][0] * cd[1][0] * cd[1][0] * cd[1][0] * pv1[16] );
+  l[4][0] = ( cd[0][0]   * cd[0][0] * cd[0][0] * cd[0][0] * pv2[16]
+              + cd[0][0] * cd[0][0] * cd[0][0] * cd[1][0] * pv2[15]
+              + cd[0][0] * cd[0][0] * cd[1][0] * cd[1][0] * pv2[14]
+              + cd[0][0] * cd[1][0] * cd[1][0] * cd[1][0] * pv2[13]
+              + cd[1][0] * cd[1][0] * cd[1][0] * cd[1][0] * pv2[12] );
+
+
+  /* For a check:
+  {
+    size_t i, j;
+    for(i=0; i<=4; ++i)
+      for(j=0;j<=4;++j)
+       {
+         printf("k%ld_%ld \t %.10E\n",   i, j, k[i][j]);
+         printf("l%ld_%ld \t %.10E\n\n", i, j, l[i][j]);
+       }
+  }
+  */
+}
+
+
+
+
+
+
+/* Intermidiate equations formed during sip->pv conversions. */
+static void
+wcsdistortion_intermidate_sipeq(double cd[2][2], double cd_inv[2][2],
+                                double a_coeff[5][5], double b_coeff[5][5],
+                                double *pv1, double *pv2)
+{
+  /*Intemidiate equations which give the value of PVi_j
+    excluding radial terms PV[i,3], PV[i,11]
+
+    For the intermidiate PVi_j calculations, the Eqs. 1 and 2 from
+    section 2 of the SPIE proceedings paper at
+    http://web.ipac.caltech.edu/staff/shupe/reprints/SIP_to_PV_SPIE2012.pdf
+    are used which were further modified as shown in
+    https://github.com/stargaser/sip_tpv/blob/master/sip_tpv/pvsiputils.py
+    to generate these equtions.
+
+    The exact script used for generation of these equations are given here:
+    https://gitlab.com/sachinkumarsingh092/gnuastro-test-files/-/blob/
+    master/scripts/equations.py
+
+    References and citations:
+    "More flexibility in representing geometric distortion in
+    astronomical images,"
+    Shupe, David L.; Laher, Russ R.; Storrie-Lombardi, Lisa; Surace, Jason;
+    Grillmair, Carl; Levitan, David; Sesar, Branimir, 2012, in Software and
+    Cyberinfrastructure for Astronomy II.
+    Proceedings of the SPIE, Volume 8451, article id. 84511M.
+  */
+
+  /* pvi_0 */
+  pv1[0]=a_coeff[0][0]*cd[0][0] + b_coeff[0][0]*cd[0][1];
+  pv2[0]=a_coeff[0][0]*cd[1][0] + b_coeff[0][0]*cd[1][1];
+
+
+  /* pvi_1 */
+  pv1[1] = ( a_coeff[0][1]   * cd[0][0] * cd_inv[1][0]
+             + a_coeff[1][0] * cd[0][0] * cd_inv[0][0]
+             + b_coeff[0][1] * cd[0][1] * cd_inv[1][0]
+             + b_coeff[1][0] * cd[0][1] * cd_inv[0][0]
+             +      cd[0][0] * cd_inv[0][0]
+             +      cd[0][1] * cd_inv[1][0]  );
+
+  pv2[1] = ( a_coeff[0][1]   * cd[1][0] * cd_inv[1][1]
+             + a_coeff[1][0] * cd[1][0] * cd_inv[0][1]
+             + b_coeff[0][1] * cd[1][1] * cd_inv[1][1]
+             + b_coeff[1][0] * cd[1][1] * cd_inv[0][1]
+             +      cd[1][0] * cd_inv[0][1]
+             +      cd[1][1] * cd_inv[1][1]  );
+
+
+  /* pvi_2 */
+  pv1[2] = ( a_coeff[0][1]   * cd[0][0] * cd_inv[1][1]
+             + a_coeff[1][0] * cd[0][0] * cd_inv[0][1]
+             + b_coeff[0][1] * cd[0][1] * cd_inv[1][1]
+             + b_coeff[1][0] * cd[0][1] * cd_inv[0][1]
+             +      cd[0][0] * cd_inv[0][1]
+             +      cd[0][1] * cd_inv[1][1]  );
+
+  pv2[2] = ( a_coeff[0][1]   * cd[1][0] * cd_inv[1][0]
+             + a_coeff[1][0] * cd[1][0] * cd_inv[0][0]
+             + b_coeff[0][1] * cd[1][1] * cd_inv[1][0]
+             + b_coeff[1][0] * cd[1][1] * cd_inv[0][0]
+             +      cd[1][0] * cd_inv[0][0]
+             +      cd[1][1] * cd_inv[1][0]  );
+
+
+  /* pvi_4 */
+  pv1[4] = ( a_coeff[0][2]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][0]
+             + a_coeff[1][1] * cd[0][0] * cd_inv[0][0] * cd_inv[1][0]
+             + a_coeff[2][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0]
+             + b_coeff[0][2] * cd[0][1] * cd_inv[1][0] * cd_inv[1][0]
+             + b_coeff[1][1] * cd[0][1] * cd_inv[0][0] * cd_inv[1][0]
+             + b_coeff[2][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] );
+
+  pv2[4] = ( a_coeff[0][2]   * cd[1][0] * cd_inv[1][1] * cd_inv[1][1]
+             + a_coeff[1][1] * cd[1][0] * cd_inv[0][1] * cd_inv[1][1]
+             + a_coeff[2][0] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1]
+             + b_coeff[0][2] * cd[1][1] * cd_inv[1][1] * cd_inv[1][1]
+             + b_coeff[1][1] * cd[1][1] * cd_inv[0][1] * cd_inv[1][1]
+             + b_coeff[2][0] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] );
+
+
+  /* pvi_5 */
+  pv1[5] = ( 2*a_coeff[0][2]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][1]
+             +   a_coeff[1][1] * cd[0][0] * cd_inv[0][0] * cd_inv[1][1]
+             +   a_coeff[1][1] * cd[0][0] * cd_inv[0][1] * cd_inv[1][0]
+             + 2*a_coeff[2][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1]
+             + 2*b_coeff[0][2] * cd[0][1] * cd_inv[1][0] * cd_inv[1][1]
+             +   b_coeff[1][1] * cd[0][1] * cd_inv[0][0] * cd_inv[1][1]
+             +   b_coeff[1][1] * cd[0][1] * cd_inv[0][1] * cd_inv[1][0]
+             + 2*b_coeff[2][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] );
+
+  pv2[5] = ( 2*a_coeff[0][2]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][1]
+             +   a_coeff[1][1] * cd[1][0] * cd_inv[0][0] * cd_inv[1][1]
+             +   a_coeff[1][1] * cd[1][0] * cd_inv[0][1] * cd_inv[1][0]
+             + 2*a_coeff[2][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1]
+             + 2*b_coeff[0][2] * cd[1][1] * cd_inv[1][0] * cd_inv[1][1]
+             +   b_coeff[1][1] * cd[1][1] * cd_inv[0][0] * cd_inv[1][1]
+             +   b_coeff[1][1] * cd[1][1] * cd_inv[0][1] * cd_inv[1][0]
+             + 2*b_coeff[2][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] );
+
+
+  /* pvi_6 */
+  pv1[6]= ( a_coeff[0][2]   * cd[0][0] * cd_inv[1][1] * cd_inv[1][1]
+            + a_coeff[1][1] * cd[0][0] * cd_inv[0][1] * cd_inv[1][1]
+            + a_coeff[2][0] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1]
+            + b_coeff[0][2] * cd[0][1] * cd_inv[1][1] * cd_inv[1][1]
+            + b_coeff[1][1] * cd[0][1] * cd_inv[0][1] * cd_inv[1][1]
+            + b_coeff[2][0] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] );
+
+  pv2[6]= ( a_coeff[0][2]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][0]
+            + a_coeff[1][1] * cd[1][0] * cd_inv[0][0] * cd_inv[1][0]
+            + a_coeff[2][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0]
+            + b_coeff[0][2] * cd[1][1] * cd_inv[1][0] * cd_inv[1][0]
+            + b_coeff[1][1] * cd[1][1] * cd_inv[0][0] * cd_inv[1][0]
+            + b_coeff[2][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] );
+
+
+  /* pvi_7 */
+  pv1[7] = ( a_coeff[0][3]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0]
+             + a_coeff[1][2] * cd[0][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0]
+             + a_coeff[2][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0]
+             + a_coeff[3][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0]
+             + b_coeff[0][3] * cd[0][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0]
+             + b_coeff[1][2] * cd[0][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0]
+             + b_coeff[2][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0]
+             + b_coeff[3][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] );
+
+  pv2[7] = ( a_coeff[0][3]   * cd[1][0] * cd_inv[1][1] * cd_inv[1][1] * 
cd_inv[1][1]
+             + a_coeff[1][2] * cd[1][0] * cd_inv[0][1] * cd_inv[1][1] * 
cd_inv[1][1]
+             + a_coeff[2][1] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][1]
+             + a_coeff[3][0] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1]
+             + b_coeff[0][3] * cd[1][1] * cd_inv[1][1] * cd_inv[1][1] * 
cd_inv[1][1]
+             + b_coeff[1][2] * cd[1][1] * cd_inv[0][1] * cd_inv[1][1] * 
cd_inv[1][1]
+             + b_coeff[2][1] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][1]
+             + b_coeff[3][0] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] );
+
+
+  /* pvi_8 */
+  pv1[8] = ( 3*a_coeff[0][3]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][1]
+             + 2*a_coeff[1][2] * cd[0][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][1]
+             +   a_coeff[1][2] * cd[0][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0]
+             +   a_coeff[2][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][1]
+             + 2*a_coeff[2][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0]
+             + 3*a_coeff[3][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1]
+             + 3*b_coeff[0][3] * cd[0][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][1]
+             + 2*b_coeff[1][2] * cd[0][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][1]
+             +   b_coeff[1][2] * cd[0][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0]
+             +   b_coeff[2][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][1]
+             + 2*b_coeff[2][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0]
+             + 3*b_coeff[3][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] );
+
+  pv2[8] = ( 3*a_coeff[0][3]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][1] * 
cd_inv[1][1]
+             +   a_coeff[1][2] * cd[1][0] * cd_inv[0][0] * cd_inv[1][1] * 
cd_inv[1][1]
+             + 2*a_coeff[1][2] * cd[1][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][1]
+             + 2*a_coeff[2][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][1]
+             +   a_coeff[2][1] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0]
+             + 3*a_coeff[3][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1]
+             + 3*b_coeff[0][3] * cd[1][1] * cd_inv[1][0] * cd_inv[1][1] * 
cd_inv[1][1]
+             +   b_coeff[1][2] * cd[1][1] * cd_inv[0][0] * cd_inv[1][1] * 
cd_inv[1][1]
+             + 2*b_coeff[1][2] * cd[1][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][1]
+             + 2*b_coeff[2][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][1]
+             +   b_coeff[2][1] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0]
+             + 3*b_coeff[3][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] );
+
+
+  /* pvi_9 */
+  pv1[9] = ( 3*a_coeff[0][3]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][1] * 
cd_inv[1][1]
+             +   a_coeff[1][2] * cd[0][0] * cd_inv[0][0] * cd_inv[1][1] * 
cd_inv[1][1]
+             + 2*a_coeff[1][2] * cd[0][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][1]
+             + 2*a_coeff[2][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][1]
+             +   a_coeff[2][1] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0]
+             + 3*a_coeff[3][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1]
+             + 3*b_coeff[0][3] * cd[0][1] * cd_inv[1][0] * cd_inv[1][1] * 
cd_inv[1][1]
+             +   b_coeff[1][2] * cd[0][1] * cd_inv[0][0] * cd_inv[1][1] * 
cd_inv[1][1]
+             + 2*b_coeff[1][2] * cd[0][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][1]
+             + 2*b_coeff[2][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][1]
+             +   b_coeff[2][1] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0]
+             + 3*b_coeff[3][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] );
+
+  pv2[9] = ( 3*a_coeff[0][3]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][1]
+             + 2*a_coeff[1][2] * cd[1][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][1]
+             +   a_coeff[1][2] * cd[1][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0]
+             +   a_coeff[2][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][1]
+             + 2*a_coeff[2][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0]
+             + 3*a_coeff[3][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1]
+             + 3*b_coeff[0][3] * cd[1][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][1]
+             + 2*b_coeff[1][2] * cd[1][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][1]
+             +   b_coeff[1][2] * cd[1][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0]
+             +   b_coeff[2][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][1]
+             + 2*b_coeff[2][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0]
+             + 3*b_coeff[3][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] );
+
+
+  /* pvi_10 */
+  pv1[10] = ( a_coeff[0][3]   * cd[0][0] * cd_inv[1][1] * cd_inv[1][1] * 
cd_inv[1][1]
+              + a_coeff[1][2] * cd[0][0] * cd_inv[0][1] * cd_inv[1][1] * 
cd_inv[1][1]
+              + a_coeff[2][1] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][1]
+              + a_coeff[3][0] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1]
+              + b_coeff[0][3] * cd[0][1] * cd_inv[1][1] * cd_inv[1][1] * 
cd_inv[1][1]
+              + b_coeff[1][2] * cd[0][1] * cd_inv[0][1] * cd_inv[1][1] * 
cd_inv[1][1]
+              + b_coeff[2][1] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][1]
+              + b_coeff[3][0] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] );
+
+
+  pv2[10] = ( a_coeff[0][3]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0]
+              + a_coeff[1][2] * cd[1][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0]
+              + a_coeff[2][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0]
+              + a_coeff[3][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0]
+              + b_coeff[0][3] * cd[1][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0]
+              + b_coeff[1][2] * cd[1][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0]
+              + b_coeff[2][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0]
+              + b_coeff[3][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] );
+
+
+  /* pvi_12 */
+  pv1[12] = ( a_coeff[0][4]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + a_coeff[1][3] * cd[0][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + a_coeff[2][2] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + a_coeff[3][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[1][0]
+              + a_coeff[4][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[0][0]
+              + b_coeff[0][4] * cd[0][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + b_coeff[1][3] * cd[0][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + b_coeff[2][2] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + b_coeff[3][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[1][0]
+              + b_coeff[4][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[0][0] );
+
+  pv2[12] = ( a_coeff[0][4]   * cd[1][0] * cd_inv[1][1] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + a_coeff[1][3] * cd[1][0] * cd_inv[0][1] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + a_coeff[2][2] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + a_coeff[3][1] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][1]
+              + a_coeff[4][0] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[0][1]
+              + b_coeff[0][4] * cd[1][1] * cd_inv[1][1] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + b_coeff[1][3] * cd[1][1] * cd_inv[0][1] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + b_coeff[2][2] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + b_coeff[3][1] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][1]
+              + b_coeff[4][0] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[0][1] );
+
+
+  /* pvi_13 */
+  pv1[13] = ( 4*a_coeff[0][4]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 3*a_coeff[1][3] * cd[0][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   a_coeff[1][3] * cd[0][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + 2*a_coeff[2][2] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 2*a_coeff[2][2] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][0]
+              +   a_coeff[3][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[1][1]
+              + 3*a_coeff[3][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 4*a_coeff[4][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[0][1]
+              + 4*b_coeff[0][4] * cd[0][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 3*b_coeff[1][3] * cd[0][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   b_coeff[1][3] * cd[0][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + 2*b_coeff[2][2] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 2*b_coeff[2][2] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][0]
+              +   b_coeff[3][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[1][1]
+              + 3*b_coeff[3][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 4*b_coeff[4][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[0][1] );
+
+
+  pv2[13] = ( 4*a_coeff[0][4]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              +   a_coeff[1][3] * cd[1][0] * cd_inv[0][0] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*a_coeff[1][3] * cd[1][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 2*a_coeff[2][2] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 2*a_coeff[2][2] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 3*a_coeff[3][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][1]
+              +   a_coeff[3][1] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 4*a_coeff[4][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[0][1]
+              + 4*b_coeff[0][4] * cd[1][1] * cd_inv[1][0] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              +   b_coeff[1][3] * cd[1][1] * cd_inv[0][0] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*b_coeff[1][3] * cd[1][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 2*b_coeff[2][2] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 2*b_coeff[2][2] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 3*b_coeff[3][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][1]
+              +   b_coeff[3][1] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 4*b_coeff[4][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[0][1] );
+
+
+  /* pvi_14 */
+  pv1[14] = ( 6*a_coeff[0][4]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*a_coeff[1][3] * cd[0][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*a_coeff[1][3] * cd[0][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   a_coeff[2][2] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 4*a_coeff[2][2] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   a_coeff[2][2] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][0]
+              + 3*a_coeff[3][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[1][1]
+              + 3*a_coeff[3][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 6*a_coeff[4][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[0][1]
+              + 6*b_coeff[0][4] * cd[0][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*b_coeff[1][3] * cd[0][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*b_coeff[1][3] * cd[0][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   b_coeff[2][2] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 4*b_coeff[2][2] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   b_coeff[2][2] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][0]
+              + 3*b_coeff[3][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[1][1]
+              + 3*b_coeff[3][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 6*b_coeff[4][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[0][1] );
+
+  pv2[14] = ( 6*a_coeff[0][4]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*a_coeff[1][3] * cd[1][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*a_coeff[1][3] * cd[1][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   a_coeff[2][2] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 4*a_coeff[2][2] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   a_coeff[2][2] * cd[1][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][0]
+              + 3*a_coeff[3][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[1][1]
+              + 3*a_coeff[3][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 6*a_coeff[4][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[0][1]
+              + 6*b_coeff[0][4] * cd[1][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*b_coeff[1][3] * cd[1][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*b_coeff[1][3] * cd[1][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   b_coeff[2][2] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 4*b_coeff[2][2] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   b_coeff[2][2] * cd[1][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][0]
+              + 3*b_coeff[3][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[1][1]
+              + 3*b_coeff[3][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 6*b_coeff[4][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[0][1] );
+
+
+  /* pvi_15 */
+  pv1[15] = ( 4*a_coeff[0][4]   * cd[0][0] * cd_inv[1][0] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              +  a_coeff[1][3]  * cd[0][0] * cd_inv[0][0] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*a_coeff[1][3] * cd[0][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 2*a_coeff[2][2] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 2*a_coeff[2][2] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 3*a_coeff[3][1] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][1]
+              +   a_coeff[3][1] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 4*a_coeff[4][0] * cd[0][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[0][1]
+              + 4*b_coeff[0][4] * cd[0][1] * cd_inv[1][0] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              +   b_coeff[1][3] * cd[0][1] * cd_inv[0][0] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 3*b_coeff[1][3] * cd[0][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 2*b_coeff[2][2] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + 2*b_coeff[2][2] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 3*b_coeff[3][1] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][1]
+              +   b_coeff[3][1] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 4*b_coeff[4][0] * cd[0][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[0][1] );
+
+  pv2[15] = ( 4*a_coeff[0][4]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 3*a_coeff[1][3] * cd[1][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   a_coeff[1][3] * cd[1][0] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + 2*a_coeff[2][2] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 2*a_coeff[2][2] * cd[1][0] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][0]
+              +   a_coeff[3][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[1][1]
+              + 3*a_coeff[3][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 4*a_coeff[4][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[0][1]
+              + 4*b_coeff[0][4] * cd[1][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 3*b_coeff[1][3] * cd[1][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              +   b_coeff[1][3] * cd[1][1] * cd_inv[0][1] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + 2*b_coeff[2][2] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0] * cd_inv[1][1]
+              + 2*b_coeff[2][2] * cd[1][1] * cd_inv[0][0] * cd_inv[0][1] * 
cd_inv[1][0] * cd_inv[1][0]
+              +   b_coeff[3][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[1][1]
+              + 3*b_coeff[3][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][1] * cd_inv[1][0]
+              + 4*b_coeff[4][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[0][1] );
+
+
+  /* pvi_16 */
+  pv1[16] = ( a_coeff[0][4]   * cd[0][0] * cd_inv[1][1] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + a_coeff[1][3] * cd[0][0] * cd_inv[0][1] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + a_coeff[2][2] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + a_coeff[3][1] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][1]
+              + a_coeff[4][0] * cd[0][0] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[0][1]
+              + b_coeff[0][4] * cd[0][1] * cd_inv[1][1] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + b_coeff[1][3] * cd[0][1] * cd_inv[0][1] * cd_inv[1][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + b_coeff[2][2] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[1][1] * cd_inv[1][1]
+              + b_coeff[3][1] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[1][1]
+              + b_coeff[4][0] * cd[0][1] * cd_inv[0][1] * cd_inv[0][1] * 
cd_inv[0][1] * cd_inv[0][1] );
+
+  pv2[16] = ( a_coeff[0][4]   * cd[1][0] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + a_coeff[1][3] * cd[1][0] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + a_coeff[2][2] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + a_coeff[3][1] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[1][0]
+              + a_coeff[4][0] * cd[1][0] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[0][0]
+              + b_coeff[0][4] * cd[1][1] * cd_inv[1][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + b_coeff[1][3] * cd[1][1] * cd_inv[0][0] * cd_inv[1][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + b_coeff[2][2] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[1][0] * cd_inv[1][0]
+              + b_coeff[3][1] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[1][0]
+              + b_coeff[4][0] * cd[1][1] * cd_inv[0][0] * cd_inv[0][0] * 
cd_inv[0][0] * cd_inv[0][0] );
+
+
+  /* For a check:
+  {
+    size_t j;
+    for(j=0; j<=16 ;++j)
+      {
+        if (j == 3 || j == 11) continue;
+        printf("pv1_%ld \t %.12E\n", j, pv1[j]);
+        printf("pv2_%ld \t %.12E\n", j, pv2[j]);
+      }
+  }
+  */
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/**********             Calculations               ************/
+/**************************************************************/
+/* Calcualte the final PV equation from intermidiate equations. */
+static void
+wcsdistortion_calc_tpveq(struct wcsprm *wcs, double cd[2][2],
+                         double tpvu[8][8], double tpvv[8][8])
+{
+  /* tpvu and tpvv are u-v distortion equations in TPV convention. */
+  size_t i=0,j=0;
+  double cd_inv[2][2];
+  double determinant=0;
+  double k[5][5], l[5][5];
+  double pv1[17]={0}, pv2[17]={0};
+
+  /* Initialise the 2d matrices. */
+  for(i=0; i<2; ++i) for(j=0; j<2; ++j) cd_inv[i][j]=0;
+  for(i=0; i<5; ++i) for(j=0; j<5; ++j) { k[i][j]=0; l[i][j]=0; }
+
+  /* Estimate the parameters. */
+  wcsdistortion_get_tpvparams(wcs, cd, pv1, pv2);
+  wcsdistortion_intermidate_tpveq(cd, pv1, pv2, k, l);
+
+  /* We will find matrix tpvu and tpvv by finding inverse of
+     CD matrix and multiplying with tpv* matrix.
+     For inverse of a 2x2 matrix we use the below trasformations:
+              inverse(|a  b|) =  1 *|d  -b|
+                      |c  d|    |A| |-c  a|
+      where |A| is the determinant of the matrix which is calculated by:
+                          |A| = a*d-b*c.
+    */
+  determinant = cd[0][0]*cd[1][1] - cd[0][1]*cd[1][0];
+
+  /* Inverse matrix */
+  cd_inv[0][0]=cd[1][1]/determinant;      /* a */
+  cd_inv[0][1]=(-1*cd[0][1])/determinant; /* b */
+  cd_inv[1][0]=(-1*cd[1][0])/determinant; /* c */
+  cd_inv[1][1]=cd[0][0]/determinant;      /* d */
+
+  /*For a check.
+  printf("%.10lf\t%.10lf\t%.10lf\t%.10lf\n", cd_inv[0][0], cd_inv[0][1], \
+                                             cd_inv[1][0], cd_inv[1][1]);
+  printf("%.10lf\n", determinant);
+  */
+
+  /* For matrix tpvv and tpvu, we have to use the following
+     matrix equation:
+
+                  |tpvu| = cd_inv*|k[i][j]|
+                  |tpvv|          |l[i][j]|
+    though intermidate polynomial equations have to be calculated prior
+    to multiplycation with cd_inv.
+  */
+
+  for(i=0; i<=4; ++i)
+    for(j=0; j<=4; ++j)
+      {
+        tpvu[i][j]=cd_inv[0][0]*k[i][j]+cd_inv[0][1]*l[i][j];
+        tpvv[i][j]=cd_inv[1][0]*k[i][j]+cd_inv[1][1]*l[i][j];
+
+        /*For a check:
+        printf("%.10E, %.10E\n", tpvu[i][j], tpvv[i][j]);
+        */
+      }
+}
+
+
+
+
+
+/* Calcualte the final SIP equation from intermidiate equations
+   and extract pv coefficients from it. */
+static void
+wcsdistortion_calc_sipeq(struct wcsprm *wcs, double cd[2][2],
+                         double *pv1, double *pv2)
+{
+  /* tpvu and tpvv are u-v distortion equations in TPV convention. */
+  size_t i=0, j=0;
+  double cd_inv[2][2];
+  double determinant=0;
+  double a_coeff[5][5], b_coeff[5][5];
+
+  /* Initialise the 2d matrices. */
+  for(i=0;i<2;++i) for(j=0;j<2;++j) cd_inv[i][j]=0;
+  for(i=0;i<5;++i) for(j=0;j<5;++j) {a_coeff[i][j]=0; b_coeff[i][j]=0;}
+
+  /* We will find matrix tpvu and tpvv by finding inverse of
+     CD matrix and multiplying with tpv* matrix.
+     For inverse of a 2x2 matrix we use the below trasformations:
+              inverse(|a  b|) =  1 *|d  -b|
+                      |c  d|    |A| |-c  a|
+      where |A| is the determinant of the matrix which is calculated by:
+                          |A| = a*d-b*c.
+    */
+  wcsdistortion_get_sipparams(wcs, cd, a_coeff, b_coeff);
+  determinant = cd[0][0]*cd[1][1] - cd[0][1]*cd[1][0];
+
+  /* Inverse matrix */
+  cd_inv[0][0]=cd[1][1]/determinant;      /* a */
+  cd_inv[0][1]=(-1*cd[0][1])/determinant; /* b */
+  cd_inv[1][0]=(-1*cd[1][0])/determinant; /* c */
+  cd_inv[1][1]=cd[0][0]/determinant;      /* d */
+
+  /* Find the parameters. */
+  wcsdistortion_intermidate_sipeq( cd, cd_inv, a_coeff, b_coeff, pv1, pv2);
+
+  /*For a check.
+  printf("%.10lf\t%.10lf\t%.10lf\t%.10lf\n", cd_inv[0][0], cd_inv[0][1], \
+                                             cd_inv[1][0], cd_inv[1][1]);
+  printf("%.10lf\n", determinant);
+  */
+}
+
+
+
+
+
+/* Calculate the SIP coefficients from CD matrix parameters and
+   PV coefficients. */
+static double
+wcsdistortion_calcsip(size_t axis, size_t m, size_t n, double tpvu[8][8],
+                      double tpvv[8][8])
+{
+  double sip_coeff;
+  if(     axis == 1) sip_coeff=tpvu[m][n];
+  else if(axis == 2) sip_coeff=tpvv[m][n];
+  else error(EXIT_FAILURE, 0, "%s: axis %zu does not exists! ",
+             __func__, axis);
+
+  if(      (axis == 1) && (m == 1) && (n == 0) ) sip_coeff = sip_coeff - 1.0;
+  else if( (axis == 2) && (m == 0) && (n == 1) ) sip_coeff = sip_coeff - 1.0;
+
+  return sip_coeff;
+}
+
+
+
+
+
+/* To calculate reverse sip coefficients, we make a grid and polpulate
+   it with forward coefficients. Then we fit a polynomial through these
+   points and finally extract coefficeint from these polynomial which are
+   the reverse sip cefficents. */
+static void
+wcsdistortion_fitreverse(double *u, double *v, size_t a_order, size_t b_order,
+                         size_t naxis1, size_t naxis2, double a_coeff[5][5],
+                         double b_coeff[5][5], double ap_coeff[5][5],
+                         double bp_coeff[5][5])
+{
+  double chisq_ap, chisq_bp;
+  double *udiff=NULL, *vdiff=NULL;
+  double *uprime=NULL, *vprime=NULL;
+  double **udict=NULL, **vdict=NULL;
+  size_t tsize=(naxis1/4)*(naxis2/4);
+  double **updict=NULL, **vpdict=NULL;
+  gsl_vector *y_ap, *y_bp, *c_ap, *c_bp;
+  size_t ap_order=a_order, bp_order=b_order;
+  gsl_matrix *X_ap, *X_bp, *cov_ap, *cov_bp;
+  size_t i=0, j=0, k=0, p_ap=0, p_bp=0, ij=0;
+  gsl_multifit_linear_workspace *work_ap, *work_bp;
+
+  /* Storage structures used:
+
+     updict, vpdict - dictionaries (key-value pairs) with key being an
+                      integer starting from 0 to max(aporder,
+                      bporder)(inclusive) and its values are the uprime,
+                      vprime raised to the powers of corresponding keys for
+                      each key.
+
+     udict, vdict   - dictionaries (key-value pairs) with key being an
+                      integer starting from 0 to max(aorder,
+                      border)(inclusive) and its values are the u, v raised
+                      to the powers of corresponding keys for each key.
+
+     u, v           - The 1d representation of 2d grid of all points
+                      strating from -CRPIXi to NAXISi - CRPIXi (with a
+                      stride of 4). CRPIXi is subtracted to bring pixels
+                      in world coordinate system (wcs).
+
+     uprime, vprime - The grid (represented internally as a 1d array)
+                      with forward coefficients fitted on them
+                      using the relevant udict, vdict values.
+
+     udiff, vdiff   - 1d array with the values of uprime, vprim subtracted
+                      from u, v arrays.
+
+
+     In matrix equation AX=B,
+     For axis 1 - A = transpose of a matrix of udict*vdict
+                  B = udiff
+     For axis 2 - A = transpose of a matrix of updict*vpdict
+                  B = vdiff
+    */
+
+  /* Allocate updict and vpdict and initialize them. */
+  updict=malloc((ap_order+1)*sizeof(*updict));
+  vpdict=malloc((bp_order+1)*sizeof(*vpdict));
+  for(i=0; i<=wcsdistortion_max(ap_order, bp_order); ++i)
+    {
+      updict[i]=malloc(tsize*sizeof(updict));
+      vpdict[i]=malloc(tsize*sizeof(vpdict));
+    }
+
+  /* Allocate and initialize uprime and vprime. */
+  uprime=malloc(tsize*sizeof(*uprime));
+  vprime=malloc(tsize*sizeof(*vprime));
+  for(i=0; i<tsize; ++i)
+    {
+      uprime[i]=u[i];
+      vprime[i]=v[i];
+    }
+
+  /* Allocate and initialize udict and vdict.*/
+  udict=malloc((a_order+1)*sizeof(*udict));
+  vdict=malloc((b_order+1)*sizeof(*vdict));
+  for(i=0; i<=wcsdistortion_max(a_order, b_order); ++i)
+    {
+      udict[i]=malloc(tsize*sizeof(udict));
+      vdict[i]=malloc(tsize*sizeof(vdict));
+    }
+  for(i=0; i<tsize; ++i)
+    {
+      udict[0][i]=1;
+      vdict[0][i]=1;
+    }
+
+
+  /* Fill the values from the in the dicts. The rows of the
+      dicts act as a key to achieve a key-value functionality. */
+  for(i=1; i<=a_order; ++i)
+    for(j=0; j<tsize; ++j)
+      udict[i][j]=udict[i-1][j]*u[j];
+
+  for(i=1; i<=b_order; ++i)
+    for(j=0; j<tsize; ++j)
+      vdict[i][j]=vdict[i-1][j]*v[j];
+
+
+  /* Populating forward coefficients on a grid. */
+  for(i=0; i<=a_order; ++i)
+    for(j=0; j<=a_order-i; ++j)
+      {
+        for(k=0; k<tsize; ++k)
+          uprime[k]+=a_coeff[i][j]*udict[i][k]*vdict[j][k];
+
+        /* The number of parameters for AP_* coefficients. */
+        ++p_ap;
+      }
+  for(i=0; i<=b_order; ++i)
+    for(j=0; j<=b_order-i; ++j)
+      {
+        for(k=0; k<tsize; ++k)
+          vprime[k]+=b_coeff[i][j]*udict[i][k]*vdict[j][k];
+
+        /* The number of parameters for BP_* coefficients. */
+        ++p_bp;
+      }
+
+  /*For a check.
+  for(i=0; i<=a_order; ++i)
+    for(j=0; j<tsize; ++j)
+    {
+      printf("udict[%ld][%ld] = %.8lf\n", i, j, uprime[j]);
+      printf("u%ld = %.10E\n", i, u[i]);
+    }
+  */
+
+  /* Now we have a grid populated with forward coeffiecients.  Now we fit a
+     reverse polynomial through points using multiparameter linear least
+     square fittings.*/
+
+  /* Initialize dicts. */
+  for(i=0; i<tsize; ++i)
+    {
+      updict[0][i]=1;
+      vpdict[0][i]=1;
+    }
+
+  /* Fill the values from the in the dicts. The rows of the
+      dicts act as a key to achieve a key-value functionality. */
+  for(i=1; i<=ap_order; ++i)
+    for(j=0; j<tsize; ++j)
+      updict[i][j]=updict[i-1][j]*uprime[j];
+
+  for(i=1; i<=bp_order; ++i)
+    for(j=0; j<tsize; ++j)
+      vpdict[i][j]=vpdict[i-1][j]*vprime[j];
+
+  /* Allocate memory for Multi-parameter Linear Regressions. */
+  X_ap = gsl_matrix_alloc (tsize, p_ap);
+  X_bp = gsl_matrix_alloc (tsize, p_bp);
+
+  y_ap = gsl_vector_alloc (tsize);
+  y_bp = gsl_vector_alloc (tsize);
+
+  c_ap = gsl_vector_alloc (p_ap);
+  c_bp = gsl_vector_alloc (p_bp);
+
+  cov_ap = gsl_matrix_alloc (p_ap, p_ap);
+  cov_bp = gsl_matrix_alloc (p_bp, p_bp);
+
+  /* Allocate and initialize udiff and vdiff. */
+  udiff=malloc(tsize*sizeof(*udiff));
+  vdiff=malloc(tsize*sizeof(*vdiff));
+  for(i=0; i<tsize; ++i)
+    {
+      udiff[i]=u[i]-uprime[i];
+      vdiff[i]=v[i]-vprime[i];
+
+      gsl_vector_set (y_ap, i, udiff[i]);
+      gsl_vector_set (y_bp, i, vdiff[i]);
+    }
+
+  /* Filling up he X_ap matrix in column-wise order. */
+  for(i=0; i<=ap_order; ++i)
+    for(j=0; j<=ap_order-i; ++j)
+      {
+        for(k=0; k<tsize; ++k)
+          {
+            gsl_matrix_set(X_ap, k, ij, updict[i][k]*vpdict[j][k]);
+
+            /*For a check.
+            printf("x_ap[%ld] = %.8lf\n", ij, updict[i][k]*vpdict[j][k]);
+            */
+          }
+        ++ij;
+      }
+
+  ij=0;
+  /* Filling up he X_bp matrix in column-wise order. */
+  for(i=0; i<=bp_order; ++i)
+    for(j=0; j<=bp_order-i; ++j)
+      {
+        for(k=0; k<tsize; ++k)
+          {
+            gsl_matrix_set(X_bp, k, ij, updict[i][k]*vpdict[j][k]);
+
+            /*For a check.
+            printf("x_bp[%ld] = %.8lf\n", ij, updict[i][k]*vpdict[j][k]);
+            */
+          }
+        ++ij;
+      }
+
+  /* Initialize and do the linear least square fitting. */
+  work_ap = gsl_multifit_linear_alloc (tsize, p_ap);
+  work_bp = gsl_multifit_linear_alloc (tsize, p_bp);
+  gsl_multifit_linear(X_ap, y_ap, c_ap, cov_ap, &chisq_ap, work_ap);
+  gsl_multifit_linear(X_bp, y_bp, c_bp, cov_bp, &chisq_bp, work_bp);
+
+  /* Extract the reverse coefficients. */
+  p_ap=0;
+  for(i=0; i<=ap_order; ++i)
+    for(j=0; j<=ap_order-i; ++j)
+    {
+        ap_coeff[i][j]=gsl_vector_get(c_ap, p_ap);
+
+        /*For a check.
+        printf("AP_%ld_%ld = %.8E\n", i, j, ap_coeff[i][j]);
+        */
+
+        ++p_ap;
+    }
+  p_bp=0;
+  for(i=0; i<=bp_order; ++i)
+    for(j=0; j<=bp_order-i; ++j)
+    {
+        bp_coeff[i][j]=gsl_vector_get(c_bp, p_bp);
+
+        /*For a check.
+        printf("BP_%ld_%ld = %.8E\n", i, j, bp_coeff[i][j]);
+        */
+
+        ++p_bp;
+    }
+
+  /*For a check.
+  for(i=0; i<p_ap; ++i)
+    for(j=0; j<tsize; ++j)
+      printf("X[%ld][%ld] = %.8lf\n", i, j, gsl_matrix_get(X_ap, j, i));
+  */
+
+  /* Free the memory allocations.*/
+  gsl_multifit_linear_free(work_bp);
+  gsl_multifit_linear_free(work_ap);
+  gsl_matrix_free(cov_bp);
+  gsl_matrix_free(cov_ap);
+  gsl_vector_free(c_bp);
+  gsl_vector_free(c_ap);
+  gsl_vector_free(y_bp);
+  gsl_vector_free(y_ap);
+  gsl_matrix_free(X_bp);
+  gsl_matrix_free(X_ap);
+  free(vdiff);
+  free(udiff);
+  free(vprime);
+  free(uprime);
+
+  for(i=0; i<ap_order; ++i) { free(vpdict[i]); free(updict[i]); }
+  for(i=0; i<a_order; ++i) { free(vdict[i]); free(udict[i]); }
+  free(vpdict);
+  free(updict);
+  free(vdict);
+  free(udict);
+}
+
+
+
+
+
+
+/* Calculate the reverse sip coefficients. */
+static void
+wcsdistortion_get_revkeyvalues(struct wcsprm *wcs, size_t *fitsize,
+                               double ap_coeff[5][5], double bp_coeff[5][5])
+{
+  size_t tsize;
+  size_t i, j, k;
+  double *u=NULL, *v=NULL;
+  size_t a_order=0, b_order=0;
+  double a_coeff[5][5], b_coeff[5][5];
+  size_t naxis1=fitsize[1], naxis2=fitsize[0];
+  double crpix1=wcs->crpix[0], crpix2=wcs->crpix[1];
+
+  /* Initialise the 2d matrices. */
+  tsize=(naxis1/4)*(naxis2/4);
+  for(i=0;i<5;++i) for(j=0;j<5;++j) {a_coeff[i][j]=0; b_coeff[i][j]=0;}
+
+  /* Allocate the size of u,v arrays. */
+  u=malloc(tsize*sizeof(*u));
+  v=malloc(tsize*sizeof(*v));
+
+  if(u==NULL)
+    error(EXIT_FAILURE, 0, "%s: allocating %zu bytes for `u'",
+          __func__, tsize*sizeof(*u));
+  if(v==NULL)
+    error(EXIT_FAILURE, 0, "%s: allocating %zu bytes for `v'",
+          __func__, tsize*sizeof(*v));
+
+
+  /* Make the grid and bring it's origin to the world's origin*/
+  k=0; for(i=0; i<naxis2; i+=4) for(j=0; j<naxis1; j+=4) u[k++]=j-crpix1;
+  k=0; for(i=0; i<naxis2; i+=4) for(j=0; j<naxis1; j+=4) v[k++]=i-crpix2;
+  /*For a check.
+    for(i=0; i<tsize; ++i)
+    printf("u%ld = %.10E\n", i, u[i]);
+  */
+
+  wcsdistortion_get_sipcoeff(wcs, &a_order, &b_order, a_coeff, b_coeff);
+
+  wcsdistortion_fitreverse(u, v, a_order, b_order, naxis1, naxis2,
+                           a_coeff, b_coeff, ap_coeff, bp_coeff);
+
+  /* Free the memory allocations. */
+  free(v);
+  free(u);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************/
+/**********          Writing utilities             ************/
+/**************************************************************/
+/* Make the sip key-cards and add them to fullheader.
+
+   Return:
+   char *fullheader - string of keycards.*/
+static char *
+wcsdistortion_add_sipkeywords(struct wcsprm *wcs, size_t *fitsize,
+                              double tpvu[8][8], double tpvv[8][8],
+                              int add_reverse, int *nkeys)
+{
+  double val=0;
+  uint8_t i, j, k=0;
+  int size = wcs->naxis;
+  size_t a_order=0, b_order=0;
+  size_t ap_order=0, bp_order=0;
+  size_t m, n, num=0, numkey=200;
+  double ap_coeff[5][5], bp_coeff[5][5];
+
+  /* The literal strings are a little longer than necessary because GCC
+     will complain/warn that size_t can be very long. Although it will
+     never happen that we need to write 10-decimal numbers in these, but to
+     have a clean build, we'll just set the literal space to be large
+     enough to avoid the warnings. */
+  char *fullheader, fmt[50], sipkey[50], keyaxis[50], pcaxis[50];
+
+  /* Initialise the 2d matrices. */
+  *nkeys = 0;
+  for(i=0;i<5;++i) for(j=0;j<5;++j) {ap_coeff[i][j]=0; bp_coeff[i][j]=0;}
+
+  /* The format for each card. */
+  sprintf(fmt, "%%-8s= %%20.12E%%50s");
+
+  /* Allcate memory for cards. */
+  fullheader = malloc(numkey*80);
+  if(fullheader==NULL)
+    error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `fullheader'",
+          __func__, sizeof *fullheader);
+
+  /* Add other necessary cards. */
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20d%50s", "WCSAXES",
+          wcs->naxis, "");
+
+  for(i=1; i<=size; ++i)
+    {
+      sprintf(keyaxis, "CRPIX%d", i);
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.8lf%50s", keyaxis,
+              wcs->crpix[i-1], "");
+    }
+
+  for(i=1; i<=size; ++i)
+    for(j=1; j<=size; ++j)
+      {
+        sprintf(pcaxis, "PC%d_%d", i, j);
+        sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.17lf%50s", pcaxis,
+                wcs->pc[k++], "");
+      }
+
+  for(i=1; i<=size; ++i)
+    {
+      sprintf(keyaxis, "CDELT%d", i);
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.17lf%50s", keyaxis,
+              wcs->cdelt[i-1], "");
+    }
+
+  for(i=1; i<=size; ++i)
+    {
+      sprintf(keyaxis, "CUNIT%d", i);
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %-70s", keyaxis,
+              wcs->cunit[i-1]);
+    }
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %-70s", "CTYPE1",
+          "'RA---TAN-SIP'");
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %-70s", "CTYPE2",
+          "'DEC--TAN-SIP'");
+
+  for(i=1; i<=size; ++i)
+    {
+      sprintf(keyaxis, "CRVAL%d", i);
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.10lf%50s", keyaxis,
+              wcs->crval[i-1], "");
+    }
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.17lf%50s", "LONPOLE",
+          wcs->lonpole, "");
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.17lf%50s", "LATPOLE",
+          wcs->latpole, "");
+
+#if GAL_CONFIG_HAVE_WCSLIB_MJDREF == 1
+  for(i=1; i<=size; ++i)
+    sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.1lf%50s", "MJDREFI",
+            wcs->mjdref[i-1], "");
+#endif
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %-70s", "RADESYS",
+          wcs->radesys);
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.1lf%50s", "EQUINOX",
+          wcs->equinox, "");
+
+
+  for(m=0; m<=4; ++m)
+    for(n=0; n<=4; ++n)
+      {
+        /*For axis = 1*/
+        val=wcsdistortion_calcsip(1, m, n, tpvu, tpvv);
+        if(val != 0)
+          {
+            /* Make keywords */
+            sprintf(sipkey, "A_%zu_%zu", m, n);
+            sprintf(fullheader+(FLEN_CARD-1)*num++, fmt, sipkey, val, "");
+            a_order=wcsdistortion_max(a_order, wcsdistortion_max(m,n));
+          }
+
+        /*For axis = 2*/
+        val=wcsdistortion_calcsip(2, m, n, tpvu, tpvv);
+        if(val != 0)
+          {
+            /* Make keywords */
+            sprintf(sipkey, "B_%zu_%zu", m, n);
+            sprintf(fullheader+(FLEN_CARD-1)*num++, fmt, sipkey, val, "");
+            b_order=wcsdistortion_max(b_order, wcsdistortion_max(m,n));
+          }
+
+      }
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20zu%50s", "A_ORDER",
+          a_order, "");
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20zu%50s", "B_ORDER",
+          b_order, "");
+
+  /* If reverse coefficients are required. */
+  if( add_reverse )
+    {
+      ap_order=a_order;
+      bp_order=b_order;
+
+      wcsdistortion_get_revkeyvalues(wcs, fitsize, ap_coeff, bp_coeff);
+
+      for(m=0; m<=ap_order; ++m)
+        for(n=0; n<=ap_order-m; ++n)
+          {
+            /*For axis = 1*/
+            val=ap_coeff[m][n];
+            if(val != 0)
+              {
+                /* Make keywords */
+                sprintf(sipkey, "AP_%zu_%zu", m, n);
+                sprintf(fullheader+(FLEN_CARD-1)*num++, fmt, sipkey,
+                        val, "");
+              }
+
+            /*For axis = 2*/
+            val=bp_coeff[m][n];
+            if(val != 0)
+              {
+                /* Make keywords */
+                sprintf(sipkey, "BP_%zu_%zu", m, n);
+                sprintf(fullheader+(FLEN_CARD-1)*num++, fmt, sipkey,
+                        val, "");
+              }
+          }
+
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20zu%50s", "AP_ORDER",
+              ap_order, "");
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20zu%50s", "BP_ORDER",
+              bp_order, "");
+
+    }
+
+  /*For a check.
+    printf("%s\n", fullheader);
+  */
+  *nkeys = num;
+  return fullheader;
+}
+
+
+
+
+
+
+/* Make the pv key-cards and add them to fullheader.
+
+   Return:
+   char *fullheader - string of keycards. */
+static char *
+wcsdistortion_add_pvkeywords(struct wcsprm *wcs, double *pv1,
+                             double *pv2, int *nkeys)
+{
+
+  double val=0;
+  uint8_t i, j, k=0;
+  int size = wcs->naxis;
+  size_t m, n, num=0, numkey=100;
+
+  /* The literal strings are a little longer than necessary because GCC
+     will complain/warn that size_t can be very long. Although it will
+     never happen that we need to write 10-decimal numbers in these, but to
+     have a clean build, we'll just set the literal space to be large
+     enough to avoid the warnings. */
+  char *fullheader, fmt[50], pvkey[50], keyaxis[50], pcaxis[50];
+
+  /* Initialize values. */
+  *nkeys = 0;
+
+  /* The format for each card. */
+  sprintf(fmt, "%%-8s= %%20.12E%%50s");
+
+  /* Allcate memory for cards. */
+  fullheader = malloc(numkey*80);
+  if(fullheader==NULL)
+        error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for `fullheader'",
+              __func__, sizeof *fullheader);
+
+  /* Add other necessary cards. */
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20d%50s", "WCSAXES",
+          wcs->naxis, "");
+
+  for(i=1; i<=size; ++i)
+    {
+      sprintf(keyaxis, "CRPIX%d", i);
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.8lf%50s", keyaxis,
+              wcs->crpix[i-1], "");
+    }
+
+  for(i=1; i<=size; ++i)
+    for(j=1; j<=size; ++j)
+      {
+        sprintf(pcaxis, "PC%d_%d", i, j);
+        sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.17lf%50s", pcaxis,
+                wcs->pc[k++], "");
+      }
+
+  for(i=1; i<=size; ++i)
+    {
+      sprintf(keyaxis, "CDELT%d", i);
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.17lf%50s", keyaxis,
+              wcs->cdelt[i-1], "");
+    }
+
+  for(i=1; i<=size; ++i)
+    {
+      sprintf(keyaxis, "CUNIT%d", i);
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %-70s", keyaxis,
+              wcs->cunit[i-1]);
+    }
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %-70s", "CTYPE1",
+          "'RA---TPV'");
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %-70s", "CTYPE2",
+          "'DEC--TPV'");
+
+  for(i=1; i<=size; ++i)
+    {
+      sprintf(keyaxis, "CRVAL%d", i);
+      sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.10lf%50s", keyaxis,
+              wcs->crval[i-1], "");
+    }
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.17lf%50s", "LONPOLE",
+          wcs->lonpole, "");
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.17lf%50s", "LATPOLE",
+          wcs->latpole, "");
+
+#if GAL_CONFIG_HAVE_WCSLIB_MJDREF == 1
+  for(i=1; i<=size; ++i)
+    sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.1lf%50s", "MJDREFI",
+            wcs->mjdref[i-1], "");
+#endif
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %-70s", "RADESYS",
+          wcs->radesys);
+
+  sprintf(fullheader+(FLEN_CARD-1)*num++, "%-8s= %20.1lf%50s", "EQUINOX",
+          wcs->equinox, "");
+
+  for(m=1; m<=2; ++m)
+    for(n=0; n<=16; ++n)
+      {
+        /*For axis = 1*/
+        if(m == 1)
+          {
+            val=pv1[n];
+            if(val != 0)
+              {
+                /* Make keywords */
+                sprintf(pvkey, "PV%zu_%zu", m, n);
+                sprintf(fullheader+(FLEN_CARD-1)*num++, fmt, pvkey,
+                        val, "");
+              }
+          }
+
+        /*For axis = 2*/
+        if(m == 2)
+          {
+            val=pv2[n];
+            if(val != 0)
+              {
+                /* Make keywords */
+                sprintf(pvkey, "PV%zu_%zu", m, n);
+                sprintf(fullheader+(FLEN_CARD-1)*num++, fmt, pvkey,
+                        val, "");
+              }
+          }
+
+      }
+
+  /*For a check.
+  printf("%s\n", fullheader);
+  */
+  *nkeys = num;
+  return fullheader;
+
+}
+
+
+
+
+
+ /* Set the internal structure and do sanity checks. */
+static void
+wcsdistortion_set_internalstruct(struct wcsprm *wcs, char *fullheader,
+                                 size_t fulllen, int status, int nwcs,
+                                 int sumcheck)
+{
+  size_t i;
+
+  if( wcs == NULL )
+  {
+
+    fprintf(stderr, "\n##################\n"
+            "WCSLIB Warning: wcspih ERROR %d: %s.\n"
+            "##################\n",
+            status, wcs_errmsg[status]);
+    wcs=NULL; nwcs=0;
+  }
+
+  if(wcs)
+    {
+      /* It may happen that the WCS-related keyword values are stored as
+         strings (they have single-quotes around them). In this case,
+         WCSLIB will read the CRPIX and CRVAL values as zero. When this
+         happens do a small check and abort, while informing the user about
+         the problem. */
+      sumcheck=0;
+      for(i=0;i<wcs->naxis;++i)
+        {sumcheck += (wcs->crval[i]==0.0f) + (wcs->crpix[i]==0.0f);}
+      if(sumcheck==wcs->naxis*2)
+        {
+          /* We only care about the first set of characters in each
+             80-character row, so we don't need to parse the last few
+             characters anyway. */
+          fulllen=strlen(fullheader)-12;
+          for(i=0;i<fulllen;++i)
+            if( strncmp(fullheader+i, "CRVAL1  = '", 11) == 0 )
+              fprintf(stderr, "WARNING: WCS Keyword values are not "
+                      "numbers.\n\n"
+                      "WARNING: The values to the WCS-related keywords are "
+                      "enclosed in single-quotes. In the FITS standard "
+                      "this is how string values are stored, therefore "
+                      "WCSLIB is unable to read them AND WILL PUT ZERO IN "
+                      "THEIR PLACE (creating a wrong WCS in the output). "
+                      "Please update the respective keywords of the input "
+                      "to be numbers (see next line).\n\n"
+                      "WARNING: You can do this with Gnuastro's `astfits' "
+                      "program and the `--update' option. The minimal WCS "
+                      "keywords that need a numerical value are: `CRVAL1', "
+                      "`CRVAL2', `CRPIX1', `CRPIX2', `EQUINOX' and "
+                      "`CD%%_%%' (or `PC%%_%%', where the %% are integers), "
+                      "please see the FITS standard, and inspect your FITS "
+                      "file to identify the full set of keywords that you "
+                      "need correct (for example PV%%_%% keywords).\n\n");
+        }
+
+      /* CTYPE is a mandatory WCS keyword, so if it hasn't been given (its
+         '\0'), then the headers didn't have a WCS structure. However,
+         WCSLIB still fills in the basic information (for example the
+         dimensionality of the dataset). */
+      if(wcs->ctype[0][0]=='\0')
+        {
+          wcsfree(wcs);
+          wcs=NULL;
+          nwcs=0;
+        }
+      else
+        {
+          /* For a check.
+          printf("flag: %d\n", wcs->flag);
+          printf("naxis: %d\n", wcs->naxis);
+          printf("crpix: %f, %f\n", wcs->crpix[0], wcs->crpix[1]);
+          printf("pc: %f, %f, %f, %f\n", wcs->pc[0], wcs->pc[1],
+                 wcs->pc[2], wcs->pc[3]);
+          printf("cdelt: %f, %f\n", wcs->cdelt[0], wcs->cdelt[1]);
+          printf("crval: %f, %f\n", wcs->crval[0], wcs->crval[1]);
+          printf("cunit: %s, %s\n", wcs->cunit[0], wcs->cunit[1]);
+          printf("ctype: %s, %s\n", wcs->ctype[0], wcs->ctype[1]);
+          printf("lonpole: %f\n", wcs->lonpole);
+          printf("latpole: %f\n", wcs->latpole);
+          */
+
+          /* Set the WCS structure. */
+          status=wcsset(wcs);
+          if(status)
+            {
+              fprintf(stderr, "\n##################\n"
+                      "WCSLIB Warning: wcsset ERROR %d: %s.\n"
+                      "##################\n",
+                      status, wcs_errmsg[status]);
+              wcsfree(wcs);
+              wcs=NULL;
+              nwcs=0;
+            }
+          else
+            /* A correctly useful WCS is present. When no PC matrix
+               elements were present in the header, the default PC matrix
+               (a unity matrix) is used. In this case WCSLIB doesn't set
+               `altlin' (and gives it a value of 0). In Gnuastro, later on,
+               we might need to know the type of the matrix used, so in
+               such a case, we will set `altlin' to 1. */
+            if(wcs->altlin==0) wcs->altlin=1;
+        }
+    }
+
+  /* For a check.
+    wcsprt(wcs);
+  */
+
+  /* Clean up. */
+  status=0;
+  if (fits_free_memory(fullheader, &status) )
+    gal_fits_io_error(status, "problem in freeing the memory used to "
+                      "keep all the headers");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/************************************************************************/
+/***************         High-level functions       *********************/
+/************************************************************************/
+/* After the actual TPV->SIP conversions are done and the keycards are
+   created using `wcsdistortion_add_sipkeywords`, we then use `wcspih`
+   to generate and return the new headers having SIP coefficients.
+
+   Parameters:
+     struct wcsprm *inwcs - The wcs parameters of the input fits file.
+     size_t *fitsize      - The size of the array along each dimension.
+
+   Return:
+     struct wcsprm *outwcs - The transformed wcs parameters in the
+                             sip distortion type.*/
+struct wcsprm *
+gal_wcsdistortion_tpv_to_sip(struct wcsprm *inwcs,
+                             size_t *fitsize)
+{
+  int ctrl=0;                /* Don't report why a keyword wasn't used. */
+  int nreject=0;             /* Number of keywords rejected for syntax. */
+  double cd[2][2];
+  size_t i=0, j=0;
+  size_t fulllen=0;
+  char *fullheader;
+  int relax=WCSHDR_all;      /* Macro: use all informal WCS extensions. */
+  int nwcs, sumcheck=0;
+  int nkeys=0, status=0;
+  struct wcsprm *outwcs=NULL;
+  double tpvu[8][8], tpvv[8][8];
+
+  /* Initialise the 2d matrices. */
+  for(i=0; i<2; ++i) for(j=0; j<2; ++j) cd[i][j]=0;
+  for(i=0; i<8; ++i) for(j=0; j<8; ++j) { tpvu[i][j]=0; tpvv[i][j]=0; }
+
+  /* Calculate the pv equations and extract sip coefficients from it. */
+  wcsdistortion_calc_tpveq(inwcs, cd, tpvu, tpvv);
+
+  /* Add the sip keywords. */
+  fullheader=wcsdistortion_add_sipkeywords(inwcs, fitsize, tpvu, tpvv,
+                                           1, &nkeys);
+
+  /* WCSlib function to parse the FITS headers. */
+  status=wcspih(fullheader, nkeys, relax, ctrl, &nreject, &nwcs, &outwcs);
+
+  /* Set the internal structure. */
+  wcsdistortion_set_internalstruct(outwcs, fullheader, fulllen, status,
+                                   nwcs, sumcheck);
+
+  /* Return the output WCS. */
+  return outwcs;
+
+}
+
+
+
+
+
+/* After the actual SIP->TPV conversions are done and the keycards are
+   created using `wcsdistortion_add_pvkeywords`, we then use `wcspih`
+   to generate and return the new headers having PV coefficients.
+
+   Parameters:
+     struct wcsprm *inwcs - The wcs parameters of the input fits file.
+
+   Return:
+     struct wcsprm *outwcs - The transformed wcs parameters in the
+                             pv distortion type.*/
+struct wcsprm *
+gal_wcsdistortion_sip_to_tpv(struct wcsprm *inwcs)
+{
+  int ctrl=0;                /* Don't report why a keyword wasn't used. */
+  int nreject=0;             /* Number of keywords rejected for syntax. */
+  double cd[2][2];
+  size_t i=0, j=0;
+  size_t fulllen=0;
+  char *fullheader;
+  int nwcs, sumcheck=0;
+  int nkeys=0, status=0;
+  int relax=WCSHDR_all;      /* Macro: use all informal WCS extensions. */
+  struct wcsprm *outwcs=NULL;
+  double pv1[17]={0}, pv2[17]={0};
+
+  /* Initialise the 2d matrices. */
+  for(i=0; i<2; ++i) for(j=0; j<2; ++j) cd[i][j]=0;
+
+  /* Calculate the sip equations and extract pv coefficients from it. */
+  wcsdistortion_calc_sipeq(inwcs, cd, pv1, pv2);
+
+  /* Add the pv keywords. */
+  fullheader=wcsdistortion_add_pvkeywords(inwcs, pv1, pv2, &nkeys);
+
+
+  /* WCSlib function to parse the FITS headers. */
+  status=wcspih(fullheader, nkeys, relax, ctrl, &nreject, &nwcs, &outwcs);
+
+  /* Set the internal structure. */
+  wcsdistortion_set_internalstruct(outwcs, fullheader, fulllen, status,
+                                   nwcs, sumcheck);
+
+  return outwcs;
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index cc9a6b4..e7f05d2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -3,7 +3,7 @@
 ## Original author:
 ##     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 ## Contributing author(s):
-## Copyright (C) 2015-2019, Free Software Foundation, Inc.
+## Copyright (C) 2015-2021, 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
@@ -22,7 +22,7 @@
 
 
 
-## Translate conditions that came from `./configure' into variables that can
+## Translate conditions that came from './configure' into variables that can
 ## be used here. This includes the programs and teh dependencies of the
 ## checks.
 ##
@@ -33,11 +33,11 @@
 ## the order that their dependence is satisfied. But that is prone to
 ## errors and a lot of problem. The user also not want to build all the
 ## programs. Also, Make allows us to work with multiple threads (with
-## `-jN') and in that case, it is vital to define the dependencies.
+## '-jN') and in that case, it is vital to define the dependencies.
 ##
 ## The output of all the tests that should be used (is recognized by
 ## Automake and will not mess the system) is the name of the test file
-## appended by a `.log'. It contains the output of the program on standard
+## appended by a '.log'. It contains the output of the program on standard
 ## output and error. THIS IS IMPORTANT: So even if the program fails, the
 ## .log file is created. The check if the input for a test exists or not
 ## should be checked in the test that depends on it, it can't be done here
@@ -98,7 +98,7 @@ endif
 if COND_CROP
   MAYBE_CROP_TESTS = crop/imgcat.sh crop/wcscat.sh crop/imgcenter.sh    \
   crop/imgcenternoblank.sh crop/section.sh crop/wcscenter.sh            \
-  crop/imgpolygon.sh crop/imgoutpolygon.sh crop/wcspolygon.sh
+  crop/imgpolygon.sh crop/imgpolygonout.sh crop/wcspolygon.sh
 
   crop/imgcat.sh: mkprof/mosaic1.sh.log
   crop/wcscat.sh: mkprof/mosaic1.sh.log mkprof/mosaic2.sh.log     \
@@ -109,7 +109,7 @@ if COND_CROP
   crop/wcscenter.sh: mkprof/mosaic1.sh.log mkprof/mosaic2.sh.log      \
                      mkprof/mosaic3.sh.log mkprof/mosaic4.sh.log
   crop/imgpolygon.sh: mkprof/mosaic1.sh.log
-  crop/imgoutpolygon.sh: mkprof/mosaic1.sh.log
+  crop/imgpolygonout.sh: mkprof/mosaic1.sh.log
   crop/wcspolygon.sh: mkprof/mosaic1.sh.log mkprof/mosaic2.sh.log \
                       mkprof/mosaic3.sh.log mkprof/mosaic4.sh.log
 endif
@@ -227,14 +227,14 @@ export check_with_program=$(MAYBE_CHECK_WITH_PROGRAM);
 # The Gnuastro library is checked by compiling programs and linking them
 # with the library. As described in the last paragraph of the "Scripts
 # based test suites" section of the Automake manual, all targets specified
-# by `check_PROGRAMS' are compiled prior to actually running the targets of
-# `TESTS'. So they do not need to be specified as any dependency, they will
-# be present when the `.sh' based tests are run.
-
-# The `gnuastro/config.h' (needed by Gnuastro's library) is built by
-# `../lib/Makefile.am' and is only meant for outside users (to be tested
-# here). Thus (unlike the programs, which use `config.h') we need to add
-# the top build directory to the include search directories (`-I').
+# by 'check_PROGRAMS' are compiled prior to actually running the targets of
+# 'TESTS'. So they do not need to be specified as any dependency, they will
+# be present when the '.sh' based tests are run.
+
+# The 'gnuastro/config.h' (needed by Gnuastro's library) is built by
+# '../lib/Makefile.am' and is only meant for outside users (to be tested
+# here). Thus (unlike the programs, which use 'config.h') we need to add
+# the top build directory to the include search directories ('-I').
 LDADD = -lgnuastro $(CONFIG_LDADD)
 AM_LDFLAGS = -L\$(top_builddir)/lib
 AM_CPPFLAGS = -I\$(top_srcdir)/lib -I\$(top_builddir)/lib
@@ -274,7 +274,7 @@ EXTRA_DIST = $(TESTS) during-dev.sh buildprog/simpleio.c 
crop/cat.txt     \
 
 
 
-# Files that must be cleaned with `make clean'.
+# Files that must be cleaned with 'make clean'.
 CLEANFILES = *.log *.txt *.jpg *.fits *.pdf *.eps simpleio
 
 
@@ -282,7 +282,7 @@ CLEANFILES = *.log *.txt *.jpg *.fits *.pdf *.eps simpleio
 
 
 # CLEANFILES is only for files, not directories. Therefore we are using
-# Automake's extending rules to clean the temporary `.gnuastro' directory
-# that was built by the `prepconf.sh' scripot. See "Extending Automake
+# Automake's extending rules to clean the temporary '.gnuastro' directory
+# that was built by the 'prepconf.sh' scripot. See "Extending Automake
 # rules", and the "What Gets Cleaned" sections of the Automake manual.
 clean-local:; rm -rf .gnuastro
diff --git a/tests/arithmetic/connected-components.sh 
b/tests/arithmetic/connected-components.sh
index a49851a..6214ff4 100755
--- a/tests/arithmetic/connected-components.sh
+++ b/tests/arithmetic/connected-components.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img 2 connected-components -hDETECTIONS   \
diff --git a/tests/arithmetic/onlynumbers.sh b/tests/arithmetic/onlynumbers.sh
index 7f36be2..4f8afd5 100755
--- a/tests/arithmetic/onlynumbers.sh
+++ b/tests/arithmetic/onlynumbers.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -46,7 +46,7 @@ if [ ! -f $execname ]; then echo "$execname not created."; 
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname -1 3.45 x
diff --git a/tests/arithmetic/or.sh b/tests/arithmetic/or.sh
index 49185cb..db5722e 100755
--- a/tests/arithmetic/or.sh
+++ b/tests/arithmetic/or.sh
@@ -1,4 +1,4 @@
-# Choose two detected regions with the `or' operator
+# Choose two detected regions with the 'or' operator
 #
 # See the Tests subsection of the manual for a complete explanation
 # (in the Installing gnuastro section).
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img 1 eq $img 3 eq or -gOBJECTS --output=or.fits
diff --git a/tests/arithmetic/snimage.sh b/tests/arithmetic/snimage.sh
index 1885353..12366fe 100755
--- a/tests/arithmetic/snimage.sh
+++ b/tests/arithmetic/snimage.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -53,7 +53,7 @@ if [ ! -f $imgnc    ]; then echo "$imgnc does not exist."; 
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $imgin $imgnc - $imgnc / --hdu=1 --hdu=SKY  \
diff --git a/tests/arithmetic/where.sh b/tests/arithmetic/where.sh
index 2b9b51f..4ce92f1 100755
--- a/tests/arithmetic/where.sh
+++ b/tests/arithmetic/where.sh
@@ -1,4 +1,4 @@
-# Mask non-detected pixels in the image with the `where' operator.
+# Mask non-detected pixels in the image with the 'where' operator.
 #
 # See the Tests subsection of the manual for a complete explanation
 # (in the Installing gnuastro section).
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img $img 0 eq nan where -h1 -h2 
--output=where.fits
diff --git a/tests/buildprog/simpleio.c b/tests/buildprog/simpleio.c
index cff53f3..5586cec 100644
--- a/tests/buildprog/simpleio.c
+++ b/tests/buildprog/simpleio.c
@@ -4,7 +4,7 @@ A test program to multithreaded building using Gnuastro's 
helpers.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
diff --git a/tests/buildprog/simpleio.sh b/tests/buildprog/simpleio.sh
index e9fa953..8ae6374 100755
--- a/tests/buildprog/simpleio.sh
+++ b/tests/buildprog/simpleio.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -53,14 +53,19 @@ if [ ! -f $source   ]; then echo "$source does not exist."; 
exit 77; fi
 # Actual test script
 # ==================
 #
-# We want to use the `libgnuastro.la' corresponding to this install, not
-# the one (that is possibly) installed (hence the use of `--la').
+# We want to use the 'libgnuastro.la' corresponding to this install, not
+# the one (that is possibly) installed (hence the use of '--la').
 #
-# Except for `gnuastro/config.h', all headers are installed in
-# `$topsrc/lib' and `gnuastro/config.h' is in "../lib/"
+# Except for 'gnuastro/config.h', all headers are installed in
+# '$topsrc/lib' and 'gnuastro/config.h' is in "../lib/"
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
+echo "Test Environment"
+echo "----------------"
+echo "CPPFLAGS: $CPPFLAGS"
+echo "LDFLAGS: $LDFLAGS"
+echo "----------------"
 $check_with_program $execname $source $img 1 --la=../lib/libgnuastro.la \
                               -I$topsrc/lib -I../lib/
diff --git a/tests/convertt/blankch.sh b/tests/convertt/blankch.sh
index 8691a3a..f7e1522 100755
--- a/tests/convertt/blankch.sh
+++ b/tests/convertt/blankch.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname blank $img blank --output=blankch.jpg
diff --git a/tests/convertt/fitstojpeg.sh b/tests/convertt/fitstojpeg.sh
index a4fe2ff..8277c71 100755
--- a/tests/convertt/fitstojpeg.sh
+++ b/tests/convertt/fitstojpeg.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --invert --output=jpg
diff --git a/tests/convertt/fitstojpegcmyk.sh b/tests/convertt/fitstojpegcmyk.sh
index 8e092fb..3c326d8 100755
--- a/tests/convertt/fitstojpegcmyk.sh
+++ b/tests/convertt/fitstojpegcmyk.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -54,7 +54,7 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname blank blank blank $img --output=f2jcmyk.jpg
diff --git a/tests/convertt/fitstopdf.sh b/tests/convertt/fitstopdf.sh
index 2460fe3..a1f3b7f 100755
--- a/tests/convertt/fitstopdf.sh
+++ b/tests/convertt/fitstopdf.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -57,7 +57,7 @@ fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --output=pdf --invert
diff --git a/tests/convertt/fitstotxt.sh b/tests/convertt/fitstotxt.sh
index 88b0e6c..a17e936 100755
--- a/tests/convertt/fitstotxt.sh
+++ b/tests/convertt/fitstotxt.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -49,7 +49,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --output=psf.txt
diff --git a/tests/convertt/jpegtofits.sh b/tests/convertt/jpegtofits.sh
index becf11e..dccf3e2 100755
--- a/tests/convertt/jpegtofits.sh
+++ b/tests/convertt/jpegtofits.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --output=fits
diff --git a/tests/convertt/jpegtotxt.sh b/tests/convertt/jpegtotxt.sh
index 6ea7cd9..ceb161f 100755
--- a/tests/convertt/jpegtotxt.sh
+++ b/tests/convertt/jpegtotxt.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ "x$haslibjpeg" != "xyes" ];then echo "libjpeg not 
present.";  exit 77;fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --output=jpegtotxt.txt
diff --git a/tests/convolve/frequency.sh b/tests/convolve/frequency.sh
index 442b616..5b5a730 100755
--- a/tests/convolve/frequency.sh
+++ b/tests/convolve/frequency.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -52,7 +52,7 @@ if [ ! -f $psf      ]; then echo "$psf does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --kernel=$psf --domain=frequency \
diff --git a/tests/convolve/spatial.sh b/tests/convolve/spatial.sh
index 61e3f7f..6ce5c54 100755
--- a/tests/convolve/spatial.sh
+++ b/tests/convolve/spatial.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -52,7 +52,7 @@ if [ ! -f $psf      ]; then echo "$psf does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --kernel=$psf --domain=spatial   \
diff --git a/tests/cosmiccal/simpletest.sh b/tests/cosmiccal/simpletest.sh
index 9d0f76b..cbb9aa9 100755
--- a/tests/cosmiccal/simpletest.sh
+++ b/tests/cosmiccal/simpletest.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -46,7 +46,7 @@ if [ ! -f $execname ]; then echo "$execname not created.";    
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname --redshift=2.5
diff --git a/tests/crop/cat.txt b/tests/crop/cat.txt
index 8b606e8..e1efdfa 100644
--- a/tests/crop/cat.txt
+++ b/tests/crop/cat.txt
@@ -4,7 +4,7 @@
 # Column 4: RA_CENTER  [deg,f64]    Right Ascension.
 # Column 5: DEC_CENTER [deg,f64]    Declination.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/crop/imgcat.sh b/tests/crop/imgcat.sh
index 2f3857b..e5c11ac 100755
--- a/tests/crop/imgcat.sh
+++ b/tests/crop/imgcat.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 cat=$topsrc/tests/$prog/cat.txt
diff --git a/tests/crop/imgcenter.sh b/tests/crop/imgcenter.sh
index 766f884..f61bb33 100755
--- a/tests/crop/imgcenter.sh
+++ b/tests/crop/imgcenter.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --center=251,251 --mode=img --width=201 \
diff --git a/tests/crop/imgcenternoblank.sh b/tests/crop/imgcenternoblank.sh
index ead666c..b79cd80 100755
--- a/tests/crop/imgcenternoblank.sh
+++ b/tests/crop/imgcenternoblank.sh
@@ -7,7 +7,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -56,7 +56,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --center=500,500 --noblank --numthreads=1 \
diff --git a/tests/crop/imgpolygon.sh b/tests/crop/imgpolygon.sh
index 88fa320..80beae7 100755
--- a/tests/crop/imgpolygon.sh
+++ b/tests/crop/imgpolygon.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img $cat --mode=img --zeroisnotblank \
diff --git a/tests/crop/imgoutpolygon.sh b/tests/crop/imgpolygonout.sh
similarity index 89%
rename from tests/crop/imgoutpolygon.sh
rename to tests/crop/imgpolygonout.sh
index 3935c20..7823f35 100755
--- a/tests/crop/imgoutpolygon.sh
+++ b/tests/crop/imgpolygonout.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,9 +55,9 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img $cat --mode=img --zeroisnotblank     \
-                              --outpolygon --output=imgoutpolygon.fits  \
+                              --polygonout --output=imgpolygonout.fits  \
                               
--polygon=209,50:436.76,151:475.64,438.2:210.6,454.04:121.4,289.88
diff --git a/tests/crop/section.sh b/tests/crop/section.sh
index af38d79..2e1f102 100755
--- a/tests/crop/section.sh
+++ b/tests/crop/section.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -54,7 +54,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --section=-10:*+10,:250 --mode=img    \
diff --git a/tests/crop/wcscat.sh b/tests/crop/wcscat.sh
index bacdf2b..2f3a34e 100755
--- a/tests/crop/wcscat.sh
+++ b/tests/crop/wcscat.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -56,7 +56,7 @@ done
 # enable multithreaded access to files, the tests pass. It is the
 # users choice to enable this feature.
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 cat=$topsrc/tests/$prog/cat.txt
diff --git a/tests/crop/wcscenter.sh b/tests/crop/wcscenter.sh
index 242cb9b..2777585 100755
--- a/tests/crop/wcscenter.sh
+++ b/tests/crop/wcscenter.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ done
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --center=0.99917157,1.0008283       \
diff --git a/tests/crop/wcspolygon.sh b/tests/crop/wcspolygon.sh
index 85328cb..a3ad436 100755
--- a/tests/crop/wcspolygon.sh
+++ b/tests/crop/wcspolygon.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -52,7 +52,7 @@ done
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --mode=wcs --zeroisnotblank   \
diff --git a/tests/during-dev.sh b/tests/during-dev.sh
index 61a4edb..3d14f4a 100755
--- a/tests/during-dev.sh
+++ b/tests/during-dev.sh
@@ -16,11 +16,11 @@
 #      build directories) so if you need to make lots of temporary test
 #      files, there they won't get mixed up with non-output files.
 #
-# Combined with the `developer-build', this script can be used to greatly
+# Combined with the 'developer-build', this script can be used to greatly
 # simplify the development process. After running that script once, for
 # subsequent builds during your development, you can run this script from
-# the top source directory (by running `./tests/during-dev.sh', or giving
-# this to the `compile' command in Emacs). Note that you have to set the
+# the top source directory (by running './tests/during-dev.sh', or giving
+# this to the 'compile' command in Emacs). Note that you have to set the
 # first few variables (directories, utility name, arguments and options)
 # manually before each major development activity.
 #
@@ -34,14 +34,14 @@
 #     git checkout -- tests/during-dev.sh
 #
 # This file can also be used as a model to write a test for the work you
-# have done (to be checked with `make check'). Just copy and paste an
+# have done (to be checked with 'make check'). Just copy and paste an
 # existing test from the utility and replace the last few lines based on
 # this file.
 #
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2016-2019, Free Software Foundation, Inc.
+# Copyright (C) 2016-2021, 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
@@ -64,9 +64,9 @@
 # ====================
 
 # Set the basic test directories. If you are building over the source
-# directory, then set `builddir' to `./'. If you want the outputs to be in
-# the top source directory, set it to `./'. Since 'build' is the assumed
-# symbolic link in `developer-build', it is also assumed in the version
+# directory, then set 'builddir' to './'. If you want the outputs to be in
+# the top source directory, set it to './'. Since 'build' is the assumed
+# symbolic link in 'developer-build', it is also assumed in the version
 # controlled version of this script. Note, if your directory names have
 # space characters in them, quote the full value
 numjobs=8
@@ -88,6 +88,8 @@ options=
 
 
 
+
+
 # RUN THE PROCEDURES
 # ==================
 
@@ -104,7 +106,7 @@ if [ x"$utilname" = x ]; then echo "utilname is not set."; 
exit 1; fi
 if [ x"$builddir" = x ]; then echo "builddir is not set."; exit 1; fi
 
 
-# Make sure `utilname' doesn't start with `ast' (a common mistake).
+# Make sure 'utilname' doesn't start with 'ast' (a common mistake).
 astprefix="${utilname:0:3}"
 if [ x"$astprefix" = x"ast" ]; then
     echo "'utilname' must not start with 'ast'."; exit 1;
@@ -174,7 +176,7 @@ if make -j$numjobs -C "$builddir"; then
     fi
     cp $cfiles .gnuastro/
 
-    # Append `lastconfig' option to `gnuastro.conf', so the program doesn't
+    # Append 'lastconfig' option to 'gnuastro.conf', so the program doesn't
     # go into the system headers.
     echo ""               >> .gnuastro/gnuastro.conf
     echo " lastconfig 1"  >> .gnuastro/gnuastro.conf
diff --git a/tests/fits/copyhdu.sh b/tests/fits/copyhdu.sh
index 3e67409..c66c35c 100755
--- a/tests/fits/copyhdu.sh
+++ b/tests/fits/copyhdu.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -53,7 +53,7 @@ if [ ! -f $img2     ]; then echo "$img2 does not exist.";  
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img2 --copy="Mock profiles" --output=$img1
diff --git a/tests/fits/delete.sh b/tests/fits/delete.sh
index 7d7292f..3213c73 100755
--- a/tests/fits/delete.sh
+++ b/tests/fits/delete.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --delete=ABSJUNK --delete=ABSJNK2
diff --git a/tests/fits/print.sh b/tests/fits/print.sh
index 86f0c15..0f72ff5 100755
--- a/tests/fits/print.sh
+++ b/tests/fits/print.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like 'Valgrind' or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img
diff --git a/tests/fits/update.sh b/tests/fits/update.sh
index 9c95413..5031d8a 100755
--- a/tests/fits/update.sh
+++ b/tests/fits/update.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --update=ABSJUNK,8.231,"An updated 
value.",s \
diff --git a/tests/fits/write.sh b/tests/fits/write.sh
index c706d09..f511e43 100755
--- a/tests/fits/write.sh
+++ b/tests/fits/write.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 cp $img fitstest.fits
diff --git a/tests/lib/multithread.c b/tests/lib/multithread.c
index 1bb9113..bde39ca 100644
--- a/tests/lib/multithread.c
+++ b/tests/lib/multithread.c
@@ -4,7 +4,7 @@ A test program to multithreaded building using Gnuastro's 
helpers.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2017-2019, Free Software Foundation, Inc.
+Copyright (C) 2017-2021, 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
@@ -37,9 +37,9 @@ struct params
 
 
 /* This is the main worker function which will be called by the different
-   threads. `gal_threads_params' is defined in `gnuastro/threads.h' and
+   threads. 'gal_threads_params' is defined in 'gnuastro/threads.h' and
    contains the pointer to the paramter we want. Note that its input and
-   output must have `void *' types. */
+   output must have 'void *' types. */
 void *
 worker_on_thread(void *in_prm)
 {
@@ -77,8 +77,8 @@ worker_on_thread(void *in_prm)
 /* A simple program to open a FITS image, distributes its pixels between
    different threads and print the value of each pixel and the thread it
    was assigned to, this will test both the opening of a FITS file and also
-   the multi-threaded functions. After running `make check' you can see the
-   outputs in `tests/multithread.log'.
+   the multi-threaded functions. After running 'make check' you can see the
+   outputs in 'tests/multithread.log'.
 
    Please run the following command for an explanation on easily linking
    and compiling C programs that use Gnuastro's libraries (without having
@@ -90,12 +90,15 @@ int
 main(void)
 {
   struct params p;
+  int quietmmap=1;
+  size_t minmapsize=-1;
   char *filename="psf.fits", *hdu="1";
   size_t numthreads=gal_threads_number();
 
 
   /* Read the image into memory as a float32 data type. */
-  p.image=gal_fits_img_read_to_type(filename, hdu, GAL_TYPE_FLOAT32, -1, 1);
+  p.image=gal_fits_img_read_to_type(filename, hdu, GAL_TYPE_FLOAT32,
+                                    minmapsize, quietmmap);
 
 
   /* Print some basic information before the actual contents: */
@@ -114,7 +117,8 @@ main(void)
 
 
   /* Spin-off the threads and do the processing on each thread. */
-  gal_threads_spin_off(worker_on_thread, &p, p.image->size, numthreads);
+  gal_threads_spin_off(worker_on_thread, &p, p.image->size, numthreads,
+                       minmapsize, quietmmap);
 
 
   /* Clean up and return. */
diff --git a/tests/lib/multithread.sh b/tests/lib/multithread.sh
index 87a173e..8c9e08f 100755
--- a/tests/lib/multithread.sh
+++ b/tests/lib/multithread.sh
@@ -7,7 +7,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -50,7 +50,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi;
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname
diff --git a/tests/lib/versioncxx.cpp b/tests/lib/versioncxx.cpp
index caca0cd..0396615 100644
--- a/tests/lib/versioncxx.cpp
+++ b/tests/lib/versioncxx.cpp
@@ -4,7 +4,7 @@ A test program to get and use the version number of Gnuastro 
within C++.
 Original author:
      Mohammad Akhlaghi <mohammad@akhlaghi.org>
 Contributing author(s):
-Copyright (C) 2015-2019, Free Software Foundation, Inc.
+Copyright (C) 2015-2021, 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
diff --git a/tests/lib/versioncxx.sh b/tests/lib/versioncxx.sh
index 84ae738..200b093 100755
--- a/tests/lib/versioncxx.sh
+++ b/tests/lib/versioncxx.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -46,7 +46,7 @@ fi;
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname
diff --git a/tests/match/merged-cols.sh b/tests/match/merged-cols.sh
index 9d136dd..01fd125 100755
--- a/tests/match/merged-cols.sh
+++ b/tests/match/merged-cols.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $execname ]; then echo "$execname not created."; 
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat1 $cat2 --aperture=0.5 --ccol1=2,3   \
diff --git a/tests/match/positions-1.txt b/tests/match/positions-1.txt
index 78e7396..8056d9e 100644
--- a/tests/match/positions-1.txt
+++ b/tests/match/positions-1.txt
@@ -2,7 +2,7 @@
 # Column 2: EFGH
 # Column 3: IJKL
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
diff --git a/tests/match/positions-2.txt b/tests/match/positions-2.txt
index 275dbba2..3b44cfa 100644
--- a/tests/match/positions-2.txt
+++ b/tests/match/positions-2.txt
@@ -2,7 +2,7 @@
 # Column 2: ACCU1
 # Column 3: ACCU2
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
diff --git a/tests/match/positions.sh b/tests/match/positions.sh
index fbe3aa2..dc76677 100755
--- a/tests/match/positions.sh
+++ b/tests/match/positions.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $execname ]; then echo "$execname not created."; 
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat1 $cat2 --aperture=0.5 --log --ccol1=2,3 \
diff --git a/tests/mkcatalog/aperturephot.sh b/tests/mkcatalog/aperturephot.sh
index 948fcc6..5fcca3e 100755
--- a/tests/mkcatalog/aperturephot.sh
+++ b/tests/mkcatalog/aperturephot.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -53,7 +53,7 @@ if [ ! -f $objimg   ]; then echo "$objimg does not exist";  
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $objimg --hdu=1 --valuesfile=$img       \
diff --git a/tests/mkcatalog/detections.sh b/tests/mkcatalog/detections.sh
index e95eb67..a4e3e94 100755
--- a/tests/mkcatalog/detections.sh
+++ b/tests/mkcatalog/detections.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -53,7 +53,7 @@ if [ ! -f $base     ]; then echo "$base does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $labels -h1 --valuesfile=$base            \
diff --git a/tests/mkcatalog/objects-clumps.sh 
b/tests/mkcatalog/objects-clumps.sh
index 16849d3..452ebc6 100755
--- a/tests/mkcatalog/objects-clumps.sh
+++ b/tests/mkcatalog/objects-clumps.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like 'Valgrind' or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --x --y --ra --dec --magnitude     \
diff --git a/tests/mknoise/addnoise-3d.sh b/tests/mknoise/addnoise-3d.sh
index ec010f4..2a48601 100755
--- a/tests/mknoise/addnoise-3d.sh
+++ b/tests/mknoise/addnoise-3d.sh
@@ -56,4 +56,5 @@ if [ ! -f $img      ]; then echo "$img does not exist.";     
exit 77; fi
 # ==================
 export GSL_RNG_SEED=1
 export GSL_RNG_TYPE=ranlxs2
-$execname --envseed $img
+options="--background=-10 --zeropoint=0 --envseed"
+$check_with_program $execname $img $options
diff --git a/tests/mknoise/addnoise.sh b/tests/mknoise/addnoise.sh
index d1634c0..75c5d72 100755
--- a/tests/mknoise/addnoise.sh
+++ b/tests/mknoise/addnoise.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -58,10 +58,11 @@ if [ ! -f $img2     ]; then echo "$img2 does not exist.";   
 exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 export GSL_RNG_SEED=1
 export GSL_RNG_TYPE=ranlxs2
-$check_with_program $execname --envseed $img1
-$check_with_program $execname --envseed $img2
+options="--background=-10 --zeropoint=0 --envseed"
+$check_with_program $execname $img1 $options
+$check_with_program $execname $img2 $options
diff --git a/tests/mkprof/clearcanvas.sh b/tests/mkprof/clearcanvas.sh
index f6c8e9f..d221eb9 100755
--- a/tests/mkprof/clearcanvas.sh
+++ b/tests/mkprof/clearcanvas.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat --background=$img --mforflatpix \
diff --git a/tests/mkprof/clearcanvas.txt b/tests/mkprof/clearcanvas.txt
index d4259fe..8812a14 100644
--- a/tests/mkprof/clearcanvas.txt
+++ b/tests/mkprof/clearcanvas.txt
@@ -10,10 +10,10 @@
 # Column 10: Truncation radius [dist, f64]   Truncation radius to stop 
building profile
 #
 # Note that the positions and radii are multiplied by 5 compared to
-# `mkprofcat1.txt', because we are using the over-sampled image as a
+# 'mkprofcat1.txt', because we are using the over-sampled image as a
 # canvas.
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/mkprof/ellipticalmasks.sh b/tests/mkprof/ellipticalmasks.sh
index 0899020..10941a8 100755
--- a/tests/mkprof/ellipticalmasks.sh
+++ b/tests/mkprof/ellipticalmasks.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat --background=$img --mforflatpix --replace \
diff --git a/tests/mkprof/ellipticalmasks.txt b/tests/mkprof/ellipticalmasks.txt
index 3db0172..ce6b04b 100644
--- a/tests/mkprof/ellipticalmasks.txt
+++ b/tests/mkprof/ellipticalmasks.txt
@@ -9,7 +9,7 @@
 # Column 9:  Magnitude         [ABmag, f64]  Magnitude of profile within 
truncation radius
 # Column 10: Truncation radius [dist, f64]   Truncation radius to stop 
building profile
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/mkprof/mkprofcat1.txt b/tests/mkprof/mkprofcat1.txt
index b291d32..4d3c77c 100644
--- a/tests/mkprof/mkprofcat1.txt
+++ b/tests/mkprof/mkprofcat1.txt
@@ -9,7 +9,7 @@
 # Column 9:  Magnitude         [ABmag, f64]  Magnitude of profile within 
truncation radius
 # Column 10: Truncation radius [dist, f64]   Truncation radius to stop 
building profile
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/mkprof/mkprofcat2.txt b/tests/mkprof/mkprofcat2.txt
index 9ce2b83..fdebbd3 100644
--- a/tests/mkprof/mkprofcat2.txt
+++ b/tests/mkprof/mkprofcat2.txt
@@ -9,7 +9,7 @@
 # Column 9:  Magnitude         [ABmag, f64]  Magnitude of profile within 
truncation radius
 # Column 10: Truncation radius [dist, f64]   Truncation radius to stop 
building profile
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/mkprof/mkprofcat3.txt b/tests/mkprof/mkprofcat3.txt
index dba7d4d..e7b5fbc 100644
--- a/tests/mkprof/mkprofcat3.txt
+++ b/tests/mkprof/mkprofcat3.txt
@@ -9,7 +9,7 @@
 # Column 9:  Magnitude         [ABmag, f64]  Magnitude of profile within 
truncation radius
 # Column 10: Truncation radius [dist, f64]   Truncation radius to stop 
building profile
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/mkprof/mkprofcat4.txt b/tests/mkprof/mkprofcat4.txt
index a38cbe1..f14dc98 100644
--- a/tests/mkprof/mkprofcat4.txt
+++ b/tests/mkprof/mkprofcat4.txt
@@ -9,7 +9,7 @@
 # Column 9:  Magnitude         [ABmag, f64]  Magnitude of profile within 
truncation radius
 # Column 10: Truncation radius [dist, f64]   Truncation radius to stop 
building profile
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/mkprof/mosaic1.sh b/tests/mkprof/mosaic1.sh
index db3f346..27817df 100755
--- a/tests/mkprof/mosaic1.sh
+++ b/tests/mkprof/mosaic1.sh
@@ -11,7 +11,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -52,7 +52,7 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat --mergedsize=100,100      \
diff --git a/tests/mkprof/mosaic2.sh b/tests/mkprof/mosaic2.sh
index 1a263f5..9be5228 100755
--- a/tests/mkprof/mosaic2.sh
+++ b/tests/mkprof/mosaic2.sh
@@ -13,7 +13,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -57,7 +57,7 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat --mergedsize=100,100 --crpix=-99,1 \
diff --git a/tests/mkprof/mosaic3.sh b/tests/mkprof/mosaic3.sh
index f3b7665..9984579 100755
--- a/tests/mkprof/mosaic3.sh
+++ b/tests/mkprof/mosaic3.sh
@@ -11,7 +11,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -54,7 +54,7 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat --mergedsize=100,100 --crpix=1,-99
diff --git a/tests/mkprof/mosaic4.sh b/tests/mkprof/mosaic4.sh
index 8877e99..7bf48c9 100755
--- a/tests/mkprof/mosaic4.sh
+++ b/tests/mkprof/mosaic4.sh
@@ -11,7 +11,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -55,7 +55,7 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat --mergedsize=100,100 --crpix=-99,-99
diff --git a/tests/mkprof/radeccat.sh b/tests/mkprof/radeccat.sh
index a3c83f7..cc8b701 100755
--- a/tests/mkprof/radeccat.sh
+++ b/tests/mkprof/radeccat.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -50,7 +50,7 @@ if [ ! -f $cat      ]; then echo "$cat does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $cat --ccol=RA --ccol=Dec --mode=wcs   \
diff --git a/tests/mkprof/radeccat.txt b/tests/mkprof/radeccat.txt
index f318f1a..ffc7c47 100644
--- a/tests/mkprof/radeccat.txt
+++ b/tests/mkprof/radeccat.txt
@@ -9,7 +9,7 @@
 # Column 9:  Magnitude         [ABmag, f64]   Magnitude of profile within 
truncation radius
 # Column 10: Truncation radius [dist,  f64]    Truncation radius to stop 
building profile
 #
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/noisechisel/noisechisel.sh b/tests/noisechisel/noisechisel.sh
index 88ffede..ab6ef0d 100755
--- a/tests/noisechisel/noisechisel.sh
+++ b/tests/noisechisel/noisechisel.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,9 +51,9 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
-$check_with_program $execname $img --tilesize=100,100 --snquant=0.999 \
+$check_with_program $execname $img --detgrowquant=0.7 \
                               --cleangrowndet --checkdetection        \
                               --continueaftercheck
diff --git a/tests/prepconf.sh b/tests/prepconf.sh
index 1cb8640..36421e3 100755
--- a/tests/prepconf.sh
+++ b/tests/prepconf.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2016-2019, Free Software Foundation, Inc.
+# Copyright (C) 2016-2021, 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
@@ -28,10 +28,10 @@
 # ----------------------------
 #
 # This directory will keep the default configuration files for all the
-# programs. If it already exists, delete it. `mkdir_p' is the equivalent to
-# GNU's `mkdir -p' which might not exist on some systems. It comes from
+# programs. If it already exists, delete it. 'mkdir_p' is the equivalent to
+# GNU's 'mkdir -p' which might not exist on some systems. It comes from
 # Autoconf's tests and is exported to the test shell scripts from the
-# `tests/Makefile.am' file.
+# 'tests/Makefile.am' file.
 $mkdir_p .gnuastro
 
 
@@ -44,7 +44,7 @@ $mkdir_p .gnuastro
 # Copy the common options while adding the following optios only for make
 # check.
 #
-#   - `lastconfig' will make sure that the program stop searching for
+#   - 'lastconfig' will make sure that the program stop searching for
 #     configuration files after this one.
 #
 #   - Log files are not necessary during tests, they are mainly used for
@@ -68,7 +68,7 @@ rm addedoptions.txt
 # Bring utility configuration files
 # ---------------------------------
 #
-# Each utility's configuration file is copied in the `tests' directory for
+# Each utility's configuration file is copied in the 'tests' directory for
 # easy readability. Note that some programs may need to build their
 # configuration files during compilation. Hence, their configuration files
 # are in the build directory, not the source directory.
diff --git a/tests/script/list-by-night.sh b/tests/script/list-by-night.sh
index 6825a0f..4871eeb 100755
--- a/tests/script/list-by-night.sh
+++ b/tests/script/list-by-night.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -30,7 +30,9 @@
 # tested on a larger image.
 prog=sort-by-night
 dep1=fits
+dep2=table
 dep1name=../bin/$dep1/ast$dep1
+dep2name=../bin/$dep2/ast$dep2
 execname=../bin/script/astscript-$prog
 
 
@@ -47,6 +49,7 @@ execname=../bin/script/astscript-$prog
 #   - The programs it use weren't made.
 if [ ! -f $execname ]; then echo "$execname doesn't exist."; exit 77; fi
 if [ ! -f $dep1name ]; then echo "$dep1name doesn't exist."; exit 77; fi
+if [ ! -f $dep2name ]; then echo "$dep2name doesn't exist."; exit 77; fi
 
 
 
@@ -55,13 +58,16 @@ if [ ! -f $dep1name ]; then echo "$dep1name doesn't 
exist."; exit 77; fi
 # Put a link of Gnuastro program(s) used into current directory. Note that
 # other script tests may have already brought it.
 ln -sf $dep1name ast$dep1
+ln -sf $dep2name ast$dep2
+
+
 
 
 
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 #
diff --git a/tests/segment/segment.sh b/tests/segment/segment.sh
index d995f35..a05f6ad 100755
--- a/tests/segment/segment.sh
+++ b/tests/segment/segment.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --tilesize=100,100 --snquant=0.99
diff --git a/tests/statistics/basicstats.sh b/tests/statistics/basicstats.sh
index a412360..dfb0988 100755
--- a/tests/statistics/basicstats.sh
+++ b/tests/statistics/basicstats.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img -g9500 -l11000 --numasciibins=65
diff --git a/tests/statistics/estimate_sky.sh b/tests/statistics/estimate_sky.sh
index e7b2fa9..fac1727 100755
--- a/tests/statistics/estimate_sky.sh
+++ b/tests/statistics/estimate_sky.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -56,7 +56,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # tessellation, estimation, interpolation and smoothing go nicely without
 # any errors.
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --sky --checksky
diff --git a/tests/statistics/from-stdin.sh b/tests/statistics/from-stdin.sh
index e3cb588..2f43cb9 100755
--- a/tests/statistics/from-stdin.sh
+++ b/tests/statistics/from-stdin.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $in       ]; then echo "$in does not exist.";    
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 cat $in | $check_with_program $execname
diff --git a/tests/statistics/stdin-input.txt b/tests/statistics/stdin-input.txt
index 36847f6..2b45371 100644
--- a/tests/statistics/stdin-input.txt
+++ b/tests/statistics/stdin-input.txt
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021 Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/table/fits-ascii-to-txt.sh b/tests/table/fits-ascii-to-txt.sh
index 2d2e3e6..99ddffe 100755
--- a/tests/table/fits-ascii-to-txt.sh
+++ b/tests/table/fits-ascii-to-txt.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $table    ]; then echo "$table doesn't exist.";  
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $table --output=from-ascii-table.txt
diff --git a/tests/table/fits-binary-to-txt.sh 
b/tests/table/fits-binary-to-txt.sh
index 09cbe46..8497bc2 100755
--- a/tests/table/fits-binary-to-txt.sh
+++ b/tests/table/fits-binary-to-txt.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $table    ]; then echo "$table doesn't exist.";  
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like 'Valgrind' or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $table --output=from-binary-table.txt
diff --git a/tests/table/table.txt b/tests/table/table.txt
index 66daabc..305f774 100644
--- a/tests/table/table.txt
+++ b/tests/table/table.txt
@@ -27,10 +27,10 @@
 # 412 (data type conversion overflow) when the number cannot be
 # printed in the provided space (with full precision). So make sure
 # that the full-integer part of the number has less characters than
-# the `width - precision - 1' (when everything is default, you can get
-# these values from `lib/gnuastro/table.h').
+# the 'width - precision - 1' (when everything is default, you can get
+# these values from 'lib/gnuastro/table.h').
 
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification, are
 # permitted in any medium without royalty provided the copyright notice and
diff --git a/tests/table/txt-to-fits-ascii.sh b/tests/table/txt-to-fits-ascii.sh
index 8ca4501..04a5420 100755
--- a/tests/table/txt-to-fits-ascii.sh
+++ b/tests/table/txt-to-fits-ascii.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $table    ]; then echo "$table doesn't exist.";  
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $table --output=ascii-table.fits   \
diff --git a/tests/table/txt-to-fits-binary.sh 
b/tests/table/txt-to-fits-binary.sh
index 69b5051..3808e06 100755
--- a/tests/table/txt-to-fits-binary.sh
+++ b/tests/table/txt-to-fits-binary.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $table    ]; then echo "$table does not exist."; 
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $table --output=binary-table.fits \
diff --git a/tests/warp/homographic.sh b/tests/warp/homographic.sh
index c350aa2..f11f9a8 100755
--- a/tests/warp/homographic.sh
+++ b/tests/warp/homographic.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --output=homographic.fits --coveredfrac=0.5 
\
diff --git a/tests/warp/warp_scale.sh b/tests/warp/warp_scale.sh
index e0a3b0d..22a3c85 100755
--- a/tests/warp/warp_scale.sh
+++ b/tests/warp/warp_scale.sh
@@ -6,7 +6,7 @@
 # Original author:
 #     Mohammad Akhlaghi <mohammad@akhlaghi.org>
 # Contributing author(s):
-# Copyright (C) 2015-2019 Free Software Foundation, Inc.
+# Copyright (C) 2015-2021, Free Software Foundation, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -51,7 +51,7 @@ if [ ! -f $img      ]; then echo "$img does not exist.";   
exit 77; fi
 # Actual test script
 # ==================
 #
-# `check_with_program' can be something like `Valgrind' or an empty
+# 'check_with_program' can be something like Valgrind or an empty
 # string. Such programs will execute the command if present and help in
 # debugging when the developer doesn't have access to the user's system.
 $check_with_program $execname $img --scale=1/5 --centeroncorner



reply via email to

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