github.com/zhiqiangxu/util@v0.0.0-20230112053021-0a7aee056cd5/math/lambda.go (about)

     1  package math
     2  
     3  // FYI : https://www.math.sinica.edu.tw/www/file_upload/summer/crypt2017/data/2015/[20150707][carmichael].pdf
     4  
     5  func Lambda(n int) int {
     6  	if n == 0 {
     7  		panic("invalid input")
     8  	}
     9  	if n == 1 {
    10  		return 1
    11  	}
    12  
    13  	factors := PrimeFactors(n)
    14  
    15  	var parts []int
    16  	for prime, power := range factors {
    17  		switch {
    18  		case prime == 2:
    19  			if power == 1 || power == 2 {
    20  				parts = append(parts, PowInts(2, power-1))
    21  			} else {
    22  				parts = append(parts, PowInts(2, power-2))
    23  			}
    24  		default:
    25  			parts = append(parts, PowInts(prime, power)-PowInts(prime, power-1))
    26  		}
    27  	}
    28  
    29  	switch {
    30  	case len(parts) == 1:
    31  		return parts[0]
    32  	case len(parts) == 2:
    33  		return LCM(parts[0], parts[1])
    34  	default:
    35  		return LCM(parts[0], parts[1], parts[2:]...)
    36  	}
    37  }
    38  
    39  // greatest common divisor (GCD) via Euclidean algorithm
    40  func GCD(a, b int) int {
    41  	for b != 0 {
    42  		t := b
    43  		b = a % b
    44  		a = t
    45  	}
    46  	return a
    47  }
    48  
    49  // find Least Common Multiple (LCM) via GCD
    50  func LCM(a, b int, integers ...int) int {
    51  	result := a * b / GCD(a, b)
    52  
    53  	for i := 0; i < len(integers); i++ {
    54  		result = LCM(result, integers[i])
    55  	}
    56  
    57  	return result
    58  }
    59  
    60  // Assumption: n >= 0
    61  func PowInts(x, n int) int {
    62  	if n == 0 {
    63  		return 1
    64  	}
    65  
    66  	v := 1
    67  	for n != 0 {
    68  		if n&1 == 1 {
    69  			v *= x
    70  		}
    71  		x *= x
    72  		n /= 2
    73  	}
    74  
    75  	return v
    76  }
    77  
    78  // Get all prime factors of a given number n
    79  func PrimeFactors(n int) (pfs map[int]int) {
    80  	// Get the number of 2s that divide n
    81  
    82  	pfs = make(map[int]int)
    83  	for n%2 == 0 {
    84  		pfs[2] += 1
    85  		n = n / 2
    86  	}
    87  
    88  	// n must be odd at this point. so we can skip one element
    89  	// (note i = i + 2)
    90  	for i := 3; i*i <= n; i = i + 2 {
    91  		// while i divides n, append i and divide n
    92  		for n%i == 0 {
    93  			pfs[i] += 1
    94  			n = n / i
    95  		}
    96  	}
    97  
    98  	// This condition is to handle the case when n is a prime number
    99  	// greater than 2
   100  	if n > 2 {
   101  		pfs[n] += 1
   102  	}
   103  
   104  	return
   105  }