From 12478a64be4566622bb81e6d42ba752e11abcf1d Mon Sep 17 00:00:00 2001
From: Kartik Ohri <Kartik Ohri>
Date: Fri, 20 Mar 2020 19:35:08 +0530
Subject: [PATCH] Add units library.

---
 lib/arithmetic.c |  18 ++++-
 lib/units.c      | 170 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 187 insertions(+), 1 deletion(-)
 create mode 100644 lib/units.c

diff --git a/lib/arithmetic.c b/lib/arithmetic.c
index bab57078..8ea4ced4 100644
--- a/lib/arithmetic.c
+++ b/lib/arithmetic.c
@@ -37,6 +37,7 @@ along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
 #include <gnuastro/dimension.h>
 #include <gnuastro/statistics.h>
 #include <gnuastro/arithmetic.h>
+#include <gnuastro/units.h>
 
 #include <gnuastro-internal/arithmetic-internal.h>
 
@@ -429,7 +430,10 @@ arithmetic_abs(int flags, gal_data_t *in)
     case GAL_TYPE_FLOAT64:                                              \
       UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(double,   OP)               \
       break;                                                            \
-    default:                                                            \
+    case GAL_TYPE_STRING:                                               \
+      UNIFUNC_RUN_FUNCTION_ON_ELEMENT_INSET(char *,   OP)               \
+      break;                                                            \
+default:                                                                \
       error(EXIT_FAILURE, 0, "%s: type code %d not recognized",         \
             "UNIARY_FUNCTION_ON_ELEMENT", in->type);                    \
     }
@@ -479,6 +483,10 @@ arithmetic_unary_function(int operator, int flags, gal_data_t *in)
       UNIARY_FUNCTION_ON_ELEMENT( log10 );
       break;
 
+    case GAL_UNITS_CONVERT_RA_DECIMAL:
+      UNIARY_FUNCTION_ON_ELEMENT( gal_units_convert_ra_decimal  );
+      break;
+
     default:
       error(EXIT_FAILURE, 0, "%s: operator code %d not recognized",
             __func__, operator);
@@ -1737,6 +1745,8 @@ gal_arithmetic_set_operator(char *string, size_t *num_operands)
     { op=GAL_ARITHMETIC_OP_LOG;               *num_operands=1;  }
   else if (!strcmp(string, "log10"))
     { op=GAL_ARITHMETIC_OP_LOG10;             *num_operands=1;  }
+  else if (!strcmp(string, "convert_ra_decimal"))
+    { op=GAL_UNITS_CONVERT_RA_DECIMAL;        *num_operands=1;  }
 
   /* Statistical/higher-level operators. */
   else if (!strcmp(string, "minvalue"))
@@ -1887,6 +1897,7 @@ gal_arithmetic_operator_string(int operator)
     case GAL_ARITHMETIC_OP_SQRT:            return "sqrt";
     case GAL_ARITHMETIC_OP_LOG:             return "log";
     case GAL_ARITHMETIC_OP_LOG10:           return "log10";
+    case GAL_UNITS_CONVERT_RA_DECIMAL:      return "units_ra_decimal";
 
     case GAL_ARITHMETIC_OP_MINVAL:          return "minvalue";
     case GAL_ARITHMETIC_OP_MAXVAL:          return "maxvalue";
@@ -1986,6 +1997,7 @@ gal_arithmetic(int operator, size_t numthreads, int flags, ...)
     case GAL_ARITHMETIC_OP_SQRT:
     case GAL_ARITHMETIC_OP_LOG:
     case GAL_ARITHMETIC_OP_LOG10:
+    case GAL_UNITS_CONVERT_RA_DECIMAL:
       d1 = va_arg(va, gal_data_t *);
       out=arithmetic_unary_function(operator, flags, d1);
       break;
@@ -2067,6 +2079,10 @@ gal_arithmetic(int operator, size_t numthreads, int flags, ...)
       d1 = va_arg(va, gal_data_t *);
       out=arithmetic_change_type(d1, operator, flags);
       break;
+    case GAL_ARITHMETIC_OP_TO_DECIMAL:
+      d1 = va_arg(va, gal_data_t *);
+      out=arithmetic_change_type(d1, operator, flags);
+      break;
 
     /* Size operator */
     case GAL_ARITHMETIC_OP_SIZE:
