src/glplib01.c
author Alpar Juttner <alpar@cs.elte.hu>
Sun, 05 Dec 2010 17:35:23 +0100
changeset 2 4c8956a7bdf4
permissions -rw-r--r--
Set up CMAKE build environment
alpar@1
     1
/* glplib01.c (bignum arithmetic) */
alpar@1
     2
alpar@1
     3
/***********************************************************************
alpar@1
     4
*  This code is part of GLPK (GNU Linear Programming Kit).
alpar@1
     5
*
alpar@1
     6
*  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
alpar@1
     7
*  2009, 2010 Andrew Makhorin, Department for Applied Informatics,
alpar@1
     8
*  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
alpar@1
     9
*  E-mail: <mao@gnu.org>.
alpar@1
    10
*
alpar@1
    11
*  GLPK is free software: you can redistribute it and/or modify it
alpar@1
    12
*  under the terms of the GNU General Public License as published by
alpar@1
    13
*  the Free Software Foundation, either version 3 of the License, or
alpar@1
    14
*  (at your option) any later version.
alpar@1
    15
*
alpar@1
    16
*  GLPK is distributed in the hope that it will be useful, but WITHOUT
alpar@1
    17
*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
alpar@1
    18
*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
alpar@1
    19
*  License for more details.
alpar@1
    20
*
alpar@1
    21
*  You should have received a copy of the GNU General Public License
alpar@1
    22
*  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
alpar@1
    23
***********************************************************************/
alpar@1
    24
alpar@1
    25
#include "glpenv.h"
alpar@1
    26
#include "glplib.h"
alpar@1
    27
alpar@1
    28
/***********************************************************************
alpar@1
    29
*  Two routines below are intended to multiply and divide unsigned
alpar@1
    30
*  integer numbers of arbitrary precision.
alpar@1
    31
* 
alpar@1
    32
*  The routines assume that an unsigned integer number is represented in
alpar@1
    33
*  the positional numeral system with the base 2^16 = 65536, i.e. each
alpar@1
    34
*  "digit" of the number is in the range [0, 65535] and represented as
alpar@1
    35
*  a 16-bit value of the unsigned short type. In other words, a number x
alpar@1
    36
*  has the following representation:
alpar@1
    37
* 
alpar@1
    38
*         n-1
alpar@1
    39
*     x = sum d[j] * 65536^j,
alpar@1
    40
*         j=0
alpar@1
    41
* 
alpar@1
    42
*  where n is the number of places (positions), and d[j] is j-th "digit"
alpar@1
    43
*  of x, 0 <= d[j] <= 65535.
alpar@1
    44
***********************************************************************/
alpar@1
    45
alpar@1
    46
/***********************************************************************
alpar@1
    47
*  NAME
alpar@1
    48
*
alpar@1
    49
*  bigmul - multiply unsigned integer numbers of arbitrary precision
alpar@1
    50
* 
alpar@1
    51
*  SYNOPSIS
alpar@1
    52
* 
alpar@1
    53
*  #include "glplib.h"
alpar@1
    54
*  void bigmul(int n, int m, unsigned short x[], unsigned short y[]);
alpar@1
    55
* 
alpar@1
    56
*  DESCRIPTION
alpar@1
    57
* 
alpar@1
    58
*  The routine bigmul multiplies unsigned integer numbers of arbitrary
alpar@1
    59
*  precision.
alpar@1
    60
* 
alpar@1
    61
*  n is the number of digits of multiplicand, n >= 1;
alpar@1
    62
* 
alpar@1
    63
*  m is the number of digits of multiplier, m >= 1;
alpar@1
    64
* 
alpar@1
    65
*  x is an array containing digits of the multiplicand in elements
alpar@1
    66
*  x[m], x[m+1], ..., x[n+m-1]. Contents of x[0], x[1], ..., x[m-1] are
alpar@1
    67
*  ignored on entry.
alpar@1
    68
* 
alpar@1
    69
*  y is an array containing digits of the multiplier in elements y[0],
alpar@1
    70
*  y[1], ..., y[m-1].
alpar@1
    71
* 
alpar@1
    72
*  On exit digits of the product are stored in elements x[0], x[1], ...,
alpar@1
    73
*  x[n+m-1]. The array y is not changed. */
alpar@1
    74
alpar@1
    75
void bigmul(int n, int m, unsigned short x[], unsigned short y[])
alpar@1
    76
{     int i, j;
alpar@1
    77
      unsigned int t;
alpar@1
    78
      xassert(n >= 1);
alpar@1
    79
      xassert(m >= 1);
alpar@1
    80
      for (j = 0; j < m; j++) x[j] = 0;
alpar@1
    81
      for (i = 0; i < n; i++)
alpar@1
    82
      {  if (x[i+m])
alpar@1
    83
         {  t = 0;
alpar@1
    84
            for (j = 0; j < m; j++)
alpar@1
    85
            {  t += (unsigned int)x[i+m] * (unsigned int)y[j] +
alpar@1
    86
                    (unsigned int)x[i+j];
alpar@1
    87
               x[i+j] = (unsigned short)t;
alpar@1
    88
               t >>= 16;
alpar@1
    89
            }
alpar@1
    90
            x[i+m] = (unsigned short)t;
alpar@1
    91
         }
alpar@1
    92
      }
alpar@1
    93
      return;
alpar@1
    94
}
alpar@1
    95
alpar@1
    96
/***********************************************************************
alpar@1
    97
*  NAME
alpar@1
    98
*
alpar@1
    99
*  bigdiv - divide unsigned integer numbers of arbitrary precision
alpar@1
   100
* 
alpar@1
   101
*  SYNOPSIS
alpar@1
   102
* 
alpar@1
   103
*  #include "glplib.h"
alpar@1
   104
*  void bigdiv(int n, int m, unsigned short x[], unsigned short y[]);
alpar@1
   105
* 
alpar@1
   106
*  DESCRIPTION
alpar@1
   107
* 
alpar@1
   108
*  The routine bigdiv divides one unsigned integer number of arbitrary
alpar@1
   109
*  precision by another with the algorithm described in [1].
alpar@1
   110
* 
alpar@1
   111
*  n is the difference between the number of digits of dividend and the
alpar@1
   112
*  number of digits of divisor, n >= 0.
alpar@1
   113
* 
alpar@1
   114
*  m is the number of digits of divisor, m >= 1.
alpar@1
   115
* 
alpar@1
   116
*  x is an array containing digits of the dividend in elements x[0],
alpar@1
   117
*  x[1], ..., x[n+m-1].
alpar@1
   118
* 
alpar@1
   119
*  y is an array containing digits of the divisor in elements y[0],
alpar@1
   120
*  y[1], ..., y[m-1]. The highest digit y[m-1] must be non-zero.
alpar@1
   121
* 
alpar@1
   122
*  On exit n+1 digits of the quotient are stored in elements x[m],
alpar@1
   123
*  x[m+1], ..., x[n+m], and m digits of the remainder are stored in
alpar@1
   124
*  elements x[0], x[1], ..., x[m-1]. The array y is changed but then
alpar@1
   125
*  restored.
alpar@1
   126
*
alpar@1
   127
*  REFERENCES
alpar@1
   128
*
alpar@1
   129
*  1. D. Knuth. The Art of Computer Programming. Vol. 2: Seminumerical
alpar@1
   130
*  Algorithms. Stanford University, 1969. */
alpar@1
   131
alpar@1
   132
