Compare commits

..

15 Commits

Author SHA1 Message Date
filifa
ae253af15f add convolve command 2026-01-24 16:42:48 -05:00
filifa
afd8fe57da remove dead code 2025-12-05 23:07:31 -05:00
filifa
592fadb6d7 clean up 2025-12-05 23:00:56 -05:00
filifa
0dd446fe74 add flag for whether to only count coprime residues 2025-12-05 22:20:54 -05:00
filifa
6081757183 add quadratic residues sieve 2025-12-05 21:31:05 -05:00
filifa
d48e9932c1 go mod tidy 2025-11-16 22:38:32 -05:00
filifa
faa58bb4dc add readme 2025-11-16 22:37:31 -05:00
filifa
6d74fdb5e3 add coprime check 2025-11-15 19:41:41 -05:00
filifa
73c750d317 move stirling methods to public package 2025-11-15 19:34:49 -05:00
filifa
85229bffe1 add multiplicative order command 2025-11-15 19:32:09 -05:00
filifa
c842762ca9 adapt to update sieves for additive functions 2025-10-07 18:10:44 -04:00
filifa
df317e0837 refactor updating prime powers 2025-10-07 18:06:24 -04:00
filifa
365b396db1 use common updateMultiples 2025-10-07 17:57:54 -04:00
filifa
30ad962cfd refactor updating multiples into separate common function 2025-10-07 17:54:02 -04:00
filifa
26c197318e rename functions 2025-10-07 17:28:52 -04:00
22 changed files with 565 additions and 84 deletions

36
README.md Normal file
View File

