gnuastro-commits
[Top][All Lists]
Advanced

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

[gnuastro-commits] master 68823e3 17/19: Library (fits.h): correctly rea


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 68823e3 17/19: Library (fits.h): correctly reading columns of 0 width/repeat
Date: Sun, 14 Nov 2021 20:41:01 -0500 (EST)

branch: master
commit 68823e3fcdda63eac27d05f7cb0f399dfeeff2da
Author: Mohammad Akhlaghi <mohammad@akhlaghi.org>
Commit: Mohammad Akhlaghi <mohammad@akhlaghi.org>

    Library (fits.h): correctly reading columns of 0 width/repeat
    
    Until now, if a column of a FITS binary table had a 0 width or "repeat"
    value (in the TFORM keyword, for example '0A' in for a 0-repeat string
    column), Gnuastro would crash with the following CFITSIO error:
    
       FITSIO status = 308: bad first element number
       First element to write is too large: 1; max allowed value is 0
    
    This was because until now, we were assuming that a column always has
    atleast one element!
    
    With this commit, upon reading the table's metadata (and discovering that
    it has a "repeat" value of 0), a bit flag is set so when we actually want
    to read the column, in such cases, it will simply read an all-blank set of
    values (which would be the equivalent value in Gnuastro's internal data
    structure, for now).
    
    In the process the following changes are also made:
    
     - Until now, there was only a single column metadata flag, so we had used
       an 'enum' and '==' to check for it. But with this commit, we need more
       than one flag, so the flags are now bit flags and all relevant checks
       have been corrected.
    
     - This whole process happened while I was editing the Matching part of the
       Gnuastro book, so those edits are also being committed (they are just
       minor copy-edits, and not worth a separate commit!).
---
 NEWS                                |   4 +-
 bin/match/ui.c                      |   2 +
 doc/gnuastro.texi                   |  62 ++++++++++----------
 lib/blank.c                         |  17 +++++-
 lib/fits.c                          | 113 ++++++++++++++++++++++--------------
 lib/gnuastro-internal/tableintern.h |  17 ++++--
 lib/table.c                         |  38 ++++++++----
 lib/tableintern.c                   |   2 +-
 lib/txt.c                           |   5 +-
 9 files changed, 167 insertions(+), 93 deletions(-)

diff --git a/NEWS b/NEWS
index 89681c1..3df4fcd 100644
--- a/NEWS
+++ b/NEWS
@@ -38,10 +38,12 @@ See the end of the file for license conditions.
      and was raised by Sepideh Eskandarlou.
 
   Library:
-
    - gal_fits_tab_read: now takes the number of threads (only relevant for
      reading FITS tables).
    - gal_table_read: simlar to 'gal_fits_tab_read'.
+   - gal_blank_initialize: also works on string data, but initializing only
+     a tile over a larger block of elements is only supported for numeric
+     data types.
 
 ** Bugs fixed
   bug #61329: make check crash in macOS in convolve/spectrum-1d.sh, found
diff --git a/bin/match/ui.c b/bin/match/ui.c
index 0aefc96..51ef3e2 100644
--- a/bin/match/ui.c
+++ b/bin/match/ui.c
@@ -1272,8 +1272,10 @@ ui_free_report(struct matchparams *p, struct timeval *t1)
   gal_data_free(p->outcols);
   gal_list_data_free(p->cols1);
   gal_list_data_free(p->cols2);
+  if(p->kdtree) free(p->kdtree);
   gal_list_str_free(p->acols, 0);
   gal_list_str_free(p->bcols, 0);
+  if(p->kdtreehdu) free(p->kdtreehdu);
   gal_list_str_free(p->stdinlines, 1);
 
   /* Print the final message. */
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index bf670da..d6dfe28 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -19961,12 +19961,12 @@ The aperture can be a circle or an ellipse with any 
orientation.
 
 Matching involves two catalogs, let's call them catalog A (with N rows) and 
catalog B (with M rows).
 The most basic algorithm that immediately comes to mind is this:
