diff --git a/cmd/stirling.go b/cmd/stirling.go
new file mode 100644
index 0000000..91dfec5
--- /dev/null
+++ b/cmd/stirling.go
@@ -0,0 +1,91 @@
+/*
+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 (
+ "fmt"
+ "math/big"
+
+ "github.com/spf13/cobra"
+ "scm.dairydemon.net/filifa/mathtools/internal/lib"
+)
+
+var firstKind bool
+var secondKind bool
+var stirlingTop string
+var stirlingBottom string
+var stirlingUnsigned bool
+
+func stirling(cmd *cobra.Command, args []string) {
+ n, ok := new(big.Int).SetString(stirlingTop, 10)
+ if !ok {
+ cobra.CheckErr("invalid input " + stirlingTop)
+ }
+
+ k, ok := new(big.Int).SetString(stirlingBottom, 10)
+ if !ok {
+ cobra.CheckErr("invalid input " + stirlingBottom)
+ }
+
+ var result *big.Int
+ if firstKind {
+ result = lib.Stirling1(n, k)
+ } else if secondKind {
+ result = lib.Stirling2(n, k)
+ }
+
+ if stirlingUnsigned {
+ result.Abs(result)
+ }
+
+ fmt.Println(result)
+}
+
+// stirlingCmd represents the stirling command
+var stirlingCmd = &cobra.Command{
+ Use: "stirling",
+ Short: "Compute the Stirling numbers",
+ Long: `Compute the Stirling numbers.`,
+ Run: stirling,
+}
+
+func init() {
+ rootCmd.AddCommand(stirlingCmd)
+
+ // Here you will define your flags and configuration settings.
+
+ // Cobra supports Persistent Flags which will work for this command
+ // and all subcommands, e.g.:
+ // stirlingCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+ // Cobra supports local flags which will only run when this command
+ // is called directly, e.g.:
+ // stirlingCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+
+ stirlingCmd.Flags().BoolVarP(&firstKind, "first", "1", false, "Compute Stirling numbers of the first kind")
+ stirlingCmd.Flags().BoolVarP(&secondKind, "second", "2", false, "Compute Stirling numbers of the second kind")
+
+ stirlingCmd.MarkFlagsMutuallyExclusive("first", "second")
+ stirlingCmd.MarkFlagsOneRequired("first", "second")
+
+ stirlingCmd.Flags().StringVarP(&stirlingTop, "n", "n", "", "n")
+ stirlingCmd.Flags().StringVarP(&stirlingBottom, "k", "k", "", "k")
+ stirlingCmd.MarkFlagRequired("n")
+ stirlingCmd.MarkFlagRequired("k")
+
+ stirlingCmd.Flags().BoolVar(&stirlingUnsigned, "unsigned", false, "output the absolute value of the number (only relevant for first kind)")
+}
diff --git a/internal/lib/stirling.go b/internal/lib/stirling.go
new file mode 100644
index 0000000..b3f99ff
--- /dev/null
+++ b/internal/lib/stirling.go
@@ -0,0 +1,68 @@
+/*
+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 lib
+
+import (
+ "math/big"
+)
+
+// TODO: implement both of these with dynamic programming
+
+func Stirling1(n, k *big.Int) *big.Int {
+ if n.Cmp(big.NewInt(0)) == 0 && k.Cmp(big.NewInt(0)) == 0 {
+ return big.NewInt(1)
+ }
+
+ if n.Cmp(big.NewInt(0)) == 0 || k.Cmp(big.NewInt(0)) == 0 {
+ return big.NewInt(0)
+ }
+
+ newN := new(big.Int).Set(n)
+ newN.Sub(newN, big.NewInt(1))
+
+ result := Stirling1(newN, k)
+ result.Mul(result, newN)
+ result.Neg(result)
+
+ newK := new(big.Int).Set(k)
+ newK.Sub(newK, big.NewInt(1))
+
+ result.Add(result, Stirling1(newN, newK))
+ return result
+}
+
+func Stirling2(n, k *big.Int) *big.Int {
+ if n.Cmp(k) == 0 {
+ return big.NewInt(1)
+ }
+
+ if n.Cmp(big.NewInt(0)) == 0 || k.Cmp(big.NewInt(0)) == 0 {
+ return big.NewInt(0)
+ }
+
+ newN := new(big.Int).Set(n)
+ newN.Sub(newN, big.NewInt(1))
+
+ result := Stirling2(newN, k)
+ result.Mul(result, k)
+
+ newK := new(big.Int).Set(k)
+ newK.Sub(newK, big.NewInt(1))
+
+ result.Add(result, Stirling2(newN, newK))
+ return result
+}