eng is the machine you reach for when the arithmetic stops being
arithmetic. It collects, on one shelf:
x^2 = -1 has
an answer;Every word is pure
and every word is prefixed Eng.
For most of the twentieth century, applied mathematics was done out of a
book. Not a textbook — a handbook: tables of the special
functions, the distributions, the constants and the working formulas, kept
at the elbow of everyone whose job was getting numbers out rather than
proving theorems. The definitive one arrived in 1964, when the U.S. National
Bureau of Standards published Milton Abramowitz and Irene Stegun's
Handbook of Mathematical Functions. It was a thousand-page slab,
assembled in large part by human computers. It became one of the most-cited
works in all of science and has never gone out of print. Its real insight
was editorial: the hyperbolics, the gamma family, the distributions, the
roots and quadratures and constants belong on one shelf, because the person
who needs one of them this week will need another next week.
eng is that shelf as a machine — not a branch of
mathematics but an engineer's working collection, every entry computable
instead of tabulated.
This is the surface of a good scientific calculator, made into a library that does not lie to you. The formulas are mostly short and mostly famous, and almost every one has a way of producing a plausible wrong number instead of an error. That is what this machine is for.
Angles are radians at every boundary. There is no degree
mode, because a mode is state and this machine has none. A trig word whose
answer depends on a setting made three screens ago is the single worst thing
about the calculators this surface was compiled from. EngRad,
EngDeg, EngGradOf and EngFromGrad convert
at the edges, and are the only places a 180 or a 200 appears in an angle
context.
Coefficients are highest power first.
{ 1, 0, 0 - 4 } is x^2 - 4. This is the one
convention a caller will get backwards. Backwards, it evaluates without
complaint to the wrong number. So it is stated here, in the file's banner,
and beside every polynomial word.
Never compare complex numbers with =.
Structural equality reports EngComplex(0.1 + 0.2, 0) and
EngComplex(0.3, 0) as different. That is true of the bits and
useless to you. EngCNear is the only comparison offered, and it is
the one to use on any root or transform output.
Solvers refuse rather than mislead. EngZeroOf
checks that its bracket — the interval you claim holds a root —
actually contains a sign change before it starts halving. Bisection (solving
by cutting that interval in half again and again) converges perfectly
happily on an endpoint when the bracket holds no root. It then hands back a
number that is not a root at all — the worst failure available here,
and the reason the check is not optional.
The surface is closed. No stats,
math, str or seq name appears in a
signature or a return type, so you include one machine and get one vocabulary.
The wrappers over stats are mandatory rather than
polite: this machine includes stats, so a word named Mean here
would be a duplicate declaration and the machine would not load.
Nothing in the tree had a complex type before this one. Every word that can return a complex number does, rather than splitting into real and imaginary halves you have to reassemble:
Include "eng.shoddy"
Def Main()
Let z = EngComplex(3, 4)
Print(EngCAbs(z)) ' 5
Print(EngCShow(EngConj(z))) ' 3 - 4i
' Euler, which is the test that the exponential is really complex
Print(EngCShow(EngCExp(EngComplex(0, Pi())))) ' -1
' And the comparison that actually works
Print(EngCNear(EngCMul(EngComplex(0, 1), EngComplex(0, 1)),
EngComplex(0 - 1, 0), 0.0000000001)) ' True
EngCShow drops a zero part and never prints a plus followed by
a minus, so a complex number reads the way it is written on paper.
Differentiation, integration, summation and root-finding all take a
quotation — [ Number -- Number ] — and call it:
Print(EngDeriv(Fn(x) => x * x, 3)) ' 6
Print(EngIntegrate(Fn(x) => Sin(x), 0, Pi(), 100)) ' 2
Print(EngSumOf(Fn(i) => i, 1, 100)) ' 5050
Print(EngZeroOf(Fn(x) => x * x - 2, 0, 2)) ' 1.41421356...
Print(EngMinOf(Fn(x) => (x - 3) * (x - 3) + 1, 0, 10)) ' 3
The step in EngDeriv is scaled to the size of x,
because a fixed step is either noise near zero or lost in the rounding at
109. EngDeriv2 deliberately uses a much larger step.
A second difference subtracts three nearly equal numbers, and too small a
step loses every significant digit to cancellation — the digit-wipeout
that comes from subtracting nearly equal numbers. Expect about eight good
digits from the first derivative and four from the second.
EngIntegrate is composite Simpson and wants an even number of
intervals. It says so rather than quietly applying the wrong weights.
EngNewtonOf is there when you have a good starting point, and it
is the unguarded one — it diverges on a bad start where
EngZeroOf cannot.
A polynomial is a list of coefficients, highest power first. That is one convention for the whole machine and it matches lin's characteristic polynomials, so the two compose:
Let p = { 1, 0, 0 - 4 } ' x^2 - 4
Print(EngPolyEval(p, 3)) ' 5
Print(EngPolyArea(p, 0, 2)) ' the definite integral
Let d = EngPolyDiv(p, { 1, 0 - 2 }) ' divide by x - 2
Print(EngPolyEval(Quotient(d), 1)) ' 3 — the quotient is x + 2
Print(EngPolyEval(Remainder(d), 7)) ' 0 — it went exactly
EngPolyDiv hands back both halves in one record. A caller who
wanted only the quotient has usually assumed the remainder is zero, and this
makes them look. EngQuadRoots always returns complex numbers,
even when the roots are real: a word that changed its return type depending
on the discriminant (the b² − 4ac test that says whether
the roots are real) could not be branched on.
The descriptive statistics are here, prefixed like everything else:
Print(EngMean(xs))
Print(EngStdDev(xs)) ' sample form (n-1), like a spreadsheet's STDEV
Print(EngStdDevP(xs)) ' population form (n)
Print(EngNormCdf(110, 100, 15)) ' raw mean and sd, not a z-score
Both standard-deviation forms are here and each is named for which it is,
because picking the wrong one gives a plausible wrong number and no error.
(Standard deviation is the typical distance of a value from the average.)
The line is drawn at the inferential half. If you want Anova or
TTest2 you have left engineering mathematics behind, and you
include stats and say so.
A unit is a record, not a string, so a conversion between two different quantities is caught rather than performed:
Print(EngConvert(1, EngMile(), EngKilometre())) ' 1.609344
Print(EngConvert(100, EngCelsius(), EngFahrenheit())) ' 212
Print(EngConvert(1, EngStone(), EngPound())) ' 14
' EngConvert(1, EngMetre(), EngSecond()) — aborts, naming both quantities
Temperature is the only quantity here with an offset, and it is why the
record carries one at all. A temperature difference converts by the
factor alone and must not go through EngConvert.
Print(EngSigFig(0.00123456, 3)) ' 0.00123 — significant figures
Print(EngRoundTo(0.00123456, 3)) ' 0.001 — decimal places
Print(EngMetric(47000, 0)) ' 47k — what's written on the part
Print(EngSci(12345, 2)) ' 1.23E+4
Print(EngCommas(1234567, 0)) ' 1,234,567
Each of these produces a plausible wrong answer rather than an error, so each is worth knowing before it bites.
EngFact(18) is the last exact factorial. EngModPow,
EngFactorize and EngComb are bounded by it too.
That is why EngComb multiplies and divides alternately rather
than dividing one factorial by two others — EngComb(52, 5)
is 2598960 exactly, which the factorial form cannot manage.EngSinh and
EngCosh overflow, and where EngGamma does.
EngGammaLn keeps going, and every distribution here is built on it
rather than on a ratio of factorials for exactly that reason.EngDeriv is
accurate to about eight digits and EngDeriv2 to about four.
Neither is exact, and neither says so at the call site.Every word, and the records they hand back
| Word | Description |
|---|---|
| EngComplex(re, im) | Re, Im — a complex number, and its own constructor. Compare with EngCNear, never =. |
| EngPolar | Mag, Theta — magnitude and angle in radians. |
| EngDms | Degs, Mins, Secs. Degs, not Deg: EngDeg is the radians converter and a machine word outranks a field accessor of the same name. |
| EngStat | Avg, Sd, Total — a one-variable summary. Named so rather than Mean/StdDev/Sum because those are stats' words. |
| EngFit | Intercept, Slope, RFit — a fitted curve. RFit is the correlation in the fitted space, which is what a spreadsheet reports. |
| EngDivided | Quotient, Remainder — both halves of a polynomial division. |
| EngUnit | Name, Quantity, Factor, Offset. Factor and Offset take a value to the quantity's base unit. |
| EngConst | CName, Value, Units — a physical constant carrying its own units. |
| Word | Description |
|---|---|
| EngRad(deg) / EngDeg(rad) | Degrees and radians, both ways. |
| EngGradOf(rad) / EngFromGrad(grad) | Gradians, both ways. 100 to the right angle. |
| EngDmsOf(deg) | Decimal degrees to degrees/minutes/seconds → EngDms. The seconds carry the fraction, so a round trip is exact to the precision of the input. |
| EngFromDms(d) | And back. The sign lives on the degrees. |
| Word | Description |
|---|---|
| EngSec / EngCsc / EngCot | The three reciprocals the runtime does not supply. |
| EngAsec / EngAcsc / EngAcot | And their inverses. The first two refuse below |x| = 1. |
| EngHypot(x, y) | The hypotenuse, without squaring into an overflow. |
| EngDistOf(x0, y0, x1, y1) | Distance between two points. |
| EngAngleOf(dx, dy) | The direction of a vector, all four quadrants. |
| EngRectX(mag, theta) / EngRectY(mag, theta) | Polar to rectangular. Two words rather than one returning a pair, because a caller wants one or the other at the point of use. |
| EngPolarOf(x, y) | And rectangular to polar → EngPolar. |
| Word | Description |
|---|---|
| EngSinh / EngCosh / EngTanh | The three. Sinh and Cosh overflow around |x| > 710. |
| EngSech / EngCsch / EngCoth | Their reciprocals. |
| EngAsinh / EngAcosh / EngAtanh | The inverses. Acosh refuses below 1, Atanh at or beyond 1. |
| EngAsech / EngAcsch / EngAcoth | And the reciprocal inverses. |
| Word | Description |
|---|---|
| EngLogBase(base, x) / EngLog2(x) | Logarithms to an arbitrary base, and to 2. |
| EngLog1p(x) / EngExpm1(x) | log(1+x) and e^x - 1, accurate near zero where the naive forms lose most of their significant digits to cancellation. |
| EngNthRoot(x, n) | The real nth root, including the odd root of a negative that x ^ (1/n) gets wrong. Refuses an even root of a negative. |
| Word | Description |
|---|---|
| EngCAdd / EngCSub / EngCMul / EngCDiv | Arithmetic. Division refuses by zero. |
| EngConj(z) | The conjugate. |
| EngCAbs(z) / EngCArg(z) | Modulus and argument. |
| EngCExp / EngCLog / EngCSqrt / EngCPow | The transcendentals, through the polar form. |
| EngCFromPolar(mag, theta) / EngCToPolar(z) | Between the two representations. |
| EngCNear(a, b, tol) | The only comparison offered. = on two complex records answers about the bits, not the numbers. |
| EngCShow(z) | A readable string. Drops the zero part; never prints a plus followed by a minus. |
| Word | Description |
|---|---|
| EngSum / EngMean / EngMedian | The everyday three. |
| EngStdDev / EngStdDevP | Standard deviation, sample (n-1) and population (n). Named for which they are, because the wrong one is invisible. |
| EngVar / EngVarP | Variance, the same two ways. |
| EngMinimum / EngMaximum / EngRangeOf | Extremes and their spread. |
| EngQuantile(xs, p) | The value at fraction p — 0..1. The percentile form is deliberately not wrapped, so one fraction convention holds across the whole surface. |
| EngSkewness / EngKurtosis | Distribution shape. Kurtosis is excess: a normal curve is 0. |
| EngWeightedMean / EngGeoMean | Weighted and geometric averages. |
| EngCorrel / EngCov / EngSpearman | How two series move together — linear, raw, and by rank. |
| EngStat1(xs) | Mean, standard deviation and total in one EngStat. |
| EngNormPdf(x, mean, sd) / EngNormCdf(x, mean, sd) | The normal distribution, taking a raw mean and standard deviation where stats takes a z-score. |
| EngNormInv(p) | The standard normal quantile. |
| EngTCdf / EngTInv / EngChi2Cdf / EngFCdf | Student's t, chi-squared and F. |
| EngFitOf(xs, ys, model) | Fits linear, logarithmic, exponential or power → EngFit. Errors on non-positive inputs for the three log models rather than dropping points. |
| EngLinear() / EngLogModel() / EngExpModel() / EngPowerModel() | Which curve EngFitOf fits. |
| Word | Description |
|---|---|
| EngGcd(a, b) / EngLcm(a, b) | Sign is ignored; the answer is never negative. |
| EngIQuot(a, b) / EngIRem(a, b) | Integer division, truncated toward zero — the remainder takes the sign of the dividend. |
| EngModPow(base, e, m) | Square-and-multiply, so no intermediate leaves 253 even when the exponent is large. |
| EngModInv(a, m) | The modular inverse. Errors when a and m are not coprime, because a caller handed a non-inverse back will not check it. |
| EngIsPrime(n) / EngPrimes(n) | Primality, and every prime up to n. |
| EngFactorize(n) | The prime factors, with multiplicity, in order. |
| EngDivisors(n) / EngTotient(n) | Every divisor, and Euler's totient. |
| Word | Description |
|---|---|
| EngFact(n) | Factorial. Exact to 18; past that a double no longer holds whole numbers exactly. |
| EngComb(n, k) | Unordered selections, computed multiplicatively — EngComb(52, 5) is 2598960 exactly. |
| EngPerm(n, k) | Ordered arrangements. |
| EngFib(n) | Fibonacci, iteratively. |
| EngGammaLn(x) | The log of the gamma function, by Lanczos. The one to reach for: it holds where EngGamma overflows, and every distribution here is built on it. |
| EngGamma(x) | The gamma function itself, by reflection below a half. |
| EngBeta(a, b) | The beta function. |
| Word | Description |
|---|---|
| EngBinomPdf(k, n, p) / EngBinomCdf(k, n, p) | Binomial. The Cdf is summed from zero and is inclusive of k. |
| EngPoissonPdf(k, lambda) / EngPoissonCdf(k, lambda) | Poisson, through EngGammaLn rather than a factorial. |
| EngGeomPdf(k, p) / EngGeomCdf(k, p) | Geometric. k counts trials up to and including the first success, so it starts at 1 — the other convention differs by one and looks reasonable either way. |
| EngHyperPdf(k, draws, wins, popn) | Drawing without replacement. |
| EngExpCdf(x, rate) | The exponential distribution. |
| Word | Description |
|---|---|
| EngDeriv(f, x) | Central difference, step scaled to x. About eight good digits. |
| EngDeriv2(f, x) | The second derivative, on a deliberately larger step. About four. |
| EngIntegrate(f, a, b, n) | Composite Simpson over n intervals. n must be even, and the word says so rather than applying the wrong weights. |
| EngSumOf(f, lo, hi) / EngProdOf(f, lo, hi) | Summation and product over an integer range. |
Fixed-step fourth-order Runge–Kutta, over quotations like the rest of the calculus. No stiffness detection — a stiff equation (one mixing very fast and very slow behaviour) explodes rather than erroring, so pick h for the fastest rate in the problem.
| Word | Description |
|---|---|
| EngOdeStep(f, t, y, h) | One RK4 step for y′ = f(t, y): the value at t + h. Error per step is of order h⁵. |
| EngOdeSolve(f, t0, y0, t1, n) | The trajectory from t0 to t1 in n equal steps: n + 1 values, the first y0 — one per sample point, which is what a plot wants. Refuses n below 1 or fractional. |
| EngOdeSysStep(f, t, ys, h) | One RK4 step for a system: the state is a List Of Number and the derivative answers one the same length. A second-order equation is a first-order pair — the damped driven oscillator is the state { y, y′ }. |
| EngOdeSysAt(f, t0, ys0, t1, n) | The state at t1, in n equal steps. The final state only; a caller who wants the path walks EngOdeSysStep and keeps what they need. |
| Word | Description |
|---|---|
| EngZeroOf(f, lo, hi) | Bisection. Checks the bracket first and errors when there is no sign change in it. |
| EngNewtonOf(f, x0) | Newton's method with a numeric derivative, so you supply one quotation rather than two. Unbracketed and therefore unguarded — it diverges on a bad start, and EngZeroOf is the safer word. |
| EngMinOf(f, lo, hi) / EngMaxOf(f, lo, hi) | Golden-section search. Requires the function to be unimodal on the bracket; given two minima it finds one and does not say which. |
Coefficients are highest power first.
{ 1, 0, 0 - 4 } is x^2 - 4. Reversed, every word here
still returns a number, and it is the wrong number.
| Word | Description |
|---|---|
| EngPolyEval(cs, x) | Horner's method. |
| EngPolyDegree(cs) | Leading zeros do not count. |
| EngPolyAdd / EngPolySub / EngPolyMul / EngPolyScale | Arithmetic, aligned on the lowest power and trimmed afterwards. |
| EngPolyDiv(a, b) | Long division → EngDivided. Both halves, because a caller who wanted only the quotient has usually assumed the remainder is zero. |
| EngPolyDeriv(cs) | Differentiation. |
| EngPolyIntegral(cs, c0) | Integration. The constant is required rather than assumed to be zero. |
| EngPolyArea(cs, a, b) | The definite integral between two points. |
| EngPolyTrim(cs) | Drops leading zeros. |
| EngQuadRoots(a, b, c) | Both roots, always as complex numbers even when they are real — a word with two return types could not be branched on. |
| EngCubicReal(a, b, c, d) | The real roots of a cubic: three by the trigonometric form, one by Cardano's radicals. The length of the answer tells you which case you were in. |
| Word | Description |
|---|---|
| EngLinSpace(lo, hi, n) | n evenly spaced values, reaching the far end exactly. |
| EngGeomSpace(lo, hi, n) | Geometrically spaced. Needs both ends strictly positive: no ratio crosses zero. |
| EngCumSum(xs) | Running total. |
| EngDiff(xs) | Successive differences — one shorter than its input, which is the point. |
| EngMovAvg(xs, w) | Moving average over a window. |
| EngNormalize(xs) | Scaled to 0..1. A flat list becomes zeros rather than dividing by a zero range. |
| EngDotOf(xs, ys) / EngMagOf(xs) | Dot product and Euclidean length of a plain list. |
| Word | Description |
|---|---|
| EngConvert(x, src, dst) | Converts, and errors when the two quantities differ, naming both. |
| EngToBase(x, u) / EngFromBase(x, u) | To and from the quantity's base unit. |
| Length | EngMetre EngKilometre EngCentimetre EngMillimetre EngInch EngFoot EngYard EngMile EngNauticalMile |
| Mass | EngKilogram EngGram EngTonne EngPound EngOunce EngStone |
| Time | EngSecond EngMinute EngHour EngDay |
| Temperature | EngKelvin EngCelsius EngFahrenheit — the only quantity with an offset, and the reason the record carries one. |
| Pressure | EngPascal EngKilopascal EngBar EngPsi EngAtm |
| Energy | EngJoule EngKilojoule EngCalorie EngKwh EngBtu |
| Power | EngWatt EngKilowatt EngHorsepower — mechanical horsepower, the one James Watt sold engines against. |
| Volume | EngLitre EngMillilitre EngCubicMetre EngGallonUk EngGallonUs |
| Speed | EngMps EngKph EngMph EngKnot |
Each returns an EngConst carrying its own units, because a bare
number is the mistake these exist to prevent.
Value(EngLightSpeed()) is the number.
| Word | Description |
|---|---|
| EngLightSpeed / EngPlanck / EngGravitation / EngStdGravity | 299792458 m/s exactly by definition, and the rest. |
| EngElectronCharge / EngElectronMass / EngProtonMass | The particle constants. |
| EngAvogadro / EngBoltzmann / EngGasConstant / EngStefanBoltzmann / EngFaraday | The thermodynamic and chemical ones. |
| EngVacuumPermittivity / EngVacuumPermeability | The electromagnetic ones. |
| EngE() / EngTau() | The two mathematical constants the runtime does not supply. Pi is a builtin and needs no wrapper. |
| Word | Description |
|---|---|
| EngRoundTo(x, n) | Round to n decimal places. |
| EngSigFig(x, n) | Round to n significant figures. Not the same thing: EngSigFig(0.00123456, 3) is 0.00123 and EngRoundTo(0.00123456, 3) is 0.001. |
| EngSci(x, p) | Scientific notation — one digit before the point. |
| EngMetric(x, p) | Engineering notation: the exponent moves in threes and the mantissa carries an SI prefix. 47000 ohms prints as 47k, which is what is written on the part. |
| EngMetricPrefix(k) | The SI prefix for a power-of-1000 exponent, y through Y. |
| EngCommas(x, p) | Grouped thousands. |
| EngFracOf / EngClamp / EngLerp / EngInvLerp / EngRemap / EngSmoothstep | The interpolation family. |
| EngSign(x) | -1, 0 or 1. |
| EngNear(a, b, tol) | The scalar counterpart of EngCNear, and the same warning applies. |
What is deliberately elsewhere. Number bases,
bit operations and truth tables are bool. Symbolic
factoring and symbolic calculus are alg. Linear algebra over
matrix's Matrix is
lin. The hypothesis tests are
stats. And random numbers stay in
math, because wrapping Rnd would make this
machine's purity untrue. Polynomial arithmetic over coefficient lists is not
symbolic algebra, and ships here.
| Machine | Why | |
|---|---|---|
| lin | Summation, the polynomial words under LinCharPoly, and EngZeroOf under LinEigVals. |
| Machine | Why | |
|---|---|---|
| math | Rad, Deg, Angle, Hypot, LogBase and the interpolation family under their Eng names. | |
| seq | Append builds the factor and quotient lists; CountIf is Euler's totient; ZipWith is the dot product. | |
| stats | Every descriptive statistic and distribution this machine offers, and LinFit under the four regression models. | |
| str | ToFixed and Commas build EngSci, EngMetric and EngCommas. |