@@ -0,0 +1,36 @@
# mathtools
mathtools is a program for computing various mathematical results that would be
tedious to compute by hand.
## Why?
Obviously, libraries, software packages, and websites exist for these sort of
calculations, but there are tradeoffs with any approach. Rather than needing to
write a script, use a REPL, or load a webpage, this allows for an approach more
like standard CLI utilities such as [GNU
factor](https://www.gnu.org/software/coreutils/factor).
Generally, I've opted to implement routines for problems that are best solved
with *algorithms*, rather than *formulas*. For instance, the quadratic formula,
while useful, is basically plug and chug, and thus isn't implemented here. On
the other hand, determining whether a number is prime is a little more tedious
to do by hand, so it's provided as a routine.
## Available routines
Available routines include:
* convergents of a periodic continued fraction
* solving systems of linear congruences with the Chinese remainder theorem
* discrete logarithm
* greatest common divisor
* primality testing
* Jacobi symbol
* modular inverse
* modular square root
* multiplicative order
* integer partitions
* solving Pell equations
* primitive root modulo n
* area of a simple polygon from vertex coordinates
* sieves for totient function, divisor function, Mobius function, and more
* repetend of the continued fraction of a square root
* Stirling numbers
* summatory functions

32
cmd/common.go Normal file
View File

@@ -0,0 +1,32 @@
/*
Copyright © 2026 filifa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd
import (
"bufio"
"os"
)
func splitLines(file *os.File) []string {
slice := make([]string, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
slice = append(slice, scanner.Text())
}
return slice
}

110
cmd/convolve.go Normal file
View File

@@ -0,0 +1,110 @@
/*
Copyright © 2026 filifa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd
import (
"bufio"
"fmt"
"os"
"strconv"
"github.com/spf13/cobra"
"scm.dairydemon.net/filifa/mathtools/internal/lib"
)
var convolveA string
var convolveB string
func readSequenceFromFile(filepath string) ([]complex128, error) {
// initial length 1 so index math works
seq := make([]complex128, 1)
file, err := os.Open(filepath)
if err != nil {
return seq, err
}
defer file.Close()
lines := splitLines(file)
for _, line := range lines {
x, err := strconv.ParseComplex(line, 128)
if err != nil {
return seq, err
}
seq = append(seq, x)
}
return seq, nil
}
func convolve(cmd *cobra.Command, args []string) {
a, err := readSequenceFromFile(convolveA)
if err != nil {
cobra.CheckErr(err)
}
b, err := readSequenceFromFile(convolveB)
if err != nil {
cobra.CheckErr(err)
}
bufStdout := bufio.NewWriter(os.Stdout)
defer bufStdout.Flush()
for i, n := range lib.DirichletConvolve(a, b) {
if i == 0 {
continue
}
if imag(n) == 0 {
fmt.Fprintln(bufStdout, real(n))
} else {
fmt.Fprintln(bufStdout, n)
}
}
}
// convolveCmd represents the convolve command
var convolveCmd = &cobra.Command{
Use: "convolve",
Short: "Compute the Dirichlet convolution of two sequences",
Long: `Compute the Dirichlet convolution of two sequences.
Each sequence is provided as a file, where line k of the file gives the kth term of the sequence.
`,
Run: convolve,
}
func init() {
rootCmd.AddCommand(convolveCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// convolveCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// convolveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
convolveCmd.Flags().StringVarP(&convolveA, "first", "a", "", "first sequence")
convolveCmd.MarkFlagRequired("first")
convolveCmd.Flags().StringVarP(&convolveB, "second", "b", "", "second sequence")
convolveCmd.MarkFlagRequired("second")
}

View File

@@ -32,7 +32,7 @@ func divisors(cmd *cobra.Command, args []string) {
bufStdout := bufio.NewWriter(os.Stdout)
defer bufStdout.Flush()
ch := sieve.DivisorsSieve(divisorsN, divisorsE, 1000)
ch := sieve.Divisors(divisorsN, divisorsE, 1000)
for i := 0; ; i++ {
v, ok := <-ch
if !ok {

View File

@@ -31,7 +31,7 @@ func mobius(cmd *cobra.Command, args []string) {
bufStdout := bufio.NewWriter(os.Stdout)
defer bufStdout.Flush()
ch := sieve.MobiusSieve(mobiusN, 1000)
ch := sieve.Mobius(mobiusN, 1000)
for i := 0; ; i++ {
v, ok := <-ch
if !ok {

View File

@@ -0,0 +1,75 @@
/*
Copyright © 2025 filifa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd
import (
"fmt"
"math/big"
"github.com/spf13/cobra"
"scm.dairydemon.net/filifa/mathtools/internal/lib"
)
var multiplicativeOrderBase string
var multiplicativeOrderModulus string
func multiplicativeOrder(cmd *cobra.Command, args []string) {
g, ok := new(big.Int).SetString(multiplicativeOrderBase, 10)
if !ok {
cobra.CheckErr("invalid base " + multiplicativeOrderBase)
}
m, ok := new(big.Int).SetString(multiplicativeOrderModulus, 10)
if !ok {
cobra.CheckErr("invalid modulus " + multiplicativeOrderModulus)
}
gcd := new(big.Int).GCD(nil, nil, g, m)
if gcd.Cmp(big.NewInt(1)) != 0 {
cobra.CheckErr("base " + multiplicativeOrderBase + " and modulus " + multiplicativeOrderModulus + " are not coprime")
}
k := lib.MultiplicativeOrder(g, m)
fmt.Println(k)
}
// multiplicativeOrderCmd represents the multiplicativeOrder command
var multiplicativeOrderCmd = &cobra.Command{
Use: "multiplicative-order",
Short: "Compute multiplicative order",
Long: `Compute the multiplicative order of a number given a modulus.`,
Run: multiplicativeOrder,
}
func init() {
rootCmd.AddCommand(multiplicativeOrderCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// multiplicativeOrderCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// multiplicativeOrderCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
multiplicativeOrderCmd.Flags().StringVarP(&multiplicativeOrderBase, "base", "g", "", "base")
multiplicativeOrderCmd.Flags().StringVarP(&multiplicativeOrderModulus, "modulus", "m", "", "modulus")
multiplicativeOrderCmd.MarkFlagRequired("base")
multiplicativeOrderCmd.MarkFlagRequired("modulus")
}

View File

@@ -32,7 +32,7 @@ func primeOmega(cmd *cobra.Command, args []string) {
bufStdout := bufio.NewWriter(os.Stdout)
defer bufStdout.Flush()
ch := sieve.PrimeOmegaSieve(primeOmegaN, primeOmegaMul, 1000)
ch := sieve.PrimeOmega(primeOmegaN, primeOmegaMul, 1000)
for i := 0; ; i++ {
v, ok := <-ch
if !ok {

75
cmd/quadraticResidues.go Normal file
View File

@@ -0,0 +1,75 @@
/*
Copyright © 2025 filifa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd
import (
"bufio"
"fmt"
"os"
"github.com/spf13/cobra"
"scm.dairydemon.net/filifa/mathtools/internal/lib/sieve"
)
var quadraticResiduesN uint
var quadraticResiduesCoprime bool
func quadraticResidues(cmd *cobra.Command, args []string) {
bufStdout := bufio.NewWriter(os.Stdout)
defer bufStdout.Flush()
ch := sieve.QuadraticResidues(quadraticResiduesN, quadraticResiduesCoprime, 1000)
for i := 0; ; i++ {
v, ok := <-ch
if !ok {
break
}
if i == 0 {
continue
}
fmt.Fprintln(bufStdout, v)
}
}
// quadraticResiduesCmd represents the quadraticResidues command
var quadraticResiduesCmd = &cobra.Command{
Use: "quadratic-residues",
Short: "Compute the number of quadratic residues for all moduli less than n",
Long: `Compute the number of quadratic residues for all moduli less than n.`,
Run: quadraticResidues,
}
func init() {
sieveCmd.AddCommand(quadraticResiduesCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// quadraticResiduesCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// quadraticResiduesCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
quadraticResiduesCmd.Flags().UintVarP(&quadraticResiduesN, "limit", "n", 0, "upper limit")
quadraticResiduesCmd.MarkFlagRequired("limit")
quadraticResiduesCmd.Flags().BoolVarP(&quadraticResiduesCoprime, "coprime-only", "c", false, "only count residues coprime to the modulus")
}

View File

@@ -31,7 +31,7 @@ func radical(cmd *cobra.Command, args []string) {
bufStdout := bufio.NewWriter(os.Stdout)
defer bufStdout.Flush()
ch := sieve.RadicalSieve(radicalN, 1000)
ch := sieve.Radical(radicalN, 1000)
for i := 0; ; i++ {
v, ok := <-ch
if !ok {

View File

@@ -17,7 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"bufio"
"fmt"
"os"
"strconv"
@@ -29,16 +28,6 @@ import (
var shoelaceFile string
func splitLines(file *os.File) []string {
slice := make([]string, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
slice = append(slice, scanner.Text())
}
return slice
}
func readFromFile(filepath string) ([]lib.Point, error) {
points := make([]lib.Point, 0)
file, err := os.Open(filepath)

View File

@@ -22,7 +22,7 @@ import (
"strconv"
"github.com/spf13/cobra"
"scm.dairydemon.net/filifa/mathtools/internal/lib"
"scm.dairydemon.net/filifa/mathtools/lib"
)
var firstKind bool

View File

@@ -31,7 +31,7 @@ func totient(cmd *cobra.Command, args []string) {
bufStdout := bufio.NewWriter(os.Stdout)
defer bufStdout.Flush()
for v := range sieve.TotientSieve(totientN, 1000) {
for v := range sieve.Totient(totientN, 1000) {
if v == 0 {
continue
}

3
go.mod
View File

@@ -2,8 +2,9 @@ module scm.dairydemon.net/filifa/mathtools
go 1.24.4
require github.com/spf13/cobra v1.9.1
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
)

32
internal/lib/convolve.go Normal file
View File

@@ -0,0 +1,32 @@
/*
Copyright © 2026 filifa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
func DirichletConvolve(a, b []complex128) []complex128 {
c := make([]complex128, min(len(a), len(b)))
for i, x := range a {
for j, y := range b {
if i*j >= len(c) {
break
}
c[i*j] += x * y
}
}
return c
}

View File

@@ -0,0 +1,36 @@
/*
Copyright © 2025 filifa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sieve
func updateMultiples(sieve []uint, p uint, n uint, additive bool) {
for q := p; ; q *= p {
// sieve[a*b] = sieve[a] * sieve[b] if gcd(a,b) = 1
for i := 2 * q; i < n; i += q {
if i%(p*q) != 0 {
if additive {
sieve[i] += sieve[q]
} else {
sieve[i] *= sieve[q]
}
}
}
if p*q >= n {
break
}
}
}

View File

@@ -30,29 +30,10 @@ func pow(base uint, exp uint) uint {
return result
}
func updateMultiples(sieve []uint, x uint, p uint, n uint) {
for q := p; ; q *= p {
// sigma_x(a*b) = sigma_x(a) * sigma_x(b) if gcd(a,b) = 1
for i := 2 * q; i < n; i += q {
if i%(p*q) != 0 {
sieve[i] *= sieve[q]
}
}
if p*q >= n {
break
}
println(q)
// sigma_x(p^k) = p^(kx) + sigma_x(p^(k-1))
sieve[p*q] = pow(p*q, x) + sieve[q]
}
}
/*
DivisorSieve computes sigma_x(k) for k=1 to n, where sigma_x is the divisor sum function. x sets the power each divisor is raised to.
Divisors computes sigma_x(k) for k=1 to n, where sigma_x is the divisor sum function. x sets the power each divisor is raised to.
*/
func DivisorsSieve(n uint, x uint, buflen uint) chan uint {
func Divisors(n uint, x uint, buflen uint) chan uint {
sieve := make([]uint, n)
sieve[0] = 0
for i := uint(1); i < n; i++ {
@@ -69,7 +50,12 @@ func DivisorsSieve(n uint, x uint, buflen uint) chan uint {
}
sieve[i] = pow(i, x) + 1
updateMultiples(sieve, x, i, n)
for j := i; i*j < n; j *= i {
// sigma_x(p^k) = p^(kx) + sigma_x(p^(k-1))
sieve[i*j] = pow(i*j, x) + sieve[j]
}
updateMultiples(sieve, i, n, false)
ch <- sieve[i]
}
}()

View File

@@ -17,9 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package sieve
/*
MobiusSieve computes mobius(k) for k=1 to n, where mobius is the Mobius function.
Mobius computes mobius(k) for k=1 to n, where mobius is the Mobius function.
*/
func MobiusSieve(n uint, buflen uint) chan int {
func Mobius(n uint, buflen uint) chan int {
sieve := make([]int, n)
for i := 0; i < int(n); i++ {
sieve[i] = i

View File

@@ -16,29 +16,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sieve
func primeOmegaUpdateMultiples(sieve []uint, p uint, n uint, multiplicity bool) {
for q := p; ; q *= p {
// omega(a*b) = omega(a) + omega(b) if gcd(a,b) = 1
for i := 2 * q; i < n; i += q {
if i%(p*q) != 0 {
sieve[i] += sieve[q]
}
}
if p*q >= n {
break
}
if multiplicity {
func updatePowers(sieve []uint, p uint, n uint) {
for q := p; p*q < n; q *= p {
sieve[p*q] = 1 + sieve[q]
}
}
}
/*
PrimeOmegaSieve computes omega(k) for k=1 to n, where omega is the prime omega function. If multiplicity is true, factors are counted with multiplicity.
PrimeOmega computes omega(k) for k=1 to n, where omega is the prime omega function. If multiplicity is true, factors are counted with multiplicity.
*/
func PrimeOmegaSieve(n uint, multiplicity bool, buflen uint) chan uint {
func PrimeOmega(n uint, multiplicity bool, buflen uint) chan uint {
sieve := make([]uint, n)
for i := uint(0); i < n; i++ {
sieve[i] = 0
@@ -54,7 +41,11 @@ func PrimeOmegaSieve(n uint, multiplicity bool, buflen uint) chan uint {
}
sieve[i] = 1
primeOmegaUpdateMultiples(sieve, i, n, multiplicity)
if multiplicity {
updatePowers(sieve, i, n)
}
updateMultiples(sieve, i, n, true)
ch <- sieve[i]
}
}()

View File

@@ -0,0 +1,131 @@
/*
Copyright © 2025 filifa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sieve
// NOTE: these formulas come from https://web.archive.org/web/20151224013638/http://www.maa.org/sites/default/files/Walter_D22068._Stangl.pdf
func qrPowersOfTwoCoprime(sieve []uint, n uint) {
for q := uint(8); 2*q < n; q *= 2 {
// q(2^n) = 2^(n-3) for n >= 3
sieve[2*q] = 2 * sieve[q]
}
}
func qrPowersOfOddPrimesCoprime(sieve []uint, p uint, n uint) {
for q := p; p*q < n; q *= p {
// q(p^n) = (p^n - p^(n-1)) / 2
sieve[p*q] = (p*q - q) / 2
}
}
func qrPowersOfTwo(sieve []uint, n uint) {
k := 1
for q := uint(1); 2*q < n; q *= 2 {
if k%2 == 0 {
// s(2^n) = (2^(n-1) + 4) / 3 for even n
sieve[2*q] = (q + 4) / 3
} else {
// s(2^n) = (2^(n-1) + 5) / 3 for odd n
sieve[2*q] = (q + 5) / 3
}
k += 1
}
}
func qrPowersOfOddPrimes(sieve []uint, p uint, n uint) {
k := 3
for q := p * p; p*q < n; q *= p {
if k%2 == 0 {
// s(p^n) = (p^(n+1) + p + 2) / (2*(p+1)) for even n
sieve[p*q] = (p*p*q + p + 2) / (2 * (p + 1))
} else {
// s(p^n) = (p^(n+1) + 2*p + 1) / (2*(p+1)) for odd n
sieve[p*q] = (p*p*q + 2*p + 1) / (2 * (p + 1))
}
k += 1
}
}
/*
QuadraticResidues computes the number of quadratic residues modulo k for k=1 to n.
see https://oeis.org/A000224 and https://oeis.org/A046073
*/
func QuadraticResidues(n uint, coprime bool, buflen uint) chan uint {
sieve := make([]uint, n)
for i := uint(0); i < n; i++ {
sieve[i] = 1
}
ch := make(chan uint, buflen)
go func() {
defer close(ch)
if coprime {
sieveQRCoprime(sieve, n, ch)
} else {
sieveQR(sieve, n, ch)
}
}()
return ch
}
func sieveQRCoprime(sieve []uint, n uint, ch chan uint) {
for i := uint(0); i < n; i++ {
if i == 0 || i == 1 || i == 4 || i == 6 || i == 8 || i == 12 || i == 24 || sieve[i] != 1 {
ch <- sieve[i]
continue
}
if i == 2 {
qrPowersOfTwoCoprime(sieve, n)
} else {
// q(p) = (p - 1) / 2
sieve[i] = (i - 1) / 2
qrPowersOfOddPrimesCoprime(sieve, i, n)
}
updateMultiples(sieve, i, n, false)
ch <- sieve[i]
}
}
func sieveQR(sieve []uint, n uint, ch chan uint) {
for i := uint(0); i < n; i++ {
if i == 0 || i == 1 || sieve[i] != 1 {
ch <- sieve[i]
continue
}
if i == 2 {
qrPowersOfTwo(sieve, n)
} else {
// s(p) = (p + 1) / 2
sieve[i] = (i + 1) / 2
if i*i < n {
// s(p^2) = (p^2 - p + 2) / 2
sieve[i*i] = (i*i - i + 2) / 2
}
qrPowersOfOddPrimes(sieve, i, n)
}
updateMultiples(sieve, i, n, false)
ch <- sieve[i]
}
}

View File

@@ -16,28 +16,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sieve
func radicalUpdateMultiples(sieve []uint, p uint, n uint) {
for q := p; ; q *= p {
// rad(a*b) = rad(a) * rad(b) if gcd(a,b) = 1
for i := 2 * q; i < n; i += q {
if i%(p*q) != 0 {
sieve[i] *= sieve[q]
}
}
if p*q >= n {
break
}
// rad(p^k) = rad(p)
sieve[p*q] = sieve[q]
}
}
/*
RadicalSieve computes rad(k) for k=1 to n, where rad(n) is the radical of n.
Radical computes rad(k) for k=1 to n, where rad(n) is the radical of n.
*/
func RadicalSieve(n uint, buflen uint) chan uint {
func Radical(n uint, buflen uint) chan uint {
sieve := make([]uint, n)
sieve[0] = 0
for i := uint(1); i < n; i++ {
@@ -54,7 +36,12 @@ func RadicalSieve(n uint, buflen uint) chan uint {
}
sieve[i] = i
radicalUpdateMultiples(sieve, i, n)
for j := i; i*j < n; j *= i {
// rad(p^k) = rad(p)
sieve[i*j] = sieve[i]
}
updateMultiples(sieve, i, n, false)
ch <- sieve[i]
}
}()

View File

@@ -17,9 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package sieve
/*
TotientSieve computes totient(k) for k=1 to n, where totient is Euler's totient function. buflen sets the buffer length of the returned channel. Larger buffer lengths can result in better performance at the cost of higher memory usage.
Totient computes totient(k) for k=1 to n, where totient is Euler's totient function. buflen sets the buffer length of the returned channel. Larger buffer lengths can result in better performance at the cost of higher memory usage.
*/
func TotientSieve(n uint, buflen uint) chan uint {
func Totient(n uint, buflen uint) chan uint {
totients := make([]uint, n)
totients[0] = 0
totients[1] = 1