-for each row in A (let's call it @mymath{A_i}), go over all the rows in B and 
calculate the distance to each and find the @mymath{B_j} that is nearest to 
@mymath{A_i}.
-If the distance between @mymath{A_i} and @mymath{B_j} is below a certain 
acceptable threshold (or radius), consider them as a match.
+for each row in A (let's call it @mymath{A_i}), go over all the rows in B 
(@mymath{B_j}, where @mymath{0<j<M}) and calculate the distance 
@mymath{|B_j-A_i|}.
+If this distance is less than a certain acceptable distance threshold (or 
radius), consider @mymath{A_i} and @mymath{B_j} as a match.
 
-You may think of this (initially genius!) solution: During the parsing, when I 
find that @mymath{A_i} and @mymath{B_j} satisfy the match above, I will remove 
@mymath{B_j} from the search of matches for @mymath{A_k} (where @mymath{k> i}).
-As a result, as I go down A and more matches are found, I have to calculate 
less distances (there are fewer elements in B that remain to be checked).
-However, this will introduce an important bias: @mymath{B_j} may actually be 
closer to @mymath{A_k}!
+During the parsing, once you have found that @mymath{A_i} and @mymath{B_j} 
satisfy the match above, you can even remove @mymath{B_j} from the search of 
matches for @mymath{A_k} (where @mymath{k>i}).
+Therefore, as you go down A (and more matches are found), you have to 
calculate less distances (there are fewer elements in B that remain to bex 
checked).
+However, this will introduce an important bias: @mymath{B_j} may actually be 
closer to @mymath{A_k} than to the matched @mymath{A_i}!
 But because @mymath{A_i} happened to be before @mymath{A_k} in your table, you 
removed @mymath{B_j} from the potential search domain of @mymath{A_k}.
 You will therefore loose that match, and replace it by a false match between 
@mymath{A_i} and @mymath{B_j}!
 
@@ -19976,10 +19976,10 @@ Here is how we adress this problem in Gnuastro's 
Match program: as we are doing
 Afterwards, for each @mymath{B_j} we can find the nearest element of A, 
irrespective of how the inputs were sorted, or their dimensionality.
 
 On the other hand, the basic parsing algorithm mentioned above is very 
computationally expensive:
-@mymath{N\times M} distances have to measured, and calculating the distance 
requires a square root and power of 2, which are not simple operations for the 
CPU.
+@mymath{N\times M} distances have to measured, and calculating the distance 
requires a square root and power of 2 (in 2 dimensions it would be 
@mymath{\sqrt{(B_{ix}-A_{ix})^2+(B_{iy}-A_{iy})^2}}), which are not simple 
operations for the CPU.
 As a result, this basic algorithm will become terribly slow as your datasets 
grow in size.
 For example when N or M exceed hundreds of thousands (which is common in the 
current days with datasets like the European Space Agency's Gaia mission).
-Therefore that basic parsing algoritm won't work and we need to use more 
efficient ways to parse the two catalogs.
+Therefore that basic parsing algoritm will take too much time and we need to 
use more efficient ways to parse the two catalogs.
 Gnuastro's Match currently has two such algorithms:
 
 @table @asis
@@ -19989,21 +19989,30 @@ To avoid the problem of calculating @mymath{N\times 
M} Euclidean distances (that
 @enumerate
 @item
 Sort the two datasets by their first coordinate.
-Therefore @mymath{A_i<A_j} (only in first coordinate; when @mymath{i<j}), and 
similarly for the elements of B.
+Therefore @mymath{A_i<A_j} (only in first coordinate; when @mymath{i<j}), and 
similarly, sort the elements of B based on the first coordinate.
 @item
-Use the radial distance threshold to define a moving window of B over A.
-Therefore, within a single parsing of A, you can find all the elements in A 
that are sufficiently near every @mymath{B_j}.
+Use the radial distance threshold to define the width of a moving interval 
over both A and B.
+Therefore, with a single parsing of A, you can find all the elements in A that 
are sufficiently near every element of B (while rejecting the ones that are too 
distant when taking other dimensions into account).
 @item
 The nearest A within the subset that is nearest to @mymath{B_j} will be 
considered as the match.
 @end enumerate
 
+This method has some caveats:
+1) It requires sorting, which can again be slow.
+2) It can only be done on a single CPU thread! So it can't benefit from the 
modern CPUs with many threads.
+3) There is no way to preserve intermediate information for future matches, 
for example this can greatly help when one of the matched datasets is always 
the same.
+
 @item k-d tree based
-The k-d tree concept is much more abstract, but powerful (for example enabling 
parallel processing, that was not possible in the sort-based method above).
+The k-d tree concept is much more abstract, but powerful (addressing all the 
caveats of the sort-based method).
 In short a k-d tree is a partitioning of a k-dimentional space.
+The k-d tree of table A is another table with the same number of rows, but 
only two integer columns: the integers contain the row indexs (counting from 
zero) of the left and right branch of that row.
 For more on the k-d tree and Gnuastro's implementation of it, please see 
@ref{K-d tree}.
 
-To avoid going too deep into theory, let's look at it from a user's 
perspective: the k-d tree of table A is another table with the same number of 
rows, but only two integer columns (the integers contain the row indexs of the 
left and right branch of that row).
-Match will internally construct a k-d tree for catalog A (the first catalog 
given to it) and use it for matching with catalog B.
+When given two catalogs (like the command below), Gnuastro's Match will 
internally construct a k-d tree for catalog A (the first catalog given to it) 
and use it for matching with catalog B.
+@example
+$ astmatch A.fits --ccol1=ra,dec B.fits --ccol2=ra,dec \
+           --aperture=1/3600
+@end example
 However, optionally, you can also build the k-d tree of A and save it into a 
file, with a separate call to Match (with @option{--kdtree=build} and 
@option{--output=mykdtree.fits}).
 This external k-d tree can be fed to Match later (to avoid having to 
reconstruct it every time you want to match with the same first catalog; using 
@option{--kdtree=mykdtree.fits}).
 
@@ -24452,35 +24461,28 @@ Blank value for string types (this is itself a 
string, it isn't the
 The functions below can be used to work with blank pixels.
 
 @deftypefun void gal_blank_write (void @code{*pointer}, uint8_t @code{type})
-Write the blank value for the given @code{type} into the space that
-@code{pointer} points to. This can be used when the space is already
-allocated (for example one element in an array or a statically allocated
-variable).
+Write the blank value for the given @code{type} into the space that 
@code{pointer} points to.
+This can be used when the space is already allocated (for example one element 
in an array or a statically allocated variable).
 @end deftypefun
 
 @deftypefun {void *} gal_blank_alloc_write (uint8_t @code{type})
-Allocate the space required to keep the blank for the given data type
-@code{type}, write the blank value into it and return the pointer to it.
+Allocate the space required to keep the blank for the given data type 
@code{type}, write the blank value into it and return the pointer to it.
 @end deftypefun
 
 @deftypefun void gal_blank_initialize (gal_data_t @code{*input})
-Initialize all the elements in the @code{input} dataset to the blank value
-that corresponds to its type. If @code{input} is a tile over a larger
-dataset, only the region that the tile covers will be set to blank.
+Initialize all the elements in the @code{input} dataset to the blank value 
that corresponds to its type.
+If @code{input} isn't a string, and is a tile over a larger dataset, only the 
region that the tile covers will be set to blank.
+For strings, the full dataset will be initialized.
 @end deftypefun
 
 @deftypefun void gal_blank_initialize_array (void @code{*array}, size_t 
@code{size}, uint8_t @code{type})
-Initialize all the elements in the @code{array} to the blank value that
-corresponds to its type (identified with @code{type}), assuming the array
-has @code{size} elements.
+Initialize all the elements in the @code{array} to the blank value that 
corresponds to its type (identified with @code{type}), assuming the array has 
@code{size} elements.
 @end deftypefun
 
 @deftypefun {char *} gal_blank_as_string (uint8_t @code{type}, int 
@code{width})
-Write the blank value for the given data type @code{type} into a string and
-return it. The space for the string is dynamically allocated so it must be
-freed after you are done with it. If @code{width!=0}, then the final string
-will be padded with white space characters to have the requested width if
-it is smaller.
+Write the blank value for the given data type @code{type} into a string and 
return it.
+The space for the string is dynamically allocated so it must be freed after 
you are done with it.
+If @code{width!=0}, then the final string will be padded with white space 
characters to have the requested width if it is smaller.
 @end deftypefun
 
 @deftypefun int gal_blank_is (void @code{*pointer}, uint8_t @code{type})
diff --git a/lib/blank.c b/lib/blank.c
index dda0e91..5e33f97 100644
--- a/lib/blank.c
+++ b/lib/blank.c
@@ -107,7 +107,22 @@ gal_blank_alloc_write(uint8_t type)
 void
 gal_blank_initialize(gal_data_t *input)
 {
-  GAL_TILE_PARSE_OPERATE(input, NULL, 0, 0, {*i=b;});
+  size_t i;
+  char **strarr;
+
+  /* For strings, we will initialize the full array, for numerical data
+     types we will consider tiles. */
+  if(input->type==GAL_TYPE_STRING)
+    {
+      strarr=input->array;
+      for(i=0;i<input->size;++i)
+        {
+          if(strarr[i]) free(strarr[i]);
+          gal_checkset_allocate_copy(GAL_BLANK_STRING, &strarr[i]);
+        }
+    }
+  else
+    {GAL_TILE_PARSE_OPERATE(input, NULL, 0, 0, {*i=b;});}
 }
 
 
diff --git a/lib/fits.c b/lib/fits.c
index d61fc9d..bdedfde 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -3091,6 +3091,13 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
               else
                 fits_binary_tform(value, &datatype, &repeat, NULL, &status);
 
+              /* Write the "repeat" element into 'allcols->minmapsize', but
+                 also activate the repeat flag within the dataset.*/
+              allcols[index].minmapsize = repeat;
+              if(repeat==0)
+                allcols[index].flag
+                  |= GAL_TABLEINTERN_FLAG_TFORM_REPEAT_IS_ZERO;
+
               /* Write the type into the data structure. */
               allcols[index].type=gal_fits_datatype_to_type(datatype, 1);
 
@@ -3108,7 +3115,14 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
                               "standard in %s", filename, hdu, keyname, value,
                               __func__);
                     }