diff --git a/lib/units.c b/lib/units.c
new file mode 100644
index 00000000..e123ade4
--- /dev/null
+++ b/lib/units.c
@@ -0,0 +1,170 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <error.h>
+
+
+
+
+/* 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, unsigned int n)
+{
+  char *token, *copy, *end;
+  unsigned int i = 0;
+  /* Create a copy of the string to be parsed */
+  token = copy = strdup (convert);
+
+  while (*token)
+    {
+      /* Check if the required number of arguments are passed */
+      if (i == n)
+        {
+          error (EXIT_FAILURE, 0, "'%s' exceeds maximum number of "
+                                  "arguments\n", convert);
+          return 0;
+        }
+
+      /* Extract the substring till the next delimiter */
+      token = strtok (i == 0 ? copy : NULL, &delimiter);
+      if (token)
+        {
+          /* Parse extracted string as a number */
+          args[i++] = strtod (token, &end);
+          /* Check if strtod fails to parse any character in substring */
+          if (*end && *end != delimiter)
+            {
+              error (EXIT_FAILURE, 0, "Unable to parse element %u in "
+                                      "'%s'\n", i, convert);
+              return 0;
+            }
+          else
+            token = end + 1;
+        }
+    }
+
+  /* Check if the number of elements parsed is unequal to numbers of
+   * elements requested */
+  if (i != n)
+    {
+      error (EXIT_FAILURE, 0, "`%s' must contain %u numbers, but has "
+                              "%u numbers\n", convert, n, i);
+      return 0;
+    }
+
+  return 1;
+}
+
+
+
+
+/* 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_convert_ra_decimal (char *convert)
+{
+  double val[3];
+  double decimal = 0.0;
+
+  /* Check whether the string is successfully parsed */
+  if (gal_units_extract_decimal (convert, ':', val, 3))
+    {
+      /* Check whether value of hours is in within limits */
+      if (val[0] < 0.0 || val[0] > 24.0)
+        {
+          error (EXIT_FAILURE, 0, "Error: Value of hours should be "
+                                  "between be 0 and 24. %s\n", convert);
+          return NAN;
+        }
+      /* Add hours to the decimal value */
+      decimal += val[0];
+
+      /* Check whether value of minutes is in within limits */
+      if (val[1] < 0.0 || val[1] > 60.0)
+        {
+          error (EXIT_FAILURE, 0, "Error: Value of minutes should be "
+                                  "between be 0 and 60. %s\n", convert);
+          return NAN;
+        }
+      /* Convert minutes to hours and add to the decimal value */
+      decimal += val[1] / 60;
+
+      /* Check whether value of seconds is in within limits */
+      if (val[2] < 0.0 || val[2] > 60.0)
+        {
+          error (EXIT_FAILURE, 0, "Error: Value of seconds should be "
+                                  "between be 0 and 60. %s\n", convert);
+          return NAN;
+        }
+      /* Convert seconds to hours and add to the decimal value */
+      decimal += val[2] / 3600;
+
+      /* Convert value to degrees */
+      decimal *= 15.0;
+
+      return decimal;
+    }
+  else
+    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_convert_dec_decimal (char *convert)
+{
+  double val[3];
+  double decimal = 0.0;
+
+  if (gal_units_extract_decimal (convert, ':', val, 3))
+    {
+      /* Check whether value of degrees is in within limits */
+      if (val[0] < 0.0 || val[0] > 90.0)
+        {
+          error (EXIT_FAILURE, 0, "Error: Value of degrees should be "
+                                  "between be 0 and 90. %s\n", convert);
+          return NAN;
+        }
+      /* Add degrees to the decimal value */
+      decimal += val[0];
+
+      /* Check whether value of arc-minutes is in within limits */
+      if (val[1] < 0.0 || val[1] > 60.0)
+        {
+          error (EXIT_FAILURE, 0, "Error: Value of arc-minutes should be "
+                                  "between be 0 and 60. %s\n", convert);
+          return NAN;
+        }
+      /* Convert arc-minutes to degrees and add to the decimal value */
+      decimal += val[1] / 60;
+
+      /* Check whether value of arc-seconds is in within limits */
+      if (val[2] < 0.0 || val[2] > 60.0)
+        {
+          error (EXIT_FAILURE, 0, "Error: Value of arc-seconds should be "
+                                  "between be 0 and 60. %s\n", convert);
+          return NAN;
+        }
+      /* Convert arc-seconds to degrees and add to the decimal value */
+      decimal += val[2] / 3600;
+
+      return decimal;
+    }
+  else
+    return NAN;
+}
+
+
+
+
+int main ()
+{
+  printf ("%f\n", gal_units_convert_ra_decimal ("02:30:20"));
+  printf ("%f\n", gal_units_convert_ra_decimal ("2:30:20"));
+}
-- 
2.20.1

