prime

package module
v0.0.0-...-520610c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 3 Imported by: 0

README

go-ruby-prime/prime

prime — go-ruby-prime

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's prime standard library — the deterministic, interpreter-independent core of MRI 4.0.5's Prime class and the Integer#prime? / Integer#prime_division refinements. It generates the primes, tests primality, factorises an integer and reconstructs it — matching MRI byte-for-byte on the integer value model, without any Ruby runtime.

It is the prime backend for go-embedded-ruby, but is a standalone, reusable module with no dependency on the Ruby runtime — a sibling of go-ruby-regexp (the Onigmo engine), go-ruby-yaml (the Psych port) and go-ruby-marshal.

What it is — and isn't. The number theory behind Prime — sieving the primes, testing primality, factorising an integer — is fully deterministic and needs no interpreter, so it lives here as pure Go. Binding it to Ruby objects (the Prime.each enumerator, Integer#prime?) is the host's job; this library hands back *big.Int values the host wraps in its own Integer.

Features

Faithful port of prime, validated against the ruby binary on every supported platform:

  • The generatorTake(n) / First(n) return the first n primes (Prime.take / Prime.first); Each(ubound, yield) enumerates every prime p <= ubound (Prime.each(ubound)); EachPrime() is the unbounded cursor.
  • PrimalityIsPrime(n) mirrors Prime.prime? / Integer#prime? exactly: numbers < 2 are not prime, and every Carmichael number (561, 1105, …) and strong pseudoprime (2047, 3215031751, …) is correctly rejected. Small inputs use trial division; everything else uses a deterministic Baillie–PSW test (strong base-2 Miller–Rabin + strong Lucas), exact across the whole 64-bit range.
  • FactorisationPrimeDivision(n) returns [[p, exp], …] in ascending prime order (Prime.prime_division / Integer#prime_division), with a leading [-1, 1] for negative n and a ZeroError panic (MRI's ZeroDivisionError) for 0. Large cofactors fall back to Pollard's rho.
  • ReconstructionInt(pairs) multiplies prime**exp back to the integer (Prime.int_from_prime_division), the inverse of PrimeDivision.
  • CursorNext(n) / Prev(n) step to the adjacent prime.

CGO-free, dependency-free (only math/big), 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x).

Install

go get github.com/go-ruby-prime/prime

Usage

package main

import (
	"fmt"
	"math/big"

	"github.com/go-ruby-prime/prime"
)

func main() {
	fmt.Println(prime.Take(5))                       // [2 3 5 7 11]   (Prime.take 5)
	fmt.Println(prime.IsPrime(big.NewInt(561)))      // false          (Carmichael)
	fmt.Println(prime.IsPrime(big.NewInt(7919)))     // true
	fmt.Println(prime.PrimeDivision(big.NewInt(12))) // [[2 2] [3 1]]  (prime_division)
	fmt.Println(prime.PrimeDivision(big.NewInt(-12)))// [[-1 1] [2 2] [3 1]]

	// Reconstruct the integer from its factorisation.
	n := prime.Int(prime.PrimeDivision(big.NewInt(360)))
	fmt.Println(n) // 360

	// Bounded enumeration (Prime.each(11)).
	prime.Each(11, func(p *big.Int) bool { fmt.Print(p, " "); return true })
	fmt.Println() // 2 3 5 7 11
}

API

// IsPrime reports whether n is prime (Prime.prime? / Integer#prime?).
func IsPrime(n *big.Int) bool

// Take / First return the first n primes (Prime.take / Prime.first).
func Take(n int) []*big.Int
func First(n int) []*big.Int

// Each yields every prime p <= ubound, stopping early if yield returns false
// (Prime.each(ubound) { |p| ... }).
func Each(ubound int64, yield func(p *big.Int) bool)

// EachPrime returns a stateful generator: each call returns the next prime,
// starting at 2 and continuing forever (the unbounded Prime.each enumerator).
func EachPrime() func() *big.Int

// PrimeDivision returns the [prime, exponent] pairs of n in ascending order
// (Prime.prime_division / Integer#prime_division); a leading [-1, 1] carries a
// negative sign. It panics with ZeroError for n == 0 (MRI's ZeroDivisionError);
// PrimeDivisionErr is the non-panicking form.
func PrimeDivision(n *big.Int) [][2]*big.Int
func PrimeDivisionErr(n *big.Int) ([][2]*big.Int, error)

// Int reconstructs the integer from a prime-division slice
// (Prime.int_from_prime_division), the inverse of PrimeDivision.
func Int(pairs [][2]*big.Int) *big.Int

// Next / Prev return the adjacent prime (Prev returns nil when none exists).
func Next(n *big.Int) *big.Int
func Prev(n *big.Int) *big.Int

type ZeroError struct{} // mirrors Ruby's ZeroDivisionError

Ruby ↔ Go value model

Every integer flows through *big.Int, so a host can map its own Integer to and from this package without precision loss.

Ruby Go
Prime.prime?(n) / n.prime? IsPrime(n)
Prime.take(n) / Prime.first(n) Take(n) / First(n)
Prime.each(ubound) { ... } Each(ubound, yield)
Prime.each (enumerator) EachPrime()
Prime.prime_division(n) / n.prime_division PrimeDivision(n)
Prime.int_from_prime_division(ps) Int(ps)
ZeroDivisionError ZeroError (panic)

Algorithm