void bigdiv(int n, int m, unsigned short x[], unsigned short y[])
alpar@1
   133
{     int i, j;
alpar@1
   134
      unsigned int t;
alpar@1
   135
      unsigned short d, q, r;
alpar@1
   136
      xassert(n >= 0);
alpar@1
   137
      xassert(m >= 1);
alpar@1
   138
      xassert(y[m-1] != 0);
alpar@1
   139
      /* special case when divisor has the only digit */
alpar@1
   140
      if (m == 1)
alpar@1
   141
      {  d = 0;
alpar@1
   142
         for (i = n; i >= 0; i--)
alpar@1
   143
         {  t = ((unsigned int)d << 16) + (unsigned int)x[i];
alpar@1
   144
            x[i+1] = (unsigned short)(t / y[0]);
alpar@1
   145
            d = (unsigned short)(t % y[0]);
alpar@1
   146
         }
alpar@1
   147
         x[0] = d;
alpar@1
   148
         goto done;
alpar@1
   149
      }
alpar@1
   150
      /* multiply dividend and divisor by a normalizing coefficient in
alpar@1
   151
         order to provide the condition y[m-1] >= base / 2 */
alpar@1
   152
      d = (unsigned short)(0x10000 / ((unsigned int)y[m-1] + 1));
alpar@1
   153
      if (d == 1)
alpar@1
   154
         x[n+m] = 0;
alpar@1
   155
      else
alpar@1
   156
      {  t = 0;
alpar@1
   157
         for (i = 0; i < n+m; i++)
alpar@1
   158
         {  t += (unsigned int)x[i] * (unsigned int)d;
alpar@1
   159
            x[i] = (unsigned short)t;
alpar@1
   160
            t >>= 16;
alpar@1
   161
         }
alpar@1
   162
         x[n+m] = (unsigned short)t;
alpar@1
   163
         t = 0;
alpar@1
   164
         for (j = 0; j < m; j++)
alpar@1
   165
         {  t += (unsigned int)y[j] * (unsigned int)d;
alpar@1
   166
            y[j] = (unsigned short)t;
alpar@1
   167
            t >>= 16;
alpar@1
   168
         }
alpar@1
   169
      }
alpar@1
   170
      /* main loop */
alpar@1
   171
      for (i = n; i >= 0; i--)
alpar@1
   172
      {  /* estimate and correct the current digit of quotient */
alpar@1
   173
         if (x[i+m] < y[m-1])
alpar@1
   174
         {  t = ((unsigned int)x[i+m] << 16) + (unsigned int)x[i+m-1];
alpar@1
   175
            q = (unsigned short)(t / (unsigned int)y[m-1]);
alpar@1
   176
            r = (unsigned short)(t % (unsigned int)y[m-1]);
alpar@1
   177
            if (q == 0) goto putq; else goto test;
alpar@1
   178
         }
alpar@1
   179
         q = 0;
alpar@1
   180
         r = x[i+m-1];
alpar@1
   181
decr:    q--; /* if q = 0 then q-- = 0xFFFF */
alpar@1
   182
         t = (unsigned int)r + (unsigned int)y[m-1];
alpar@1
   183
         r = (unsigned short)t;
alpar@1
   184
         if (t > 0xFFFF) goto msub;
alpar@1
   185
test:    t = (unsigned int)y[m-2] * (unsigned int)q;
alpar@1
   186
         if ((unsigned short)(t >> 16) > r) goto decr;
alpar@1
   187
         if ((unsigned short)(t >> 16) < r) goto msub;
alpar@1
   188
         if ((unsigned short)t > x[i+m-2]) goto decr;
alpar@1
   189
msub:    /* now subtract divisor multiplied by the current digit of
alpar@1
   190
            quotient from the current dividend */
alpar@1
   191
         if (q == 0) goto putq;
alpar@1
   192
         t = 0;
alpar@1
   193
         for (j = 0; j < m; j++)
alpar@1
   194
         {  t += (unsigned int)y[j] * (unsigned int)q;
alpar@1
   195
            if (x[i+j] < (unsigned short)t) t += 0x10000;
alpar@1
   196
            x[i+j] -= (unsigned short)t;
alpar@1
   197
            t >>= 16;
alpar@1
   198
         }
alpar@1
   199
         if (x[i+m] >= (unsigned short)t) goto putq;
alpar@1
   200
         /* perform correcting addition, because the current digit of
alpar@1
   201
            quotient is greater by one than its correct value */
alpar@1
   202
         q--;
alpar@1
   203
         t = 0;
alpar@1
   204
         for (j = 0; j < m; j++)
alpar@1
   205
         {  t += (unsigned int)x[i+j] + (unsigned int)y[j];
alpar@1
   206
            x[i+j] = (unsigned short)t;
alpar@1
   207
            t >>= 16;
alpar@1
   208
         }
alpar@1
   209
putq:    /* store the current digit of quotient */
alpar@1
   210
         x[i+m] = q;
alpar@1
   211
      }
alpar@1
   212
      /* divide divisor and remainder by the normalizing coefficient in
alpar@1
   213
         order to restore their original values */
alpar@1
   214
      if (d > 1)
alpar@1
   215
      {  t = 0;
alpar@1
   216
         for (i = m-1; i >= 0; i--)
alpar@1
   217
         {  t = (t << 16) + (unsigned int)x[i];
alpar@1
   218
            x[i] = (unsigned short)(t / (unsigned int)d);
alpar@1
   219
            t %= (unsigned int)d;
alpar@1
   220
         }
alpar@1
   221
         t = 0;
alpar@1
   222
         for (j = m-1; j >= 0; j--)
alpar@1
   223
         {  t = (t << 16) + (unsigned int)y[j];
alpar@1
   224
            y[j] = (unsigned short)(t / (unsigned int)d);
alpar@1
   225
            t %= (unsigned int)d;
alpar@1
   226
         }
alpar@1
   227
      }
alpar@1
   228
done: return;
alpar@1
   229
}
alpar@1
   230
