This commit is contained in:
Lucas F. 2026-07-16 11:08:24 -03:00
commit d161ab35a7
5 changed files with 198 additions and 0 deletions

41
.gitignore vendored Normal file
View file

@ -0,0 +1,41 @@
## Go
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
bin
# Test binary, built with `go test -c`
*.test
# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov
# Dependency directories (remove the comment below to include it)
# vendor/
# development notes
/devnotes
# Go workspace file
go.work
go.work.sum
# env file
.env
# compiled binary at root
/main
# Editor/IDE
# .idea/
# .vscode/

10
Makefile Normal file
View file

@ -0,0 +1,10 @@
.PHONY: build test clean
build:
@mkdir -p bin && go build -o bin/cpfgen .
test:
@go test -v ./...
clean:
@rm -rf bin

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module git.lucasf.xyz/public/cpfgen
go 1.26.4

57
main.go Normal file
View file

@ -0,0 +1,57 @@
package main
import (
"flag"
"fmt"
"math/rand/v2"
"strings"
)
func main() {
onlyNum := flag.Bool("num", false, "generate only numbers (no formatting)")
flag.Parse()
cpf := generateCPF()
if *onlyNum {
fmt.Println(cpf)
} else {
fmt.Printf("%s.%s.%s-%s\n", cpf[:3], cpf[3:6], cpf[6:9], cpf[9:])
}
}
func generateCPF() string {
digits := make([]int, 11)
for i := range 9 {
digits[i] = rand.IntN(10)
}
sum := 0
for i := range 9 {
sum += digits[i] * (10 - i)
}
rem := sum % 11
if rem < 2 {
digits[9] = 0
} else {
digits[9] = 11 - rem
}
sum = 0
for i := range 10 {
sum += digits[i] * (11 - i)
}
rem = sum % 11
if rem < 2 {
digits[10] = 0
} else {
digits[10] = 11 - rem
}
var result strings.Builder
for _, d := range digits {
fmt.Fprintf(&result, "%d", d)
}
return result.String()
}

87
main_test.go Normal file
View file

@ -0,0 +1,87 @@
package main
import (
"strconv"
"testing"
)
func TestGenerateCPFLength(t *testing.T) {
cpf := generateCPF()
if len(cpf) != 11 {
t.Fatalf("expected length 11, got %d: %s", len(cpf), cpf)
}
}
func TestGenerateCPFCheckDigits(t *testing.T) {
for range 100 {
cpf := generateCPF()
if !isValidCPF(cpf) {
t.Fatalf("invalid CPF generated: %s", cpf)
}
}
}
func TestGenerateCPFAllDigits(t *testing.T) {
seen := make(map[string]bool)
for range 1000 {
cpf := generateCPF()
if seen[cpf] {
t.Fatal("duplicate CPF generated")
}
seen[cpf] = true
}
}
func isValidCPF(cpf string) bool {
if len(cpf) != 11 {
return false
}
digits := make([]int, 11)
for i, ch := range cpf {
d, err := strconv.Atoi(string(ch))
if err != nil {
return false
}
digits[i] = d
}
allEqual := true
for i := 1; i < 11; i++ {
if digits[i] != digits[0] {
allEqual = false
break
}
}
if allEqual {
return false
}
sum := 0
for i := range 9 {
sum += digits[i] * (10 - i)
}
rem := sum % 11
var d1 int
if rem < 2 {
d1 = 0
} else {
d1 = 11 - rem
}
if d1 != digits[9] {
return false
}
sum = 0
for i := range 10 {
sum += digits[i] * (11 - i)
}
rem = sum % 11
var d2 int
if rem < 2 {
d2 = 0
} else {
d2 = 11 - rem
}
return d2 == digits[10]
}