Primality is exact, not probabilistic, over the range Ruby programs use:

  • Small inputs (< 1000²) are settled by trial division against the primes up to 1000.
  • Larger inputs use Baillie–PSW — a strong base-2 Miller–Rabin test combined with a strong Lucas test (Selfridge parameters). BPSW has no known counterexample and is proven to have none below 2⁶⁴, so the result is exact across the entire 64-bit range and a sound probable-prime test beyond it.
  • Factorisation strips small primes, then splits the cofactor with Pollard's rho (Brent's variant), recursing to proven primes.

Tests & coverage

The suite pairs deterministic, ruby-free golden tables (which alone hold coverage at 100%, so the qemu cross-arch and Windows lanes pass the gate) with a differential MRI oracle: a corpus is computed here and by the system ruby (Prime.prime?, Prime.take, Prime.prime_division, …) and the two are compared. The oracle scripts $stdout.binmode / $stdin.binmode so Windows text-mode never pollutes the bytes, gate themselves on RUBY_VERSION >= "4.0", and skip where ruby is absent.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-prime/prime authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package prime is a pure-Go (no cgo) reimplementation of Ruby's `prime` standard library — the deterministic, interpreter-independent core of MRI 4.0.5's Prime class and the Integer#prime? / Integer#prime_division refinements.

It exposes the prime generator (Prime.each / Prime.take / Prime.first), the primality test (Prime.prime? / Integer#prime?), and the prime factorisation (Prime.prime_division / Integer#prime_division) together with its inverse (Prime.int_from_prime_division), all matching MRI byte-for-byte on the integer value model.

Primality trial-divides by the small primes up to 37 for tiny inputs. Any value that fits in a uint64 is then settled by a deterministic Miller–Rabin test over the smallest witness set proven to have no composite counterexample for that magnitude ({2,7,61} below ~4.76e9, growing to the twelve-base set {2,3,5,7,…,37} for the whole 64-bit range). Each round runs in machine-word arithmetic with no heap allocation: below 2^32 a plain uint64 multiply suffices, and for the full range Montgomery multiplication replaces the per-step 64-bit division with a multiply, add and shift. Genuinely larger values fall through to a deterministic Baillie–PSW test (a strong base-2 Miller–Rabin combined with a strong Lucas test) evaluated with math/big. Both paths agree exactly on the 64-bit range (each is proven exact there) and BPSW remains a correct probable-prime test beyond it — exactly the guarantee MRI's own generator relies on.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Each

func Each(ubound int64, yield func(p *big.Int) bool)

Each yields every prime p with p <= ubound, in ascending order, calling yield for each. If yield returns false, iteration stops early. This is the bounded form of Ruby's Prime.each(ubound) { |p| ... }; the unbounded generator is served by Take / First / EachPrime, which never need an upper bound.

A non-positive ubound yields nothing.

func EachPrime

func EachPrime() func() *big.Int

EachPrime returns a stateful generator: each call returns the next prime, starting at 2 and continuing forever (it is the unbounded Prime.each enumerator). It is the building block behind the Prev / Next cursor and reads from the shared memoized sieve, so successive calls are amortised O(1).

func First

func First(n int) []*big.Int

First is an alias for Take, mirroring Prime.first(n) == Prime.take(n).

func Int

func Int(pairs [][2]*big.Int) *big.Int

Int reconstructs an integer from a prime-division slice, matching Ruby's Prime.int_from_prime_division: it multiplies prime**exponent over the pairs (a leading [-1, 1] applies the sign). Exponents are taken as non-negative integers; the result is exact for the integer case MRI's Integer model uses.

func IsPrime

func IsPrime(n *big.Int) bool

IsPrime reports whether n is prime, matching Ruby's Prime.prime?(n) / Integer#prime?. Numbers < 2 (including negatives and zero) are not prime; 2 and 3 are prime; every Carmichael number and Miller–Rabin/Lucas pseudoprime in the 64-bit range is correctly rejected.

n is not mutated.

func Next

func Next(n *big.Int) *big.Int

Next returns the smallest prime strictly greater than n. n is not mutated.

func Prev

func Prev(n *big.Int) *big.Int

Prev returns the largest prime strictly less than n, or nil when none exists (n <= 2). n is not mutated.

func PrimeDivision

func PrimeDivision(n *big.Int) [][2]*big.Int

PrimeDivision returns the prime factorisation of n as Ruby's Prime.prime_division(n) / Integer#prime_division does: a slice of [prime, exponent] pairs in ascending prime order. For negative n a leading [-1, 1] pair carries the sign (matching MRI). It panics with ZeroError for n == 0, mirroring MRI's ZeroDivisionError; PrimeDivisionErr is the non-panicking form.

n is not mutated.

func PrimeDivisionErr

func PrimeDivisionErr(n *big.Int) ([][2]*big.Int, error)

PrimeDivisionErr is PrimeDivision without the panic: it returns a ZeroError for n == 0 instead of panicking. The factorisation is otherwise identical.

func Take

func Take(n int) []*big.Int

Take returns the first n primes (2, 3, 5, ...), matching Ruby's Prime.take(n) / Prime.first(n). A non-positive n returns an empty slice.

Types

type ZeroError

type ZeroError struct{}

ZeroError is returned (as a panic value, mirroring MRI's ZeroDivisionError) when a factorisation is asked for zero. MRI raises ZeroDivisionError from Prime.prime_division(0); callers that prefer an error can use the PrimeDivisionErr variant.

func (ZeroError) Error

func (ZeroError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL