cmath

package module
v0.0.0-...-5f2ebb9 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: 1 Imported by: 0

README

go-ruby-cmath/cmath

cmath — go-ruby-cmath

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's CMath standard library — the complex-aware trigonometric and transcendental functions of MRI 4.0.5's cmath gem. CMath accepts real or complex arguments and returns a real result when the input lies on the real branch of the function and a complex result otherwise, so CMath.sqrt(4) is 2.0 while CMath.sqrt(-4) is (0+2i). This library reproduces that exact real-vs-complex decision and MRI's branch cuts and float formulas — without any Ruby runtime.

It is a CMath backend for go-embedded-ruby, but is a standalone, reusable module — a sibling of go-ruby-regexp (the Onigmo engine), go-ruby-marshal (the Marshal codec) and go-ruby-yaml (the Psych port).

What it is — and isn't. The mathematics of CMath — the real-vs-complex branch test, the closed-form complex expressions, the principal branch cuts — is fully deterministic and needs no interpreter, so it lives here as pure Go. Binding the results to a host's live Float/Complex objects is the host's job; this library takes and returns a small, explicit value model (Number).

The Number value model

A Number is a real-or-complex value, the Go analogue of a Ruby Float-or-Complex:

n := cmath.Real(4)         // a real argument (Ruby Integer / Float)
z := cmath.Complex(0, 1)   // a complex argument (Ruby Complex)

r := cmath.Sqrt(n)         // r.IsComplex == false, r.Real == 2.0
c := cmath.Sqrt(cmath.Real(-4))
                           // c.IsComplex == true, c.Real == 0, c.Imag == 2.0

cmath.Complex(re, 0) stays complex even with a zero imaginary part, matching Ruby: Complex(1, 0).real? is false, so CMath treats it as complex.

API

Every function mirrors the same-named method of Ruby's CMath module, taking and returning Number:

Go Ruby Real branch (returns real)
Sqrt(z) CMath.sqrt real z >= 0
Exp(z) CMath.exp real z
Log(z[, base]) CMath.log real z >= 0 and real base >= 0
Log2(z) CMath.log2 real z >= 0
Log10(z) CMath.log10 real z >= 0
Cbrt(z) CMath.cbrt real z >= 0
Sin/Cos/Tan(z) CMath.sin/cos/tan real z
Sinh/Cosh/Tanh(z) CMath.sinh/cosh/tanh real z
Asin(z) CMath.asin real z in [-1, 1]
Acos(z) CMath.acos real z in [-1, 1]
Atan(z) CMath.atan real z
Atan2(y, x) CMath.atan2 real y and real x
Asinh(z) CMath.asinh real z
Acosh(z) CMath.acosh real z >= 1
Atanh(z) CMath.atanh real z in [-1, 1]

Outside the real branch each function returns the complex result, computed with the same closed-form expression CMath uses — Asin, for instance, is built from this package's own Log and Sqrt, exactly as CMath.asin is — so the branch cuts agree with MRI.

A few representative results, matching ruby -rcmath:

cmath.Sqrt(cmath.Real(4))    // real 2.0
cmath.Sqrt(cmath.Real(-4))   // complex (0+2i)
cmath.Log(cmath.Real(-1))    // complex (0+πi)
cmath.Asin(cmath.Real(2))    // complex (1.5707963267948966-1.3169578969248166i)
cmath.Cbrt(cmath.Real(-8))   // complex (1+1.7320508075688772i)

Tests & coverage

The suite is in two halves. The deterministic suite carries golden values captured from MRI 4.0.5 and asserts both the exact real-vs-complex shape and the float components to a tight tolerance; it needs no Ruby and alone holds coverage at 100%. The oracle suite drives the real ruby -rcmath binary across a broad real / negative / out-of-domain / complex corpus and confirms genuine MRI parity; it skips itself where ruby is absent or older than 4.0 (the Windows and qemu cross-arch lanes), so the gate stays green everywhere. CI runs the suite on three operating systems and all six supported 64-bit architectures (amd64, arm64, riscv64, loong64, ppc64le, s390x), with cgo disabled.

go test ./...                       # deterministic + (if ruby present) oracle
COVERPKG=$(go list ./... | paste -sd, -)
go test -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # total: 100.0%

License

BSD-3-Clause — see LICENSE. Copyright (c) the go-ruby-cmath/cmath 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 cmath is a pure-Go (no cgo) reimplementation of Ruby's CMath standard library — the complex-aware trigonometric and transcendental functions of MRI 4.0.5's `cmath` gem.

