From 655d34cb6c42204016facd6b643225e6edf7fd92 Mon Sep 17 00:00:00 2001
From: Florian Prill <florian.prill@dwd.de>
Date: Thu, 25 Apr 2019 11:06:02 +0200
Subject: [PATCH] [divide_timespans]  implemented large integer division
 algorithm for (days + msecs). - not yet enabled in mtime library.

---
 examples/Makefile.in       |  22 +++-
 examples/int_div_example.c |  99 +++++++++++++++
 include/int_div.h          |  46 +++++++
 src/Makefile.in            |  11 +-
 src/int_div.c              | 253 +++++++++++++++++++++++++++++++++++++
 5 files changed, 425 insertions(+), 6 deletions(-)
 create mode 100644 examples/int_div_example.c
 create mode 100644 include/int_div.h
 create mode 100644 src/int_div.c

diff --git a/examples/Makefile.in b/examples/Makefile.in
index 337b5814..8938a376 100644
--- a/examples/Makefile.in
+++ b/examples/Makefile.in
@@ -93,7 +93,8 @@ noinst_PROGRAMS = time_calculus$(EXEEXT) model_integration$(EXEEXT) \
 	output_control$(EXEEXT) recurrence$(EXEEXT) repetitor$(EXEEXT) \
 	tas$(EXEEXT) uniq$(EXEEXT) modulo$(EXEEXT) \
 	comp_weights$(EXEEXT) callback_test$(EXEEXT) iconatm$(EXEEXT) \
-	iconoce$(EXEEXT) iconoce_hl$(EXEEXT) simulate_iau$(EXEEXT)
+	iconoce$(EXEEXT) iconoce_hl$(EXEEXT) simulate_iau$(EXEEXT) \
+	int_div_example$(EXEEXT)
 subdir = examples
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_prog_doxygen.m4 \
@@ -219,6 +220,12 @@ uniq_DEPENDENCIES = ../src/libmtime.la
 uniq_LINK = $(LIBTOOL) $(AM_V_lt) --tag=FC $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(FCLD) $(AM_FCFLAGS) $(FCFLAGS) \
 	$(uniq_LDFLAGS) $(LDFLAGS) -o $@
+am_int_div_example_OBJECTS = int_div_example.$(OBJEXT)
+int_div_example_OBJECTS = $(am_int_div_example_OBJECTS)
+int_div_example_DEPENDENCIES = ../src/libmtime.la
+int_div_example_LINK = $(LIBTOOL) $(AM_V_lt) --tag=FC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(FCLD) $(AM_FCFLAGS) $(FCFLAGS) \
+	$(int_div_example_LDFLAGS) $(LDFLAGS) -o $@
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -277,14 +284,16 @@ SOURCES = $(callback_test_SOURCES) $(comp_weights_SOURCES) \
 	$(model_integration_SOURCES) $(modulo_SOURCES) \
 	$(output_control_SOURCES) $(recurrence_SOURCES) \
 	$(repetitor_SOURCES) $(simulate_iau_SOURCES) $(tas_SOURCES) \
-	$(time_calculus_SOURCES) $(uniq_SOURCES)
+	$(time_calculus_SOURCES) $(uniq_SOURCES) \
+	$(int_div_example_SOURCES)
 DIST_SOURCES = $(callback_test_SOURCES) $(comp_weights_SOURCES) \
 	$(duration_SOURCES) $(example_SOURCES) $(example_hl_SOURCES) \
 	$(iconatm_SOURCES) $(iconoce_SOURCES) $(iconoce_hl_SOURCES) \
 	$(model_integration_SOURCES) $(modulo_SOURCES) \
 	$(output_control_SOURCES) $(recurrence_SOURCES) \
 	$(repetitor_SOURCES) $(simulate_iau_SOURCES) $(tas_SOURCES) \