-                  allcols[index].disp_width=repeat;
+
+                  /* TFORM's 'repeat' element can be zero (signifying a
+                     column without any data)! In this case, we want the
+                     output to be fully blank, so we need space for the
+                     blank string. */
+                  allcols[index].disp_width = ( repeat
+                                                ? repeat
+                                                : strlen(GAL_BLANK_STRING)+1);
                 }
             }
         }
@@ -3191,9 +3205,10 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
 
                   /* This flag is not relevant for FITS tables. */
                   if(allcols[index].flag
-                     ==GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING)
+                     & GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING)
                     {
-                      allcols[index].flag=0;
+                      allcols[index].flag
+                        &= ~GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING;
                       free(allcols[index].array);
                     }
                 }
@@ -3230,9 +3245,10 @@ gal_fits_tab_info(char *filename, char *hdu, size_t 
*numcols,
 
               /* This flag is not relevant for FITS tables. */
               if(allcols[tmp_i->v].flag
-                 ==GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING)
+                 & GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING)
                 {
-                  allcols[tmp_i->v].flag=0;
+                  allcols[tmp_i->v].flag
+                    &= ~GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING;
                   free(allcols[tmp_i->v].array);
                 }
             }
@@ -3402,45 +3418,58 @@ fits_tab_read_onecol(void *in_prm)
             }
         }
 
