github.com/primecitizens/pcz/std@v0.2.1/core/bits/arith.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  //
     4  // Copyright 2017 The Go Authors. All rights reserved.
     5  // Use of this source code is governed by a BSD-style
     6  // license that can be found in the LICENSE file.
     7  
     8  package bits
     9  
    10  import (
    11  	"github.com/primecitizens/pcz/std/core/arch"
    12  	"github.com/primecitizens/pcz/std/core/assert"
    13  )
    14  
    15  // Add returns the sum with carry of x, y and carry: sum = x + y + carry.
    16  // The carry input must be 0 or 1; otherwise the behavior is undefined.
    17  // The carryOut output is guaranteed to be 0 or 1.
    18  //
    19  // This function's execution time does not depend on the inputs.
    20  func Add(x, y, carry uint) (sum, carryOut uint) {
    21  	if arch.UintBits == 32 {
    22  		s32, c32 := Add32(uint32(x), uint32(y), uint32(carry))
    23  		return uint(s32), uint(c32)
    24  	}
    25  	s64, c64 := Add64(uint64(x), uint64(y), uint64(carry))
    26  	return uint(s64), uint(c64)
    27  }
    28  
    29  // Add32 returns the sum with carry of x, y and carry: sum = x + y + carry.
    30  // The carry input must be 0 or 1; otherwise the behavior is undefined.
    31  // The carryOut output is guaranteed to be 0 or 1.
    32  //
    33  // This function's execution time does not depend on the inputs.
    34  func Add32(x, y, carry uint32) (sum, carryOut uint32) {
    35  	sum64 := uint64(x) + uint64(y) + uint64(carry)
    36  	sum = uint32(sum64)
    37  	carryOut = uint32(sum64 >> 32)
    38  	return
    39  }
    40  
    41  // Add64 returns the sum with carry of x, y and carry: sum = x + y + carry.
    42  // The carry input must be 0 or 1; otherwise the behavior is undefined.
    43  // The carryOut output is guaranteed to be 0 or 1.
    44  //
    45  // This function's execution time does not depend on the inputs.
    46  func Add64(x, y, carry uint64) (sum, carryOut uint64) {
    47  	sum = x + y + carry
    48  	// The sum will overflow if both top bits are set (x & y) or if one of them
    49  	// is (x | y), and a carry from the lower place happened. If such a carry
    50  	// happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum).
    51  	carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
    52  	return
    53  }
    54  
    55  // Sub returns the difference of x, y and borrow: diff = x - y - borrow.
    56  // The borrow input must be 0 or 1; otherwise the behavior is undefined.
    57  // The borrowOut output is guaranteed to be 0 or 1.
    58  //
    59  // This function's execution time does not depend on the inputs.
    60  func Sub(x, y, borrow uint) (diff, borrowOut uint) {
    61  	if arch.UintBits == 32 {
    62  		d32, b32 := Sub32(uint32(x), uint32(y), uint32(borrow))
    63  		return uint(d32), uint(b32)
    64  	}
    65  	d64, b64 := Sub64(uint64(x), uint64(y), uint64(borrow))
    66  	return uint(d64), uint(b64)
    67  }
    68  
    69  // Sub32 returns the difference of x, y and borrow, diff = x - y - borrow.
    70  // The borrow input must be 0 or 1; otherwise the behavior is undefined.
    71  // The borrowOut output is guaranteed to be 0 or 1.
    72  //
    73  // This function's execution time does not depend on the inputs.
    74  func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) {
    75  	diff = x - y - borrow
    76  	// The difference will underflow if the top bit of x is not set and the top
    77  	// bit of y is set (^x & y) or if they are the same (^(x ^ y)) and a borrow
    78  	// from the lower place happens. If that borrow happens, the result will be
    79  	// 1 - 1 - 1 = 0 - 0 - 1 = 1 (& diff).
    80  	borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 31
    81  	return
    82  }
    83  
    84  // Sub64 returns the difference of x, y and borrow: diff = x - y - borrow.
    85  // The borrow input must be 0 or 1; otherwise the behavior is undefined.
    86  // The borrowOut output is guaranteed to be 0 or 1.
    87  //
    88  // This function's execution time does not depend on the inputs.
    89  func Sub64(x, y, borrow uint64) (diff, borrowOut uint64) {
    90  	diff = x - y - borrow
    91  	// See Sub32 for the bit logic.
    92  	borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63
    93  	return
    94  }
    95  
    96  // --- Full-width multiply ---
    97  
    98  // Mul returns the full-width product of x and y: (hi, lo) = x * y
    99  // with the product bits' upper half returned in hi and the lower
   100  // half returned in lo.
   101  //
   102  // This function's execution time does not depend on the inputs.
   103  func Mul(x, y uint) (hi, lo uint) {
   104  	if arch.UintBits == 32 {
   105  		h, l := Mul32(uint32(x), uint32(y))
   106  		return uint(h), uint(l)
   107  	}
   108  	h, l := Mul64(uint64(x), uint64(y))
   109  	return uint(h), uint(l)
   110  }
   111  
   112  // Mul32 returns the 64-bit product of x and y: (hi, lo) = x * y
   113  // with the product bits' upper half returned in hi and the lower
   114  // half returned in lo.
   115  //
   116  // This function's execution time does not depend on the inputs.
   117  func Mul32(x, y uint32) (hi, lo uint32) {
   118  	tmp := uint64(x) * uint64(y)
   119  	hi, lo = uint32(tmp>>32), uint32(tmp)
   120  	return
   121  }
   122  
   123  // Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y
   124  // with the product bits' upper half returned in hi and the lower
   125  // half returned in lo.
   126  //
   127  // This function's execution time does not depend on the inputs.
   128  func Mul64(x, y uint64) (hi, lo uint64) {
   129  	const mask32 = 1<<32 - 1
   130  	x0 := x & mask32
   131  	x1 := x >> 32
   132  	y0 := y & mask32
   133  	y1 := y >> 32
   134  	w0 := x0 * y0
   135  	t := x1*y0 + w0>>32
   136  	w1 := t & mask32
   137  	w2 := t >> 32
   138  	w1 += x0 * y1
   139  	hi = x1*y1 + w2 + w1>>32
   140  	lo = x * y
   141  	return
   142  }
   143  
   144  // --- Full-width divide ---
   145  
   146  // Div returns the quotient and remainder of (hi, lo) divided by y:
   147  // quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper
   148  // half in parameter hi and the lower half in parameter lo.
   149  // Div panics for y == 0 (division by zero) or y <= hi (quotient overflow).
   150  func Div(hi, lo, y uint) (quo, rem uint) {
   151  	if arch.UintBits == 32 {
   152  		q, r := Div32(uint32(hi), uint32(lo), uint32(y))
   153  		return uint(q), uint(r)
   154  	}
   155  	q, r := Div64(uint64(hi), uint64(lo), uint64(y))
   156  	return uint(q), uint(r)
   157  }
   158  
   159  // Div32 returns the quotient and remainder of (hi, lo) divided by y:
   160  // quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper
   161  // half in parameter hi and the lower half in parameter lo.
   162  // Div32 panics for y == 0 (division by zero) or y <= hi (quotient overflow).
   163  func Div32(hi, lo, y uint32) (quo, rem uint32) {
   164  	if y != 0 && y <= hi {
   165  		assert.Panic(ErrOverflow{})
   166  	}
   167  	z := uint64(hi)<<32 | uint64(lo)
   168  	quo, rem = uint32(z/uint64(y)), uint32(z%uint64(y))
   169  	return
   170  }
   171  
   172  // Div64 returns the quotient and remainder of (hi, lo) divided by y:
   173  // quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper
   174  // half in parameter hi and the lower half in parameter lo.
   175  // Div64 panics for y == 0 (division by zero) or y <= hi (quotient overflow).
   176  func Div64(hi, lo, y uint64) (quo, rem uint64) {
   177  	if y == 0 {
   178  		assert.Panic(ErrDivideByZero{})
   179  	}
   180  	if y <= hi {
   181  		assert.Panic(ErrOverflow{})
   182  	}
   183  
   184  	// If high part is zero, we can directly return the results.
   185  	if hi == 0 {
   186  		return lo / y, lo % y
   187  	}
   188  
   189  	s := uint(LeadingZeros64(y))
   190  	y <<= s
   191  
   192  	const (
   193  		two32  = 1 << 32
   194  		mask32 = two32 - 1
   195  	)
   196  	yn1 := y >> 32
   197  	yn0 := y & mask32
   198  	un32 := hi<<s | lo>>(64-s)
   199  	un10 := lo << s
   200  	un1 := un10 >> 32
   201  	un0 := un10 & mask32
   202  	q1 := un32 / yn1
   203  	rhat := un32 - q1*yn1
   204  
   205  	for q1 >= two32 || q1*yn0 > two32*rhat+un1 {
   206  		q1--
   207  		rhat += yn1
   208  		if rhat >= two32 {
   209  			break
   210  		}
   211  	}
   212  
   213  	un21 := un32*two32 + un1 - q1*y
   214  	q0 := un21 / yn1
   215  	rhat = un21 - q0*yn1
   216  
   217  	for q0 >= two32 || q0*yn0 > two32*rhat+un0 {
   218  		q0--
   219  		rhat += yn1
   220  		if rhat >= two32 {
   221  			break
   222  		}
   223  	}
   224  
   225  	return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s
   226  }
   227  
   228  // Rem returns the remainder of (hi, lo) divided by y. Rem panics for
   229  // y == 0 (division by zero) but, unlike Div, it doesn't panic on a
   230  // quotient overflow.
   231  func Rem(hi, lo, y uint) uint {
   232  	if arch.UintBits == 32 {
   233  		return uint(Rem32(uint32(hi), uint32(lo), uint32(y)))
   234  	}
   235  	return uint(Rem64(uint64(hi), uint64(lo), uint64(y)))
   236  }
   237  
   238  // Rem32 returns the remainder of (hi, lo) divided by y. Rem32 panics
   239  // for y == 0 (division by zero) but, unlike Div32, it doesn't panic
   240  // on a quotient overflow.
   241  func Rem32(hi, lo, y uint32) uint32 {
   242  	return uint32((uint64(hi)<<32 | uint64(lo)) % uint64(y))
   243  }
   244  
   245  // Rem64 returns the remainder of (hi, lo) divided by y. Rem64 panics
   246  // for y == 0 (division by zero) but, unlike Div64, it doesn't panic
   247  // on a quotient overflow.
   248  func Rem64(hi, lo, y uint64) uint64 {
   249  	// We scale down hi so that hi < y, then use Div64 to compute the
   250  	// rem with the guarantee that it won't panic on quotient overflow.
   251  	// Given that
   252  	//   hi ≡ hi%y    (mod y)
   253  	// we have
   254  	//   hi<<64 + lo ≡ (hi%y)<<64 + lo    (mod y)
   255  	_, rem := Div64(hi%y, lo, y)
   256  	return rem
   257  }