Initial commit.
This commit is contained in:
		
							
								
								
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | MIT License | ||||||
|  |  | ||||||
|  | Copyright (c) 2025 smariot | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | # Golang Prime Numbers Package | ||||||
|  |  | ||||||
|  | A bunch of helper functions for dealing with small prime numbers that would fit in an unsigned integer. | ||||||
|  |  | ||||||
|  | This isn't a serious scientifically minded package, or heavily optimized, it's just a quick and dirty tool | ||||||
|  | for grabbing prime numbers should you find yourself needing them. | ||||||
|  |  | ||||||
|  | ## Installation | ||||||
|  |  | ||||||
|  | ```bash | ||||||
|  | go get smariot.com/prime | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Documentation | ||||||
|  |  | ||||||
|  | You can find the documentation at https://pkg.go.dev/smariot.com/prime. | ||||||
							
								
								
									
										475
									
								
								prime.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								prime.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,475 @@ | |||||||
|  | // A bunch of helper functions for dealing with small prime numbers that would fit in an unsigned integer. | ||||||
|  | // | ||||||
|  | // This isn't a serious scientifically minded package, or heavily optimized, it's just a quick and dirty tool | ||||||
|  | // for grabbing prime numbers should you find yourself needing them. | ||||||
|  | package prime | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"iter" | ||||||
|  | 	"math" | ||||||
|  | 	"slices" | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// initialize nextToCheck to the first odd prime number. | ||||||
|  | 	nextToCheck uint = 3 | ||||||
|  |  | ||||||
|  | 	// initialize the list to 2 since this value otherwise can't be reached by incrementing by 2 by an odd starting point. | ||||||
|  | 	primes = []uint{2} | ||||||
|  |  | ||||||
|  | 	// protect the above two variables, which are modified primarily by the functions [upTo] and [firstN]. | ||||||
|  | 	// the test package also has a [resetCache] function to reinitialize them. | ||||||
|  | 	m sync.RWMutex | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// this _must_ be larger than 1, because we assume the last item in the list to be odd when we change how switch how we generate these numbers. | ||||||
|  | 	generateLimit = 1 << 16 | ||||||
|  |  | ||||||
|  | 	// how many new primes to request at a time. | ||||||
|  | 	generateStep = 1 << 8 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // returns a sub-slice of primes that could potentially be a factor of n; primes must contain all the prime numbers up to sqrt(n) in ascending order. | ||||||
|  | func possibleFactors(primes []uint, n uint) []uint { | ||||||
|  | 	i, found := slices.BinarySearch(primes, uint(math.Sqrt(float64(n)))) | ||||||
|  | 	if found { | ||||||
|  | 		i++ | ||||||
|  | 	} | ||||||
|  | 	return primes[:i:i] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // returns true if n is prime; primes must contain all the prime numbers up to sqrt(n) in ascending order | ||||||
|  | func test(primes []uint, n uint) bool { | ||||||
|  | 	if len(primes) > 0 && n <= primes[len(primes)-1] { | ||||||
|  | 		// if the number to test would be inside the list of primes, then check the list using a binary search. | ||||||
|  | 		_, found := slices.BinarySearch(primes, n) | ||||||
|  | 		return found | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if n < 2 { | ||||||
|  | 		// 0 and 1 won't have any prime factors and would otherwise get classified as prime. | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// otherwise, test if it's prime by checking against its prime factors. | ||||||
|  | 	for _, factor := range possibleFactors(primes, n) { | ||||||
|  | 		if n%factor == 0 { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // returns a slice containing all the primes up to n inclusive (and possibly more than that). | ||||||
|  | func upTo(n uint) []uint { | ||||||
|  | 	m.RLock() | ||||||
|  | 	if n < nextToCheck || nextToCheck == 1 { | ||||||
|  | 		primes := primes | ||||||
|  | 		m.RUnlock() | ||||||
|  | 		return primes | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m.RUnlock() | ||||||
|  | 	m.Lock() | ||||||
|  | 	defer m.Unlock() | ||||||
|  |  | ||||||
|  | 	for ; n >= nextToCheck && nextToCheck != 1; nextToCheck += 2 { | ||||||
|  | 		if test(primes, nextToCheck) { | ||||||
|  | 			primes = append(primes, nextToCheck) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return primes | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // returns a slice containing the first n primes (and possibly more than that), | ||||||
|  | // and also possibly less if the nth prime can't fit in a uint. | ||||||
|  | func firstN(n int) []uint { | ||||||
|  | 	m.RLock() | ||||||
|  | 	if n <= len(primes) || nextToCheck == 1 { | ||||||
|  | 		primes := primes | ||||||
|  | 		m.RUnlock() | ||||||
|  | 		return primes | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m.RUnlock() | ||||||
|  | 	m.Lock() | ||||||
|  | 	defer m.Unlock() | ||||||
|  |  | ||||||
|  | 	for ; n > len(primes) && nextToCheck != 1; nextToCheck += 2 { | ||||||
|  | 		if test(primes, nextToCheck) { | ||||||
|  | 			primes = append(primes, nextToCheck) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return primes | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // FirstN returns a slice containing the first n prime numbers in ascending order. | ||||||
|  | // | ||||||
|  | // The returned slice should be considered read-only and must not be modified. | ||||||
|  | // | ||||||
|  | // The returned slice might contain less than n elements if those numbers wouldn't fit in a uint. | ||||||
|  | func FirstN(n int) []uint { | ||||||
|  | 	return firstN(n)[:n:n] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Test returns true if n is prime. | ||||||
|  | func Test(n uint) bool { | ||||||
|  | 	return test(upTo(uint(math.Sqrt(float64(n)))), n) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Next returns the prime number at some relative offset to n, or 0 if there is no applicable number. | ||||||
|  | // | ||||||
|  | // Next(n, 0) will return n if n is prime, otherwise 0. | ||||||
|  | // Next(n, -1) will return the next prime number before n, if one exists (i.e., n >= 3) | ||||||
|  | // Next(n, 1) will return the next prime number after n, if representable in a uint. | ||||||
|  | // Next(n, 100) will return the 100th prime number after n, if representable in a uint. | ||||||
|  | func Next(n uint, offset int) uint { | ||||||
|  | 	primes := upTo(uint(math.Sqrt(float64(n)))) | ||||||
|  |  | ||||||
|  | 	// if the list of primes already contains our number (upTo may return more than requested), possibly we can do this based on indexes alone. | ||||||
|  | 	if primes[len(primes)-1] >= n { | ||||||
|  | 		i, found := slices.BinarySearch(primes, n) | ||||||
|  |  | ||||||
|  | 		if !found { | ||||||
|  | 			if offset == 0 { | ||||||
|  | 				return 0 | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			n = primes[i] | ||||||
|  |  | ||||||
|  | 			if offset > 0 { | ||||||
|  | 				offset-- | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if offset <= 0 { | ||||||
|  | 			if i+offset >= 0 && i+offset <= i { | ||||||
|  | 				return primes[i+offset] | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// there is definitely nothing before this, stop now. | ||||||
|  | 			return 0 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// offset > 0 | ||||||
|  | 		if i+offset > i && i+offset < len(primes) { | ||||||
|  | 			// we know exactly which prime is at this offset. | ||||||
|  | 			return primes[i+offset] | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// the list doesn't contain a value for primes[offset+i], | ||||||
|  | 		// so adjust n to the last prime in the list, and decrease offset by the number of numbers we skipped over. | ||||||
|  | 		n = primes[len(primes)-1] | ||||||
|  | 		offset -= len(primes) - 1 - i | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch { | ||||||
|  | 	// is n even? | ||||||
|  | 	case n%2 == 0: | ||||||
|  | 		switch { | ||||||
|  | 		case offset < 0: | ||||||
|  | 			if n <= 2 { | ||||||
|  | 				// n=0 or n=2, either way there are no primes before them. | ||||||
|  | 				// note: this path isn't normally encountered because the the primes list generally always contains | ||||||
|  | 				// at least 2, and this situation will have been handled via direct index calculation. | ||||||
|  | 				return 0 | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// decrease to make n odd. | ||||||
|  | 			n-- | ||||||
|  | 		case offset > 0: | ||||||
|  | 			// increase to make n odd. | ||||||
|  | 			n++ | ||||||
|  | 		default: | ||||||
|  | 			if n == 2 { | ||||||
|  | 				// the only even prime number. | ||||||
|  | 				// note: this path isn't normally encountered because the the primes list generally always contains | ||||||
|  | 				// at least 2, and this situation will have been handled via direct index calculation. | ||||||
|  | 				return 2 | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// some other even number, not prime. | ||||||
|  | 			return 0 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// if n is already prime, we need to skip over it. | ||||||
|  | 	case test(primes, n): | ||||||
|  | 		switch { | ||||||
|  | 		case offset < 0: | ||||||
|  | 			switch { | ||||||
|  | 			case n == 3: | ||||||
|  | 				if offset == -1 { | ||||||
|  | 					return 2 | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				fallthrough | ||||||
|  | 			case n == 2: | ||||||
|  | 				return 0 | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			n -= 2 | ||||||
|  | 		case offset > 0: | ||||||
|  | 			n += 2 | ||||||
|  | 		default: | ||||||
|  | 			return n | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// at this point, offset will be signed, and n will be an odd number greater or equal to 3. | ||||||
|  | 	if offset == 0 || n < 3 || n%2 == 0 { | ||||||
|  | 		panic(fmt.Sprintf("n=%d offset=%d", n, offset)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if offset < 0 { | ||||||
|  | 		// count backwards | ||||||
|  |  | ||||||
|  | 		for ; n != 1; n -= 2 { | ||||||
|  | 			if test(primes, n) { | ||||||
|  | 				offset++ | ||||||
|  |  | ||||||
|  | 				if offset == 0 { | ||||||
|  | 					return n | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// n=1, meaning n=2 was skipped due to decrementing by 2 each time. | ||||||
|  | 		// if offset = -1, then return the 2 we skipped. | ||||||
|  | 		if offset == -1 { | ||||||
|  | 			return 2 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// count forwards | ||||||
|  | 	listLegalTo := uintSqr(primes[len(primes)-1]) | ||||||
|  |  | ||||||
|  | 	for ; n != 1; n += 2 { | ||||||
|  | 		for n > listLegalTo { | ||||||
|  | 			primes = firstN(len(primes) + generateStep) | ||||||
|  | 			listLegalTo = uintSqr(primes[len(primes)-1]) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if test(primes, n) { | ||||||
|  | 			offset-- | ||||||
|  |  | ||||||
|  | 			if offset == 0 { | ||||||
|  | 				return n | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// integer overflow happened, the remaining offset primes aren't representable via uint. | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpTo returns a slice containing all the up to n inclusive in ascending order. | ||||||
|  | // | ||||||
|  | // The returned slice should be considered read-only and must not be modified. | ||||||
|  | func UpTo(n uint) []uint { | ||||||
|  | 	primes := upTo(n) | ||||||
|  | 	// note that we don't add 1 here like we do in [possibleFactors], | ||||||
|  | 	// as n can be [math.MaxUint] here, which would overflow. | ||||||
|  | 	// instead, we add 1 if the requested number was found in the list. | ||||||
|  | 	i, found := slices.BinarySearch(primes, n) | ||||||
|  | 	if found { | ||||||
|  | 		i++ | ||||||
|  | 	} | ||||||
|  | 	return primes[:i:i] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // squares a, returning [math.MaxUint] if the operation would overflow. | ||||||
|  | func uintSqr(a uint) uint { | ||||||
|  | 	if a > 0 && a > math.MaxUint/a { | ||||||
|  | 		// if squaring would overflow, return MaxUint. | ||||||
|  | 		return math.MaxUint | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return a * a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // All returns an iterator yielding all the prime numbers up to [math.MaxUint] in ascending order. | ||||||
|  | func All() iter.Seq[uint] { | ||||||
|  | 	return func(yield func(uint) bool) { | ||||||
|  | 		var primes []uint | ||||||
|  |  | ||||||
|  | 		for i := 0; ; i++ { | ||||||
|  | 			if i == len(primes) { | ||||||
|  | 				if i >= generateLimit { | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				primes = firstN(min(i+generateStep, generateLimit)) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if !yield(primes[i]) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		listLegalTo := uintSqr(primes[len(primes)-1]) | ||||||
|  |  | ||||||
|  | 		// nextCandidate _should_ be odd, so it should be equal to 1 when it overflows. | ||||||
|  | 		for nextCandidate := primes[len(primes)-1] + 2; nextCandidate != 1; nextCandidate += 2 { | ||||||
|  | 			if nextCandidate > listLegalTo { | ||||||
|  | 				// need to grow the list. | ||||||
|  | 				primes = firstN(len(primes) + generateStep) | ||||||
|  | 				listLegalTo = uintSqr(primes[len(primes)-1]) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if test(primes, nextCandidate) && !yield(nextCandidate) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func factorize(primes []uint, n uint) iter.Seq2[uint, int] { | ||||||
|  | 	return func(yield func(uint, int) bool) { | ||||||
|  | 		n := n | ||||||
|  |  | ||||||
|  | 		for _, factor := range possibleFactors(primes, n) { | ||||||
|  | 			exponent := 0 | ||||||
|  |  | ||||||
|  | 			for n%factor == 0 { | ||||||
|  | 				n /= factor | ||||||
|  | 				exponent++ | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if exponent > 0 && !yield(factor, exponent) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// if n didn't reach zero, then n is prime. | ||||||
|  | 		if n > 1 { | ||||||
|  | 			yield(n, 1) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Factorize returns an iterator that yields the prime factors of n and their exponents. | ||||||
|  | func Factorize(n uint) iter.Seq2[uint, int] { | ||||||
|  | 	return factorize(upTo(uint(math.Sqrt(float64(n)))), n) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LCM computes the least common multiple. This really doesn't have much to do with prime numbers other than the algorithm heavily relying on them. | ||||||
|  | // | ||||||
|  | // Returns 0 if the LCM would overflow, or one of the numbers is 0. | ||||||
|  | func LCM(numbers ...uint) uint { | ||||||
|  | 	// trivial cases: | ||||||
|  | 	switch { | ||||||
|  | 	case len(numbers) == 0: | ||||||
|  | 		return 1 | ||||||
|  | 	case len(numbers) == 1: | ||||||
|  | 		return numbers[0] | ||||||
|  | 	case 0 == slices.Min(numbers): | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	primes := upTo(uint(math.Sqrt(float64(slices.Max(numbers))))) | ||||||
|  | 	factors := map[uint]int{} | ||||||
|  |  | ||||||
|  | 	for _, n := range numbers { | ||||||
|  | 		for factor, exponent := range factorize(primes, n) { | ||||||
|  | 			factors[factor] = max(factors[factor], exponent) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	product := uint(1) | ||||||
|  |  | ||||||
|  | 	for factor, exponent := range factors { | ||||||
|  | 		for ; exponent > 0; exponent-- { | ||||||
|  | 			if product > math.MaxUint/factor { | ||||||
|  | 				// would overflow | ||||||
|  | 				return 0 | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			product *= factor | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return product | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GCD computes the greatest common divisor. This really doesn't have much to do with prime numbers other than the algorithm heavily relying on them. | ||||||
|  | // | ||||||
|  | // Returns 0 if the list is empty or all of the numbers are 0. | ||||||
|  | func GCD(numbers ...uint) uint { | ||||||
|  | 	// trivial cases: | ||||||
|  | trivialCases: | ||||||
|  | 	switch { | ||||||
|  | 	case len(numbers) == 0: | ||||||
|  | 		return 0 | ||||||
|  | 	case len(numbers) == 1: | ||||||
|  | 		return numbers[0] | ||||||
|  | 	case 0 == slices.Min(numbers): | ||||||
|  | 		// ugh, why did you have to include a zero? | ||||||
|  | 		// clone the input slice, sort it, remove duplicates, then remove the first element, which will be the zero | ||||||
|  | 		// that it taunting us. | ||||||
|  | 		numbers = slices.Clone(numbers) | ||||||
|  | 		slices.Sort(numbers) | ||||||
|  | 		numbers = slices.Compact(numbers)[1:] | ||||||
|  |  | ||||||
|  | 		// start over again. | ||||||
|  | 		goto trivialCases | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	primes := upTo(uint(math.Sqrt(float64(slices.Max(numbers))))) | ||||||
|  | 	factors := map[uint]int{} | ||||||
|  | 	var seen []uint | ||||||
|  |  | ||||||
|  | 	for i, n := range numbers { | ||||||
|  | 		seen = seen[:0] | ||||||
|  |  | ||||||
|  | 		for factor, exponent := range factorize(primes, n) { | ||||||
|  | 			if i == 0 { | ||||||
|  | 				// if this is the first number, store the exponent normally. | ||||||
|  | 				factors[factor] = exponent | ||||||
|  | 			} else { | ||||||
|  | 				seen = append(seen, factor) | ||||||
|  |  | ||||||
|  | 				if prevExponent, found := factors[factor]; found { | ||||||
|  | 					// use the minimum of this exponent and the previously known one. | ||||||
|  | 					factors[factor] = min(prevExponent, exponent) | ||||||
|  | 				} else { | ||||||
|  | 					// the previous numbers must have had an exponent of 0 for this, | ||||||
|  | 					// and min(0, exponent) would be 0. | ||||||
|  | 					// we don't need to store factors with an exponent of 0, | ||||||
|  | 					// so we'd delete it in this case. Except it wasn't found in the map, | ||||||
|  | 					// so there's nothing to delete. | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if i != 0 { | ||||||
|  | 			// if this isn't the first number, and we know about factors that weren't visited, | ||||||
|  | 			// then those unvisited factors implicitly had an exponent of 0, and need to be deleted. | ||||||
|  | 			for factor := range factors { | ||||||
|  | 				// note: factorize returns factors in ascending order, so seen is a sorted list and we | ||||||
|  | 				// can use a binary search. | ||||||
|  | 				if _, found := slices.BinarySearch(seen, factor); !found { | ||||||
|  | 					delete(factors, factor) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	product := uint(1) | ||||||
|  |  | ||||||
|  | 	for factor, exponent := range factors { | ||||||
|  | 		for ; exponent > 0; exponent-- { | ||||||
|  | 			// note: overflow can't happen here. | ||||||
|  | 			product *= factor | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return product | ||||||
|  | } | ||||||
							
								
								
									
										1489
									
								
								prime_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1489
									
								
								prime_test.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user