-	$(time_calculus_SOURCES) $(uniq_SOURCES)
+	$(time_calculus_SOURCES) $(uniq_SOURCES) \
+	$(int_div_example_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -515,6 +524,9 @@ iconoce_hl_LDFLAGS =
 simulate_iau_SOURCES = simulate_iau.f90
 simulate_iau_LDADD = ../src/libmtime.la
 simulate_iau_LDFLAGS = 
+int_div_example_SOURCES = int_div_example.f90
+int_div_example_LDADD = ../src/libmtime.la
+int_div_example_LDFLAGS = 
 all: all-am
 
 .SUFFIXES:
@@ -626,6 +638,10 @@ uniq$(EXEEXT): $(uniq_OBJECTS) $(uniq_DEPENDENCIES) $(EXTRA_uniq_DEPENDENCIES)
 	@rm -f uniq$(EXEEXT)
 	$(AM_V_FCLD)$(uniq_LINK) $(uniq_OBJECTS) $(uniq_LDADD) $(LIBS)
 
+int_div_example$(EXEEXT): $(int_div_example_OBJECTS) $(int_div_example_DEPENDENCIES) $(EXTRA_int_div_example_DEPENDENCIES) 
+	@rm -f int_div_example$(EXEEXT)
+	$(AM_V_FCLD)$(int_div_example_LINK) $(int_div_example_OBJECTS) $(int_div_example_LDADD) $(LIBS) -lgmp 
+
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
 
diff --git a/examples/int_div_example.c b/examples/int_div_example.c
new file mode 100644
index 00000000..9bbc075d
--- /dev/null
+++ b/examples/int_div_example.c
@@ -0,0 +1,99 @@
+/* --------------------------------------------------------------------------------
+ * EXAMPLE.C
+ *
+ * 04/2019 : F. Prill, DWD
+ *
+ * limitations:
+ *
+ * - the remainder in the mtime struct "_divisionquotienttimespan" is
+ *   limited to int64_t msecs.
+ *
+ * -------------------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "int_div.h"
+#include <gmp.h>
+
+int divide_timespan_mpz(const t_int days1, const t_int day_fraction1,
+                        const t_int days2, const t_int day_fraction2,
+                        t_int *q_decimal, t_int *remainder_days, t_int *remainder_ms);
+
+int main(void) {
+  const int NREPEAT = 3000000;
+
+  int   ret, i, cnt = 0;
+  t_int days1, day_fraction1, days2, day_fraction2;
+  t_int q_decimal0 = 0, remainder_days0 = 0, remainder_ms0 = 0;
+  t_int q_decimal  = 0, remainder_days  = 0, remainder_ms  = 0;
+
+  double f1 = 1728000000.0 / (double) RAND_MAX;
+  double f2 = 86400000.0   / (double) RAND_MAX;
+
+  printf("test program\n");
+  printf("  checking %d random divisions against GNU Multiple Precision Arithmetic Library\n",NREPEAT);
+  srand ( 123 );
+
+  for (i=0; i<NREPEAT; i++) {
+    /* test randomly generated input against GNU Multiple Precision
+       Arithmetic Library: */
+    days1           = (t_int) ( f1 * (double) rand() );
+    day_fraction1   = (t_int) ( f2 * (double) rand() );
+    days2           = (t_int) ( f1 * (double) rand() );
+    day_fraction2   = (t_int) ( f2 * (double) rand() );
+
+    ret = divide_timespan_mpz(days1, day_fraction1, days2, day_fraction2,
+                              &q_decimal0, &remainder_days0, &remainder_ms0);
+    ret = divide_timespan(days1, day_fraction1, days2, day_fraction2,
+                          &q_decimal, &remainder_days, &remainder_ms);
+
+    if ((ret != 0)                          ||
+        (q_decimal != q_decimal0)           ||
+        (remainder_days != remainder_days0) ||
+        (remainder_ms != remainder_ms0))
+      {
+        printf("dividend: %" PRId64 " : %" PRId64 " \n", days1, day_fraction1);
+        printf("divisor : %" PRId64 " : %" PRId64 " \n", days2, day_fraction2);
+        printf("comparison: quotient_out = %" PRId64 ", remainder = %" PRId64 " : %" PRId64 "\n",
+               q_decimal0, remainder_days0, remainder_ms0);
+        printf("quotient_out = %" PRId64 ", remainder = %" PRId64 " : %" PRId64 "\n",
+               q_decimal, remainder_days, remainder_ms);
+        printf("return code = %d\n",ret);
+        cnt++;
+      }
+  }
+
+  printf("  %d discrepancies reported.\n", cnt);
+  printf("test program done after %d tests.\n", NREPEAT);
+  exit(0);
+}
+
+
+int divide_timespan_mpz(const t_int days1, const t_int day_fraction1,
+                        const t_int days2, const t_int day_fraction2,
+                        t_int *q_decimal, t_int *remainder_days, t_int *remainder_ms)
+{
+  mpz_t a0, a1, a2, b1, b2, q, r;
+
+  mpz_inits(a0,a1,a2,b1,b2,q,r,NULL);
+
+  mpz_set_ui(a0, 86400000);
+  mpz_set_ui(a1,days1);
+  mpz_set_ui(a2,day_fraction1);
+  mpz_set_ui(b1,days2);
+  mpz_set_ui(b2,day_fraction2);
+
+  mpz_addmul(a2,a1,a0);
+  mpz_addmul(b2,b1,a0);
+  mpz_fdiv_qr(q,r,a2,b2);
+
+  *q_decimal = mpz_get_ui(q);
+
+  mpz_fdiv_qr(a2,b2,r,a0);
+  *remainder_days = mpz_get_ui(a2);
+  *remainder_ms   = mpz_get_ui(b2);
+
+  mpz_clears(a0,a1,a2,b1,b2,q,r,NULL);
+  return 0;
+}
diff --git a/include/int_div.h b/include/int_div.h
new file mode 100644
index 00000000..480bc4f9
--- /dev/null
+++ b/include/int_div.h
@@ -0,0 +1,46 @@
+/*
+  Large integer division algorithm for (days + msecs).
+
+  04/2019 : F. Prill, DWD.
+
+  The method uses a basis b=1200 representation for time spans and the
+  division algorithm explained in
+
+  P. B. Hansen: Multiple-Length Division Revisited: A Tour of the Minefield (192)
+                Electrical Engineering and Computer Science Technical Reports. 166.
+*/
+
+#ifndef INT_DIV_H_
+#define INT_DIV_H_
+
+#include <stdint.h>
+
+/* array representation has (w+1) digits: */
+#define W_DIGITS 6
+
+typedef int64_t t_int;
+
+/* data type for a natural number represented by an array of
+   (W_DIGITS+1) digits in radix b. */
+typedef t_int t_number[W_DIGITS + 1];
+
+
+void         number_init(t_number *x, const t_int val, const unsigned int m);
+void         print_number(const t_number x);
+unsigned int length(const t_number x);
+void         product(t_number *x, const t_number y, const t_int k, const t_int b, int* ret);
+void         quotient(t_number *x, const t_number y, t_int k, const t_int b);
+void         iremainder(t_number *x, const t_number y, t_int k, const t_int b);
+t_int        trial(const t_number r, const t_number d, const int k, const int m, const t_int b, int* ret);
+int          smaller(const t_number r, const t_number dq, const int k, const int m);
+void         difference(t_number *r, const t_number dq, const int k, const int m, const t_int b, int *ret);
+int          division(const t_number x, const t_number y, t_number *q, t_number *r, const t_int b);
+
+void         convert_basis(const t_int v_in, t_number *v_out, const int digits, const int istart);
+t_int        convert_to_decimal(const t_number v_in, const int istart, const int iend);
+
+int          divide_timespan(const t_int days1, const t_int day_fraction1,
+                             const t_int days2, const t_int day_fraction2,
+                             t_int *q_decimal, t_int *remainder_days, t_int *remainder_ms);
+
+#endif /* INT_DIV_H_ */
diff --git a/src/Makefile.in b/src/Makefile.in
index 2cd42561..a8bad531 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -139,7 +139,7 @@ am__objects_1 = mtime_calendar.lo mtime_calendar360day.lo \
 	mtime_iso8601.lo mtime_date.lo mtime_datetime.lo \
 	mtime_julianDay.lo mtime_time.lo mtime_timedelta.lo \
 	mtime_eventList.lo mtime_eventHandling.lo mtime_utilities.lo \