-      /* Allocate a blank value for the given type and read/store the
-         column using CFITSIO. Note that for binary tables, we only need
-         blank values for integer types. For binary floating point types,
-         the FITS standard defines blanks as NaN (same as almost any other
-         software like Gnuastro). However if a blank value is specified,
-         CFITSIO will convert other special numbers like 'inf' to NaN
-         also. We want to be able to distringuish 'inf' and NaN here, so
-         for floating point types in binary tables, we won't define any
-         blank value. In ASCII tables, CFITSIO doesn't read the 'NAN'
-         values (that it has written itself) unless we specify a blank
-         pointer/value. */
-      isfloat = ( col->type==GAL_TYPE_FLOAT32
-                  || col->type==GAL_TYPE_FLOAT64 );
-      blank = ( ( hdutype==BINARY_TBL && isfloat )
-                ? NULL
-                : gal_blank_alloc_write(col->type) );
-      fits_read_col(fptr, gal_fits_type_to_datatype(col->type), indin+1,
-                    1, 1, col->size, blank, col->array, &anynul, &status);
-
-      /* In the ASCII table format, CFITSIO might not be able to read 'INF'
-         or '-INF'. In this case, it will set status to 'BAD_C2D' or
-         'BAD_C2F'. So, we'll use our own parser for the column values. */
-      if( hdutype==ASCII_TBL
-          && isfloat
-          && (status==BAD_C2D || status==BAD_C2F) )
+      /* If this column has a 'repeat' of zero, then just set all its
+         elements to its relevant blank type and don't call CFITSIO (there
+         is nothing for it to read, and it will crash with "FITSIO status =
+         308: bad first element number First element to write is too large:
+         1; max allowed value is 0"). */
+      if(p->allcols[indin].flag & GAL_TABLEINTERN_FLAG_TFORM_REPEAT_IS_ZERO)
+        gal_blank_initialize(col);
+
+      /* The column has non-zero width, read its contents. */
+      else
         {
-          fits_tab_read_ascii_float_special(p->filename, p->hdu,
-                                            fptr, col, indin+1, p->numrows,
-                                            p->minmapsize, p->quietmmap);
-          status=0;
+          /* Allocate a blank value for the given type and read/store the
+             column using CFITSIO. Note that for binary tables, we only
+             need blank values for integer types. For binary floating point
+             types, the FITS standard defines blanks as NaN (same as almost
+             any other software like Gnuastro). However if a blank value is
+             specified, CFITSIO will convert other special numbers like
+             'inf' to NaN also. We want to be able to distringuish 'inf'
+             and NaN here, so for floating point types in binary tables, we
+             won't define any blank value. In ASCII tables, CFITSIO doesn't
+             read the 'NAN' values (that it has written itself) unless we
+             specify a blank pointer/value. */
+          isfloat = ( col->type==GAL_TYPE_FLOAT32
+                      || col->type==GAL_TYPE_FLOAT64 );
+          blank = ( ( hdutype==BINARY_TBL && isfloat )
+                    ? NULL
+                    : gal_blank_alloc_write(col->type) );
+          fits_read_col(fptr, gal_fits_type_to_datatype(col->type), indin+1,
+                        1, 1, col->size, blank, col->array, &anynul, &status);
+
+          /* In the ASCII table format, CFITSIO might not be able to read
+             'INF' or '-INF'. In this case, it will set status to 'BAD_C2D'
+             or 'BAD_C2F'. So, we'll use our own parser for the column
+             values. */
+          if( hdutype==ASCII_TBL
+              && isfloat
+              && (status==BAD_C2D || status==BAD_C2F) )
+            {
+              fits_tab_read_ascii_float_special(p->filename, p->hdu,
+                                                fptr, col, indin+1, p->numrows,
+                                                p->minmapsize, p->quietmmap);
+              status=0;
+            }
+          gal_fits_io_error(status, NULL); /* After the 'status' correction. */
+
+          /* Clean up and sanity check (just note that the blank value for
+             strings, is an array of strings, so we need to free the
+             contents before freeing itself). */
+          if(col->type==GAL_TYPE_STRING)
+            {strarr=blank; free(strarr[0]);}
+          if(blank) free(blank);
         }
