github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/math/big/arith.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file provides Go implementations of elementary multi-precision
     6  // arithmetic operations on word vectors. Needed for platforms without
     7  // assembly implementations of these routines.
     8  
     9  package big
    10  
    11  // A Word represents a single digit of a multi-precision unsigned integer.
    12  type Word uintptr
    13  
    14  const (
    15  	// Compute the size _S of a Word in bytes.
    16  	_m    = ^Word(0)
    17  	_logS = _m>>8&1 + _m>>16&1 + _m>>32&1
    18  	_S    = 1 << _logS
    19  
    20  	_W = _S << 3 // word size in bits
    21  	_B = 1 << _W // digit base
    22  	_M = _B - 1  // digit mask
    23  
    24  	_W2 = _W / 2   // half word size in bits
    25  	_B2 = 1 << _W2 // half digit base
    26  	_M2 = _B2 - 1  // half digit mask
    27  )
    28  
    29  // ----------------------------------------------------------------------------
    30  // Elementary operations on words
    31  //
    32  // These operations are used by the vector operations below.
    33  
    34  // z1<<_W + z0 = x+y+c, with c == 0 or 1
    35  func addWW_g(x, y, c Word) (z1, z0 Word) {
    36  	yc := y + c
    37  	z0 = x + yc
    38  	if z0 < x || yc < y {
    39  		z1 = 1
    40  	}
    41  	return
    42  }
    43  
    44  // z1<<_W + z0 = x-y-c, with c == 0 or 1
    45  func subWW_g(x, y, c Word) (z1, z0 Word) {
    46  	yc := y + c
    47  	z0 = x - yc
    48  	if z0 > x || yc < y {
    49  		z1 = 1
    50  	}
    51  	return
    52  }
    53  
    54  // z1<<_W + z0 = x*y
    55  // Adapted from Warren, Hacker's Delight, p. 132.
    56  func mulWW_g(x, y Word) (z1, z0 Word) {
    57  	x0 := x & _M2
    58  	x1 := x >> _W2
    59  	y0 := y & _M2
    60  	y1 := y >> _W2
    61  	w0 := x0 * y0
    62  	t := x1*y0 + w0>>_W2
    63  	w1 := t & _M2
    64  	w2 := t >> _W2
    65  	w1 += x0 * y1
    66  	z1 = x1*y1 + w2 + w1>>_W2
    67  	z0 = x * y
    68  	return
    69  }
    70  
    71  // z1<<_W + z0 = x*y + c
    72  func mulAddWWW_g(x, y, c Word) (z1, z0 Word) {
    73  	z1, zz0 := mulWW_g(x, y)
    74  	if z0 = zz0 + c; z0 < zz0 {
    75  		z1++
    76  	}
    77  	return
    78  }
    79  
    80  // Length of x in bits.
    81  func bitLen_g(x Word) (n int) {
    82  	for ; x >= 0x8000; x >>= 16 {
    83  		n += 16
    84  	}
    85  	if x >= 0x80 {
    86  		x >>= 8
    87  		n += 8
    88  	}
    89  	if x >= 0x8 {
    90  		x >>= 4
    91  		n += 4
    92  	}
    93  	if x >= 0x2 {
    94  		x >>= 2
    95  		n += 2
    96  	}
    97  	if x >= 0x1 {
    98  		n++
    99  	}
   100  	return
   101  }
   102  
   103  // log2 computes the integer binary logarithm of x.
   104  // The result is the integer n for which 2^n <= x < 2^(n+1).
   105  // If x == 0, the result is -1.
   106  func log2(x Word) int {
   107  	return bitLen(x) - 1
   108  }
   109  
   110  // nlz returns the number of leading zeros in x.
   111  func nlz(x Word) uint {
   112  	return uint(_W - bitLen(x))
   113  }
   114  
   115  // nlz64 returns the number of leading zeros in x.
   116  func nlz64(x uint64) uint {
   117  	switch _W {
   118  	case 32:
   119  		w := x >> 32
   120  		if w == 0 {
   121  			return 32 + nlz(Word(x))
   122  		}
   123  		return nlz(Word(w))
   124  	case 64:
   125  		return nlz(Word(x))
   126  	}
   127  	panic("unreachable")
   128  }
   129  
   130  // q = (u1<<_W + u0 - r)/y
   131  // Adapted from Warren, Hacker's Delight, p. 152.
   132  func divWW_g(u1, u0, v Word) (q, r Word) {
   133  	if u1 >= v {
   134  		return 1<<_W - 1, 1<<_W - 1
   135  	}
   136  
   137  	s := nlz(v)
   138  	v <<= s
   139  
   140  	vn1 := v >> _W2
   141  	vn0 := v & _M2
   142  	un32 := u1<<s | u0>>(_W-s)
   143  	un10 := u0 << s
   144  	un1 := un10 >> _W2
   145  	un0 := un10 & _M2
   146  	q1 := un32 / vn1
   147  	rhat := un32 - q1*vn1
   148  
   149  	for q1 >= _B2 || q1*vn0 > _B2*rhat+un1 {
   150  		q1--
   151  		rhat += vn1
   152  		if rhat >= _B2 {
   153  			break
   154  		}
   155  	}
   156  
   157  	un21 := un32*_B2 + un1 - q1*v
   158  	q0 := un21 / vn1
   159  	rhat = un21 - q0*vn1
   160  
   161  	for q0 >= _B2 || q0*vn0 > _B2*rhat+un0 {
   162  		q0--
   163  		rhat += vn1
   164  		if rhat >= _B2 {
   165  			break
   166  		}
   167  	}
   168  
   169  	return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s
   170  }
   171  
   172  // Keep for performance debugging.
   173  // Using addWW_g is likely slower.
   174  const use_addWW_g = false
   175  
   176  // The resulting carry c is either 0 or 1.
   177  func addVV_g(z, x, y []Word) (c Word) {
   178  	if use_addWW_g {
   179  		for i := range z {
   180  			c, z[i] = addWW_g(x[i], y[i], c)
   181  		}
   182  		return
   183  	}
   184  
   185  	for i, xi := range x[:len(z)] {
   186  		yi := y[i]
   187  		zi := xi + yi + c
   188  		z[i] = zi
   189  		// see "Hacker's Delight", section 2-12 (overflow detection)
   190  		c = (xi&yi | (xi|yi)&^zi) >> (_W - 1)
   191  	}
   192  	return
   193  }
   194  
   195  // The resulting carry c is either 0 or 1.
   196  func subVV_g(z, x, y []Word) (c Word) {
   197  	if use_addWW_g {
   198  		for i := range z {
   199  			c, z[i] = subWW_g(x[i], y[i], c)
   200  		}
   201  		return
   202  	}
   203  
   204  	for i, xi := range x[:len(z)] {
   205  		yi := y[i]
   206  		zi := xi - yi - c
   207  		z[i] = zi
   208  		// see "Hacker's Delight", section 2-12 (overflow detection)
   209  		c = (yi&^xi | (yi|^xi)&zi) >> (_W - 1)
   210  	}
   211  	return
   212  }
   213  
   214  // The resulting carry c is either 0 or 1.
   215  func addVW_g(z, x []Word, y Word) (c Word) {
   216  	if use_addWW_g {
   217  		c = y
   218  		for i := range z {
   219  			c, z[i] = addWW_g(x[i], c, 0)
   220  		}
   221  		return
   222  	}
   223  
   224  	c = y
   225  	for i, xi := range x[:len(z)] {
   226  		zi := xi + c
   227  		z[i] = zi
   228  		c = xi &^ zi >> (_W - 1)
   229  	}
   230  	return
   231  }
   232  
   233  func subVW_g(z, x []Word, y Word) (c Word) {
   234  	if use_addWW_g {
   235  		c = y
   236  		for i := range z {
   237  			c, z[i] = subWW_g(x[i], c, 0)
   238  		}
   239  		return
   240  	}
   241  
   242  	c = y
   243  	for i, xi := range x[:len(z)] {
   244  		zi := xi - c
   245  		z[i] = zi
   246  		c = (zi &^ xi) >> (_W - 1)
   247  	}
   248  	return
   249  }
   250  
   251  func shlVU_g(z, x []Word, s uint) (c Word) {
   252  	if n := len(z); n > 0 {
   253  		ŝ := _W - s
   254  		w1 := x[n-1]
   255  		c = w1 >> ŝ
   256  		for i := n - 1; i > 0; i-- {
   257  			w := w1
   258  			w1 = x[i-1]
   259  			z[i] = w<<s | w1>>ŝ
   260  		}
   261  		z[0] = w1 << s
   262  	}
   263  	return
   264  }
   265  
   266  func shrVU_g(z, x []Word, s uint) (c Word) {
   267  	if n := len(z); n > 0 {
   268  		ŝ := _W - s
   269  		w1 := x[0]
   270  		c = w1 << ŝ
   271  		for i := 0; i < n-1; i++ {
   272  			w := w1
   273  			w1 = x[i+1]
   274  			z[i] = w>>s | w1<<ŝ
   275  		}
   276  		z[n-1] = w1 >> s
   277  	}
   278  	return
   279  }
   280  
   281  func mulAddVWW_g(z, x []Word, y, r Word) (c Word) {
   282  	c = r
   283  	for i := range z {
   284  		c, z[i] = mulAddWWW_g(x[i], y, c)
   285  	}
   286  	return
   287  }
   288  
   289  // TODO(gri) Remove use of addWW_g here and then we can remove addWW_g and subWW_g.
   290  func addMulVVW_g(z, x []Word, y Word) (c Word) {
   291  	for i := range z {
   292  		z1, z0 := mulAddWWW_g(x[i], y, z[i])
   293  		c, z[i] = addWW_g(z0, c, 0)
   294  		c += z1
   295  	}
   296  	return
   297  }
   298  
   299  func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) {
   300  	r = xn
   301  	for i := len(z) - 1; i >= 0; i-- {
   302  		z[i], r = divWW_g(r, x[i], y)
   303  	}
   304  	return
   305  }