-	vsop87.lo kepler.lo orbit.lo
+	vsop87.lo kepler.lo orbit.lo int_div.lo
 am__objects_2 = error_handling.lo mtime_constants.lo \
 	mtime_error_handling.lo mtime_c_bindings.lo libmtime.lo \
 	libmtime_hl.lo
@@ -175,7 +175,8 @@ am__depfiles_remade = ./$(DEPDIR)/kepler.Plo \
 	./$(DEPDIR)/mtime_julianDay.Plo ./$(DEPDIR)/mtime_time.Plo \
 	./$(DEPDIR)/mtime_timedelta.Plo \
 	./$(DEPDIR)/mtime_utilities.Plo ./$(DEPDIR)/orbit.Plo \
-	./$(DEPDIR)/vsop87.Plo
+	./$(DEPDIR)/vsop87.Plo \
+	./$(DEPDIR)/int_div.Plo
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -408,7 +409,8 @@ c_SOURCES = mtime_calendar.c \
 		      mtime_utilities.c \
 		      vsop87.c \
 		      kepler.c \
-		      orbit.c
+		      orbit.c \
+		      int_div.c
 
 
 # Fortran sources: this list is ordered with respect to the module
@@ -533,6 +535,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mtime_utilities.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/orbit.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vsop87.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/int_div.Plo@am__quote@ # am--include-marker
 
 $(am__depfiles_remade):
 	@$(MKDIR_P) $(@D)