CMath accepts real or complex arguments and returns a real result when the input lies on the real branch of the function (so CMath.sqrt(4) is 2.0) and a complex result otherwise (CMath.sqrt(-4) is (0+2i)). This package mirrors that real-or-complex value model with the Number type and reproduces MRI's exact branch-cut decisions and float formulas, so it can back a Ruby runtime such as go-embedded-ruby without any interpreter.

The mapping to MRI is faithful to the reference implementation: every function here uses the same real-vs-complex test and the same closed-form expression that the cmath gem's Ruby source does (e.g. asin is built from this package's own Log and Sqrt, exactly as CMath.asin is), so results agree with the `ruby` binary to within floating-point rounding.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Number

type Number struct {
	Real      float64
	Imag      float64
	IsComplex bool
}

Number is a real-or-complex value, the Go analogue of a Ruby Float-or-Complex. A real Number has IsComplex false and carries its value in Real; a complex one has IsComplex true and uses both Real and Imag. The CMath functions take and return Number, choosing the real shape exactly where MRI's CMath does.

func Acos

func Acos(z Number) Number

Acos returns the arc cosine of z. CMath.acos(z). Real for z in [-1, 1].

func Acosh

func Acosh(z Number) Number

Acosh returns the inverse hyperbolic cosine of z. CMath.acosh(z). Real for z >= 1.

func Asin

func Asin(z Number) Number

Asin returns the arc sine of z. CMath.asin(z). Real for z in [-1, 1].

func Asinh

func Asinh(z Number) Number

Asinh returns the inverse hyperbolic sine of z. CMath.asinh(z).

func Atan

func Atan(z Number) Number

Atan returns the arc tangent of z. CMath.atan(z).

func Atan2

func Atan2(y, x Number) Number

Atan2 returns the arc tangent of y/x using the signs of both to select the quadrant. CMath.atan2(y, x). Real when both arguments are real.

func Atanh

func Atanh(z Number) Number

Atanh returns the inverse hyperbolic tangent of z. CMath.atanh(z). Real for z in [-1, 1].

func Cbrt

func Cbrt(z Number) Number

Cbrt returns the principal cube root of z. CMath.cbrt(z) == z ** (1.0/3).

func Complex

func Complex(re, im float64) Number

Complex builds a complex Number (a Ruby Complex argument). It stays complex even when the imaginary part is zero, matching Ruby: Complex(1,0).real? is false, so CMath treats it as complex.

func Cos

func Cos(z Number) Number

Cos returns the cosine of z (radians). CMath.cos(z).

func Cosh

func Cosh(z Number) Number

Cosh returns the hyperbolic cosine of z. CMath.cosh(z).

func Exp

func Exp(z Number) Number

Exp returns Math::E raised to the z power. CMath.exp(z).

func Log

func Log(z Number, base ...Number) Number

Log returns the natural logarithm of z, or the base-b logarithm when a base is supplied. CMath.log(z[, b]). A non-negative real z with a non-negative base stays real; anything else is complex.

func Log2

func Log2(z Number) Number

Log2 returns the base-2 logarithm of z. CMath.log2(z).

func Log10

func Log10(z Number) Number

Log10 returns the base-10 logarithm of z. CMath.log10(z).

func Real

func Real(x float64) Number

Real builds a real Number (a Ruby Float / Integer argument).

func Sin

func Sin(z Number) Number

Sin returns the sine of z (radians). CMath.sin(z).

func Sinh

func Sinh(z Number) Number

Sinh returns the hyperbolic sine of z. CMath.sinh(z).

func Sqrt

func Sqrt(z Number) Number

Sqrt returns the principal square root of z. CMath.sqrt(z). A non-negative real stays real; a negative real becomes a pure imaginary; a complex argument uses Ruby's carry-of-the-sign formula and conjugate branch.

func Tan

func Tan(z Number) Number

Tan returns the tangent of z (radians). CMath.tan(z).

func Tanh

func Tanh(z Number) Number

Tanh returns the hyperbolic tangent of z. CMath.tanh(z).

func (Number) IsReal

func (n Number) IsReal() bool

IsReal reports whether the Number is on the real branch — the Go analogue of Ruby's Numeric#real?, which is true for Integer/Float and false for Complex.

Jump to

Keyboard shortcuts

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