github.com/benz9527/toy-box/algo@v0.0.0-20240221120937-66c0c6bd5abd/bit/bit.go (about)

     1  package bit
     2  
     3  import (
     4  	"unsafe"
     5  )
     6  
     7  func RoundupPowOf2(target uint64) uint64 {
     8  	target--
     9  	target |= target >> 1
    10  	target |= target >> 2
    11  	target |= target >> 4
    12  	target |= target >> 8
    13  	target |= target >> 16
    14  	target |= target >> 32
    15  	target++
    16  	return target
    17  }
    18  
    19  // RoundupPowOf2ByLoop rounds up the target to the power of 2.
    20  // Plain thinking.
    21  func RoundupPowOf2ByLoop(target uint64) uint64 {
    22  	var result uint64 = 1
    23  	for result < target {
    24  		result <<= 1
    25  	}
    26  	return result
    27  }
    28  
    29  // RoundupPowOf2ByCeil rounds up the target to the power of 2.
    30  // Copy from linux kernel kfifo.
    31  func RoundupPowOf2ByCeil(target uint64) uint64 {
    32  	return 1 << CeilPowOf2(target)
    33  }
    34  
    35  // CeilPowOf2 get the ceil power of 2 of the target.
    36  // Copy from linux kernel kfifo.
    37  func CeilPowOf2(target uint64) uint8 {
    38  	target--
    39  	if target == 0 {
    40  		return 0
    41  	}
    42  	var pos uint8 = 64
    43  	if target&0xffffffff00000000 == 0 {
    44  		target = target << 32
    45  		pos -= 32
    46  	}
    47  	if target&0xffff000000000000 == 0 {
    48  		target <<= 16
    49  		pos -= 16
    50  	}
    51  	if target&0xff00000000000000 == 0 {
    52  		target <<= 8
    53  		pos -= 8
    54  	}
    55  	if target&0xf000000000000000 == 0 {
    56  		target <<= 4
    57  		pos -= 4
    58  	}
    59  	if target&0xc000000000000000 == 0 {
    60  		target <<= 2
    61  		pos -= 2
    62  	}
    63  	if target&0x8000000000000000 == 0 {
    64  		pos -= 1
    65  	}
    66  	return pos
    67  }
    68  
    69  // IsPowOf2 checks if the target is power of 2.
    70  // Copy from linux kernel kfifo.
    71  func IsPowOf2(target uint64) bool {
    72  	return target&(target-1) == 0
    73  }
    74  
    75  type Number interface {
    76  	~uint8 | ~uint16 | ~uint32 | ~uint64 | ~int8 | ~int16 | ~int32 | ~int64 | ~int
    77  }
    78  
    79  // HammingWeight counts the number of 1 bit in a number.
    80  type HammingWeight[T Number] func(n T) uint8
    81  type BitCount[T Number] HammingWeight[T]
    82  
    83  func convert[T Number](n T) uint64 {
    84  	var target uint64
    85  	switch unsafe.Sizeof(n) {
    86  	case 1:
    87  		target = uint64(*(*uint8)(unsafe.Pointer(&n)))
    88  	case 2:
    89  		target = uint64(*(*uint16)(unsafe.Pointer(&n)))
    90  	case 4:
    91  		target = uint64(*(*uint32)(unsafe.Pointer(&n)))
    92  	case 8:
    93  		target = *(*uint64)(unsafe.Pointer(&n))
    94  	}
    95  	return target
    96  }
    97  
    98  // variable-precision SWAR algorithm
    99  
   100  // HammingWeightBySWAR counts the number of 1 bit by group statistics.
   101  // Calculate the number of 1 bit by tree-like structure.
   102  // Example:
   103  // 0x55555555 = 01010101010101010101010101010101
   104  // It will keep the odd bits of the original number 1 and keep the even bits of the original number 1.
   105  // Every 2 binary bits represent the number of 1 in the corresponding binary bit of the original number.
   106  // 0x33333333 = 00110011001100110011001100110011
   107  // It will keep the right two bits of the sum of the previous step and keep the left two bits of the sum of the previous step.
   108  // Every 4 binary bits represent the number of 1 in the corresponding binary bit of the original number.
   109  // 0x0f0f0f0f = 00001111000011110000111100001111
   110  // It will keep the right four bits of the sum of the previous step and keep the left four bits of the sum of the previous step.
   111  // Every 8 binary bits represent the number of 1 in the corresponding binary bit of the original number.
   112  // 0x00ff00ff = 00000000111111110000000011111111
   113  // It will keep the right eight bits of the sum of the previous step and keep the left eight bits of the sum of the previous step.
   114  // Every 16 binary bits represent the number of 1 in the corresponding binary bit of the original number.
   115  // 0x0000ffff = 00000000000000001111111111111111
   116  // It will keep the right sixteen bits of the sum of the previous step and keep the left sixteen bits of the sum of the previous step.
   117  // Every 32 binary bits represent the number of 1 in the corresponding binary bit of the original number.
   118  func HammingWeightBySWAR[T Number](n T) uint8 {
   119  	_n := convert[T](n)
   120  	_n = (_n & 0x5555555555555555) + ((_n >> 1) & 0x5555555555555555)
   121  	_n = (_n & 0x3333333333333333) + ((_n >> 2) & 0x3333333333333333)
   122  	_n = (_n & 0x0f0f0f0f0f0f0f0f) + ((_n >> 4) & 0x0f0f0f0f0f0f0f0f)
   123  	_n = (_n & 0x00ff00ff00ff00ff) + ((_n >> 8) & 0x00ff00ff00ff00ff)
   124  	_n = (_n & 0x0000ffff0000ffff) + ((_n >> 16) & 0x0000ffff0000ffff)
   125  	_n = (_n & 0x00000000ffffffff) + ((_n >> 32) & 0x00000000ffffffff)
   126  	return uint8(_n)
   127  }
   128  
   129  // HammingWeightBySWAR2 counts the number of 1 bit by group statistics.
   130  // Example:
   131  // 7 = (0111)2
   132  // step 1:
   133  // 0x7 & 0x55555555 = 0x5
   134  // 0x7 >> 1 = 0x3, 0x3 & 0x55555555 = 0x1
   135  // 0x5 + 0x1 = 0x6
   136  // step 2:
   137  // 0x6 & 0x33333333 = 0x2
   138  // 0x6 >> 2 = 0x1, 0x1 & 0x33333333 = 0x1
   139  // 0x2 + 0x1 = 0x3
   140  // step 3:
   141  // 0x3 & 0x0f0f0f0f = 0x3
   142  // 0x3 >> 4 = 0x0, 0x0 & 0x0f0f0f0f = 0x0
   143  // 0x3 + 0x0 = 0x3
   144  // step 4:
   145  // 0x3 * 0x01010101 = 0x03030303
   146  // 0x03030303 & 0x3fffffff = 0x03030303
   147  // 0x03030303 >> 24 = 0x3
   148  func HammingWeightBySWAR2[T Number](n T) uint8 {
   149  	_n := convert[T](n)
   150  	_n = (_n & 0x5555555555555555) + ((_n >> 1) & 0x5555555555555555)
   151  	_n = (_n & 0x3333333333333333) + ((_n >> 2) & 0x3333333333333333)
   152  	_n = (_n & 0x0f0f0f0f0f0f0f0f) + ((_n >> 4) & 0x0f0f0f0f0f0f0f0f)
   153  	// 8 bits quick multiply
   154  	// 0x01010101 = 00000001 00000001 00000001 00000001
   155  	//            = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0
   156  	// i * 0x01010101 = i << 24 + i << 16 + i << 8 + i << 0
   157  	// Merge
   158  	// (i * 0x01010101)>>24 = (i<<24)>>24 + (i<<16)>>24 + (i<<8)>>24 + (i<<0)>>24
   159  	// Hamming Weight
   160  	_n = ((_n * 0x0101010101010101) & ((1 << 64) - 1)) >> 56
   161  	return uint8(_n)
   162  }
   163  
   164  // HammingWeightBySWAR3 counts the number of 1 bit by group statistics.
   165  func HammingWeightBySWAR3[T Number](n T) uint8 {
   166  	_n := convert[T](n)
   167  	bits := func(num uint8) uint8 {
   168  		remainder := num&0x5 + (num>>1)&0x5
   169  		return remainder&0x3 + (remainder>>2)&0x3
   170  	}
   171  	res := uint8(0)
   172  	for i := 0; i < 16; i++ {
   173  		res += bits(uint8((_n >> (i * 4)) & 0xf))
   174  	}
   175  	return res
   176  }
   177  
   178  var (
   179  	bitCount = [16]uint8{
   180  		0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
   181  	}
   182  )
   183  
   184  func HammingWeightByGroupCount[T Number](n T) uint8 {
   185  	_n := convert[T](n)
   186  	res := uint8(0)
   187  	for i := 0; i < 16; i++ {
   188  		res += bitCount[uint8((_n>>(i*4))&0xf)]
   189  	}
   190  	return res
   191  }