@@ -742,6 +745,7 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/mtime_utilities.Plo
 	-rm -f ./$(DEPDIR)/orbit.Plo
 	-rm -f ./$(DEPDIR)/vsop87.Plo
+	-rm -f ./$(DEPDIR)/int_div.Plo
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-tags
@@ -803,6 +807,7 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/mtime_utilities.Plo
 	-rm -f ./$(DEPDIR)/orbit.Plo
 	-rm -f ./$(DEPDIR)/vsop87.Plo
+	-rm -f ./$(DEPDIR)/int_div.Plo
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
diff --git a/src/int_div.c b/src/int_div.c
new file mode 100644
index 00000000..b947fabc
--- /dev/null
+++ b/src/int_div.c
@@ -0,0 +1,253 @@
+/*
+  Large integer division algorithm for (days + msecs).
+
+  04/2019 : F. Prill, DWD.
+
+  The method uses a basis b=1200 representation for time spans and the
+  division algorithm explained in
+
+  P. B. Hansen: Multiple-Length Division Revisited: A Tour of the Minefield (192).
+                Electrical Engineering and Computer Science Technical Reports. 166.
+*/
+#include "int_div.h"
+
+const int   Fail      = -1;
+const t_int iZero     = (t_int) 0;
+const t_int msecs_day = 86400000;
+t_int const bb[4] = { 1, 1200, 1440000, 1728000000 }; /* powers of b = 1200 */
+
+
+/* initialize digits m,m+1,...,W_DIGITS by a constant val. */
+void number_init(t_number *x, const t_int val, const unsigned int m) {
+  int i;
+  for (i=m; i<=W_DIGITS; ++i)  (*x)[i]=val;
+}
+
+
+/* linear search to determine the length of a natural number. */
+unsigned int length(const t_number x) {
+  int i = W_DIGITS;
+  while ((x[i] == iZero) && (i >= 0))  i--;
+  return (i+1);
+}
+
+
+/* partial arithmetic wrt. radix b: product x=y*k, return ret!=0 if overflow. */
+void product(t_number *x, const t_number y, const t_int k, const t_int b, int* ret) {
+  int   i, m=length(y);
+  t_int temp, carry = 0;
+
+  if (*ret != 0) return;
+  for (i=0; i<m; ++i) {
+    temp = y[i]*k + carry;
+    (*x)[i] = temp % b;
+    carry = temp/b;
+  }
+  if (m<=W_DIGITS)
+    (*x)[m] = carry;
+  else
+    *ret = (carry != iZero);
+  for (i=m+1; i<=W_DIGITS; ++i)  (*x)[i] = iZero;
+}
+
+
+/* partial arithmetic wrt. radix b: quotient x=y/k. */
+void quotient(t_number *x, const t_number y, t_int k, const t_int b) {
+  int i, m = length(y);
+  t_int temp, carry = iZero;
+  number_init(x, iZero, m);
+
+  for (i=m-1; i>=0; i--) {
+    temp = carry*b + y[i];
+    (*x)[i] = temp / k;
+    carry = temp % k;
+  }
+}
+
+
+/* partial arithmetic wrt. radix b: remainder x of y/k. */
+void iremainder(t_number *x, const t_number y, t_int k, const t_int b) {
+  int m = length(y), i;
+  t_int carry = iZero;
+
+  number_init(x, iZero, 1);
+  for (i=m-1; i>=0; i--)
+    carry = (carry*b + y[i]) % k;
+  (*x)[0] = carry;
+}
+
+
+/* prefix arithmetic wrt. radix b: estimate of a quotient digit, return <0 if error. */
+t_int trial(const t_number r, const t_number d,
+            const int k, const int m, const t_int b, int* ret) {
+  t_int d2,r3,t = iZero;
+  int   km = k+m;
+#if DEBUG
+  if ((m < 2) || (km < m) || (km>W_DIGITS))  *ret = Fail;
+#endif
+  if (*ret != 0) return iZero;
+  r3 = (r[km]*b + r[km-1])*b + r[km-2];
+  d2 = d[m-1]*b + d[m-2];
+
+  t = r3/d2;
+  return (t > b-1) ? (b-1) : t;
+}
+
+
+/* prefix arithmetic: prefix comparison r{m+1} < dq, return <0 if error. */
+int smaller(const t_number r, const t_number dq, const int k, const int m) {
+  int i=m;
+
+#if DEBUG
+  if ((k < 0) || (k+m < k) || (k+m>W_DIGITS))  return Fail;
+#endif
+  while ((r[i+k] == dq[i]) && (i>=0))  i--;
+  return r[i+k] < dq[i];
+}
+
+
+/* prefix arithmetic wrt. radix b: prefix subtraction r{m+1} := r{m+1} - dq, return ret!=0 if error. */
+void difference(t_number *r, const t_number dq, const int k, const int m, const t_int b, int *ret) {
+  int i;
+  t_int borrow, diff;
+
+#ifdef DEBUG
+  if ((k < 0) || (k+m < k) || (k+m>W_DIGITS))  *ret = Fail;
+#endif
+  if (*ret != 0) return;
+  borrow = iZero;
+  for (i=0; i<=m; i++) {
+    diff = (*r)[i+k] - dq[i] - borrow + b;
+    (*r)[i+k] = diff % b;
+    borrow = (t_int) 1 - diff / b;
+  }
+  *ret = (borrow != iZero);
+}
+
+
+/* complete algorithm for multiple length division x/y wrt. radix b, return <0 if error. */
+int division(const t_number x, const t_number y,
+             t_number *q, t_number *r, const t_int b) {
+  int i, k, m, n, ret = 0;
+  t_int y1, f, qt;
+  t_number d, dq, rm;
+
+  m = length(y);
+  if (m == 1) {
+    y1 = y[m-1];
+    if (y1 <= 0) return Fail;
+    quotient(q,x,y1,b);
+    iremainder(r,x,y1,b);
+  }
+  else {
+    n = length(x);
+    number_init(q, iZero, 0);
+    number_init(r, iZero, 0);
+    if (m > n)
+      for (i=0; i<n; i++)  (*r)[i] = x[i];
+    else {
+      /* long division algorithm */
+      if ((m < 2) || (n < m) || (n>W_DIGITS)) return Fail;
+      f = b / (y[m-1] + (t_int) 1);
+      product(&rm,x,f,b, &ret);
+      product(&d, y,f,b, &ret);
+      for (k=n-m; (k>=0) && (ret == 0); k--) {
+        qt = trial(rm, d, k, m, b, &ret);
+        product(&dq,d,qt,b, &ret);
+
+        if ((ret == 0) && (smaller(rm,dq,k,m)))
+          product(&dq,d,--qt,b, &ret);
+
+        (*q)[k] = qt;
+        difference(&rm,dq,k,m,b, &ret);
+      }
+      quotient(r,rm,f,b);
+    }
+  }
+  return ret;
+}
+
+
+/* convert to basis "b" by a succession of Euclidean divisions, stores to v_out[istart...]. */
+void convert_basis(const t_int v_in, t_number *v_out,
+                   const int digits, const int istart) {
+  int i;
+  t_int v = v_in;
+  for (i=digits; i>=0; i--) {
+    (*v_out)[istart+i] = v / bb[i];
+    v %= bb[i];
+  }
+}
+
+
+/* convert to decimal basis. */
+t_int convert_to_decimal(const t_number v_in,
+                         const int istart, const int iend) {
+  int i, l = length(v_in);
+  t_int v_out = iZero;
+
+  if (iend < l)  l=iend;
+  if ((l-istart) > 3)  return (t_int) Fail;
+  for (i=0; i<(l-istart+1); i++)
+    v_out += v_in[i+istart] * bb[i];
+  return v_out;
+}
+
+
+/*
+   convert days and day fraction to b=1200 basis by a succession of
+   Euclidean divisions.
+   By pre-multiplying with a factor of 20, the day fraction can be
+   expressed by three digits wrt. the basis b=1200.
+
+   restrictions:
+
+   - the no. of digits in the algorithm restricts numbers to 1200^3
+     days, ie. approx. 4.7 mio years.
+   - the quotient is limited to 1200^3 in order to fit into int64_t
+     after conversion to decimal base.
+   - all numbers and time spans must be positive.
+*/
+int divide_timespan(const t_int days1, const t_int day_fraction1,
+                    const t_int days2, const t_int day_fraction2,
+                    t_int *q_decimal, t_int *remainder_days, t_int *remainder_ms)
+{
+  int      ret = 0;
+  t_number p1, p2, q, r; /* dividend, divisor, quotient, remainder */
+  t_int    qd = 0, rd = 0;
+
+  if ((day_fraction1 > msecs_day) || (day_fraction2 > msecs_day))  return Fail;
+
+  convert_basis(day_fraction1 * (t_int) 20, &p1, 2, 0);
+  convert_basis(days1, &p1, W_DIGITS-3, 3);
+  convert_basis(day_fraction2 * (t_int) 20, &p2, 2, 0);
+  convert_basis(days2, &p2, W_DIGITS-3, 3);
+
+  ret = division(p1, p2, &q, &r, (t_int) 1200);  /* perform division. */
+
+  if (ret == 0) {
+    /* convert result back to b=10 basis. */
+    *q_decimal      = qd = convert_to_decimal(q,0,W_DIGITS);
+    *remainder_days =      convert_to_decimal(r,3,W_DIGITS);
+    *remainder_ms   = rd = convert_to_decimal(r,0,2);
+    ret = (qd < iZero) || (rd < iZero);
+
+    *remainder_ms /= ((t_int) 20);
+  }
+  return ret;
+}
+
+
+#ifdef DEBUG
+
+#include <stdio.h>
+#include <inttypes.h>
+
+/* print number to screen. */
+void  print_number(const t_number x) {
+  int i, m = length(x) - 1;
+  for (i=m; i>=0; i--)
+    printf("%" PRId64 " ", x[i]);
+  printf("\n");
+}
+#endif
-- 
GitLab