github.com/cznic/mathutil@v0.0.0-20181122101859-297441e03548/int.go (about)

     1  // Copyright (c) 2018 The mathutil 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  package mathutil
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/big"
    11  )
    12  
    13  var (
    14  	// The maximun Int128 value.
    15  	MaxInt128 *big.Int
    16  	// The minimun Int128 value.
    17  	MinInt128 *big.Int
    18  )
    19  
    20  func init() {
    21  	MaxInt128 = big.NewInt(0)
    22  	MaxInt128.SetBit(MaxInt128, 127, 1)
    23  	MaxInt128.Sub(MaxInt128, _1)
    24  	MinInt128 = big.NewInt(0)
    25  	MinInt128.Set(MaxInt128)
    26  	MinInt128.Add(MinInt128, _1)
    27  	MinInt128.Neg(MinInt128)
    28  }
    29  
    30  // Int128 is an 128 bit integer.
    31  type Int128 struct {
    32  	Lo int64 // Bits 63..0.
    33  	Hi int64 // Bits 127..64.
    34  }
    35  
    36  // Add returns the sum of x and y and a carry indication.
    37  func (x Int128) Add(y Int128) (r Int128, cy bool) {
    38  	r.Lo = x.Lo + y.Lo
    39  	r.Hi = x.Hi + y.Hi
    40  	if uint64(r.Lo) < uint64(x.Lo) {
    41  		r.Hi++
    42  	}
    43  	return r, (r.Cmp(x) < 0) == (y.Sign() >= 0)
    44  }
    45  
    46  // BigInt returns x in the form of a big.Int.
    47  func (x Int128) BigInt() *big.Int {
    48  	r := big.NewInt(x.Hi)
    49  	r.Lsh(r, 64)
    50  	lo := big.NewInt(0)
    51  	lo.SetUint64(uint64(x.Lo))
    52  	return r.Add(r, lo)
    53  }
    54  
    55  // Cmp compares x and y and returns:
    56  //
    57  //	-1 if x <  y
    58  //	 0 if x == y
    59  //	+1 if x >  y
    60  func (x Int128) Cmp(y Int128) int {
    61  	if x.Hi > y.Hi {
    62  		return 1
    63  	}
    64  
    65  	if x.Hi < y.Hi {
    66  		return -1
    67  	}
    68  
    69  	if uint64(x.Lo) > uint64(y.Lo) {
    70  		return 1
    71  	}
    72  
    73  	if uint64(x.Lo) < uint64(y.Lo) {
    74  		return -1
    75  	}
    76  
    77  	return 0
    78  }
    79  
    80  // Neg returns -x and an indication that x was not equal to MinInt128.
    81  func (x Int128) Neg() (r Int128, ok bool) {
    82  	if x == (Int128{Hi: math.MinInt64}) {
    83  		return x, false
    84  	}
    85  
    86  	x.Lo = ^x.Lo
    87  	x.Hi = ^x.Hi
    88  	r, _ = x.Add(Int128{Lo: 1})
    89  	return r, true
    90  }
    91  
    92  // SetBigInt sets x to y, returns x and an error, if any.
    93  func (x *Int128) SetBigInt(y *big.Int) (r Int128, err error) {
    94  	if y.Cmp(MaxInt128) > 0 {
    95  		return *x, fmt.Errorf("%T.SetInt: overflow", x)
    96  	}
    97  	if y.Cmp(MinInt128) < 0 {
    98  		return *x, fmt.Errorf("%T.SetInt: underflow", x)
    99  	}
   100  	neg := y.Sign() < 0
   101  	var z big.Int
   102  	z.Set(y)
   103  	if neg {
   104  		z.Neg(&z)
   105  	}
   106  	r.Lo = z.Int64()
   107  	z.Rsh(&z, 64)
   108  	r.Hi = z.Int64()
   109  	if neg {
   110  		r, _ = r.Neg()
   111  	}
   112  	*x = r
   113  	return r, nil
   114  }
   115  
   116  // SetInt64 sets x to y and returns x.
   117  func (x *Int128) SetInt64(y int64) (r Int128) {
   118  	r.Lo = y
   119  	if y >= 0 {
   120  		r.Hi = 0
   121  		*x = r
   122  		return r
   123  	}
   124  
   125  	r.Hi = -1
   126  	*x = r
   127  	return r
   128  }
   129  
   130  // SetInt64 sets x to y and returns x.
   131  func (x *Int128) SetUint64(y uint64) (r Int128) {
   132  	r = Int128{Lo: int64(y)}
   133  	*x = r
   134  	return r
   135  }
   136  
   137  // Sign returns:
   138  //
   139  //	-1 if x <  0
   140  //	 0 if x == 0
   141  //	+1 if x >  0
   142  func (x Int128) Sign() int {
   143  	if x.Hi < 0 {
   144  		return -1
   145  	}
   146  
   147  	if x.Hi != 0 || x.Lo != 0 {
   148  		return 1
   149  	}
   150  
   151  	return 0
   152  }
   153  
   154  // String implements fmt.Stringer()
   155  func (x Int128) String() string { return x.BigInt().String() }