-      gal_fits_io_error(status, NULL); /* After the 'status' correction. */
-
-      /* Clean up and sanity check (just note that the blank value for
-         strings, is an array of strings, so we need to free the contents
-         before freeing itself). */
-      if(col->type==GAL_TYPE_STRING)
-        {strarr=blank; free(strarr[0]);}
-      if(blank) free(blank);
 
       /* Everything is fine, put this column in the output array. */
       p->colarray[indout]=col;
diff --git a/lib/gnuastro-internal/tableintern.h 
b/lib/gnuastro-internal/tableintern.h
index 555282d..8e05531 100644
--- a/lib/gnuastro-internal/tableintern.h
+++ b/lib/gnuastro-internal/tableintern.h
@@ -52,12 +52,17 @@ __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,
-};
+
+/* Number of bytes in the unsigned integer hosting the bit-flags ('flag'
+   element) of 'gal_data_t'. */
+#define GAL_TABLEINTERN_FLAG_SIZE         1
+
+/* Bit 0: If the FITS TFORM has a "repeat" value of 0. */
+#define GAL_TABLEINTERN_FLAG_TFORM_REPEAT_IS_ZERO 0x1
+
+/* Bit 1: If the 'array' element in this dataset is a blank string. */
+#define GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING 0x2
+
 
 
 