alpar@1
   231
/**********************************************************************/
alpar@1
   232
alpar@1
   233
#if 0
alpar@1
   234
#include <assert.h>
alpar@1
   235
#include <stdio.h>
alpar@1
   236
#include <stdlib.h>
alpar@1
   237
#include "glprng.h"
alpar@1
   238
alpar@1
   239
#define N_MAX 7
alpar@1
   240
/* maximal number of digits in multiplicand */
alpar@1
   241
alpar@1
   242
#define M_MAX 5
alpar@1
   243
/* maximal number of digits in multiplier */
alpar@1
   244
alpar@1
   245
#define N_TEST 1000000
alpar@1
   246
/* number of tests */
alpar@1
   247
alpar@1
   248
int main(void)
alpar@1
   249
{     RNG *rand;
alpar@1
   250
      int d, j, n, m, test;
alpar@1
   251
      unsigned short x[N_MAX], y[M_MAX], z[N_MAX+M_MAX];
alpar@1
   252
      rand = rng_create_rand();
alpar@1
   253
      for (test = 1; test <= N_TEST; test++)
alpar@1
   254
      {  /* x[0,...,n-1] := multiplicand */
alpar@1
   255
         n = 1 + rng_unif_rand(rand, N_MAX-1);
alpar@1
   256
         assert(1 <= n && n <= N_MAX);
alpar@1
   257
         for (j = 0; j < n; j++)
alpar@1
   258
         {  d = rng_unif_rand(rand, 65536);
alpar@1
   259
            assert(0 <= d && d <= 65535);
alpar@1
   260
            x[j] = (unsigned short)d;
alpar@1
   261
         }
alpar@1
   262
         /* y[0,...,m-1] := multiplier */
alpar@1
   263
         m = 1 + rng_unif_rand(rand, M_MAX-1);
alpar@1
   264
         assert(1 <= m && m <= M_MAX);
alpar@1
   265
         for (j = 0; j < m; j++)
alpar@1
   266
         {  d = rng_unif_rand(rand, 65536);
alpar@1
   267
            assert(0 <= d && d <= 65535);
alpar@1
   268
            y[j] = (unsigned short)d;
alpar@1
   269
         }
alpar@1
   270
         if (y[m-1] == 0) y[m-1] = 1;
alpar@1
   271
         /* z[0,...,n+m-1] := x * y */
alpar@1
   272
         for (j = 0; j < n; j++) z[m+j] = x[j];
alpar@1
   273
         bigmul(n, m, z, y);
alpar@1
   274
         /* z[0,...,m-1] := z mod y, z[m,...,n+m-1] := z div y */
alpar@1
   275
         bigdiv(n, m, z, y);
alpar@1
   276
         /* z mod y must be 0 */
alpar@1
   277
         for (j = 0; j < m; j++) assert(z[j] == 0);
alpar@1
   278
         /* z div y must be x */
alpar@1
   279
         for (j = 0; j < n; j++) assert(z[m+j] == x[j]);
alpar@1
   280
      }
alpar@1
   281
      fprintf(stderr, "%d tests successfully passed\n", N_TEST);
alpar@1
   282
      rng_delete_rand(rand);
alpar@1
   283
      return 0;
alpar@1
   284
}
alpar@1
   285
#endif
alpar@1
   286
alpar@1
   287
/* eof */