diff --git a/cmd/quadraticResidues.go b/cmd/quadraticResidues.go
new file mode 100644
index 0000000..ad122e8
--- /dev/null
+++ b/cmd/quadraticResidues.go
@@ -0,0 +1,72 @@
+/*
+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 .
+*/
+package cmd
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+
+ "github.com/spf13/cobra"
+ "scm.dairydemon.net/filifa/mathtools/internal/lib/sieve"
+)
+
+var quadraticResiduesN uint
+
+func quadraticResidues(cmd *cobra.Command, args []string) {
+ bufStdout := bufio.NewWriter(os.Stdout)
+ defer bufStdout.Flush()
+
+ ch := sieve.QuadraticResidues(quadraticResiduesN, 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")
+}
diff --git a/internal/lib/sieve/qresidue.go b/internal/lib/sieve/qresidue.go
new file mode 100644
index 0000000..a38b156
--- /dev/null
+++ b/internal/lib/sieve/qresidue.go
@@ -0,0 +1,62 @@
+/*
+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 .
+*/
+package sieve
+
+func updatePowersOfTwo(sieve []uint, n uint) {
+ for q := uint(8); 2*q < n; q *= 2 {
+ sieve[2*q] = 2 * sieve[q]
+ }
+}
+
+func updatePowersOfOddPrimes(sieve []uint, p uint, n uint) {
+ for q := p; p*q < n; q *= p {
+ sieve[p*q] = (p*q - q) / 2
+ }
+}
+
+/*
+QuadraticResidues computes the number of quadratic residues modulo k for k=1 to n.
+*/
+func QuadraticResidues(n uint, 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)
+ 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 {
+ updatePowersOfTwo(sieve, n)
+ } else {
+ sieve[i] = (i - 1) / 2
+ updatePowersOfOddPrimes(sieve, i, n)
+ }
+
+ updateMultiples(sieve, i, n, false)
+ ch <- sieve[i]
+ }
+ }()
+
+ return ch
+}