diff --git a/lib/table.c b/lib/table.c
index 7e6b4ca..7d21a67 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -58,7 +58,32 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
    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).
+
+   Here are the gal_data_t structure elements that are used in 'allcols':
+
+            *restrict array -> Blank value (if present).
+                       type -> Type of column data.
+                       ndim -> Blank number of dimensions (1)
+                     *dsize -> Blank dimension lengths (1)
+                       size -> Blank total size (1)
+                  quietmmap -> ------------
+                  *mmapname -> ------------
+                 minmapsize -> Repeat (FITS Binary 'TFORM')
+                       nwcs -> ------------
+                       *wcs -> ------------
+                       flag -> 'GAL_TABLEINTERN_FLAG_*' macros.
+                     status -> ------------
+                      *name -> Column name.
+                      *unit -> Column unit.
+                   *comment -> Column comments.
+                   disp_fmt -> 'GAL_TABLE_DISPLAY_FMT' macros.
+                 disp_width -> To keep width of string columns.
+             disp_precision -> ------------
+                      *next -> ------------
+                     *block -> ------------
+
+*/
 gal_data_t *
 gal_table_info(char *filename, char *hdu, gal_list_str_t *lines,
                size_t *numcols, size_t *numrows, int *tableformat)
@@ -435,15 +460,8 @@ gal_table_read(char *filename, char *hdu, gal_list_str_t 
*lines,
                                    ignorecase, filename, hdu, colmatch);
 
   /* 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'
-     list. So before calling any of them, we will first reverse the
-     '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
-     actually freed).*/
-  gal_list_sizet_reverse(&indexll);
+     structure. Also note that after these functions, the 'indexll' will be
+     all freed (each popped element is actually freed).*/
   switch(tableformat)
     {
     case GAL_TABLE_FORMAT_TXT:
diff --git a/lib/tableintern.c b/lib/tableintern.c
index bd8a1c2..32730b0 100644
--- a/lib/tableintern.c
+++ b/lib/tableintern.c
@@ -428,7 +428,7 @@ gal_tableintern_read_blank(gal_data_t *col, char *blank)
      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;
+      col->flag |= GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING;
       gal_checkset_allocate_copy(blank, (char **)(&col->array));
     }
   else
diff --git a/lib/txt.c b/lib/txt.c
index 4ad22ae..b7b252d 100644
--- a/lib/txt.c
+++ b/lib/txt.c
@@ -536,7 +536,7 @@ txt_infoll_to_array(gal_data_t *datall, size_t *numdata)
           /* 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;
@@ -750,7 +750,7 @@ txt_read_token(gal_data_t *data, gal_data_t *info, char 
*token,
   double      *d = data->array,    *db;
 
   /* See if this token is blank. */
-  int isblankstr = ( info->flag==GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING
+  int isblankstr = ( info->flag & GAL_TABLEINTERN_FLAG_ARRAY_IS_BLANK_STRING
                      ? ( strcmp(info->array,token)==0 ? 1 : 0 )
                      : 0);
 
@@ -1102,6 +1102,7 @@ txt_read(char *filename, gal_list_str_t *lines, size_t 
*dsize,
               out->dsize=out->array=NULL;
             }
         }
+      gal_list_data_reverse(&out);
       break;
 
 



reply via email to

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