Library for representing surreal numbers in Conway normal form.
Also includes functionalities for dealing with ordinal numbers and transfinite sequences.
Represents a surreal number that can be expressed in terms of a finite expression in Conway normal form.
Internally the sum is represented as an array of [exponent, coefficient]
pairs
sorted by the exponent descending with zero coefficients discarded.
The exponent is either a real number (if possible) or a Conway
(if it must
be represented as an expression).
The real coefficients can be number
or bigint
.
The upper limit of this representation is epsilon_0 (e0 = w^e0
) and the lower
limit is its negation.
The upper limit of the birthday is the ordinal
The Conway
class is re-used for representing ordinal numbers in Cantor normal form.
The isOrdinal
property determines if a particular value is an ordinal number,
and there are ordinal arithmetic operations as well.
The first type parameter of Conway
keeps track of a value is definitely an ordinal
number of not. Conway<true>
indicates the values of the type are definitely
ordinal numbers, while Conway
or Conway<boolean>
does not care if the values
are ordinal numbers or not.
The type alias Ord
is defined to be Conway
, while Ord0 = Real | Ord
. Most
functions that reference to ordinal numbers use these type aliases.
Some functions overload based on the isOrdinal
property of the arguments to
determine if the return type is ordinal or not.
These functions are: mono, mono1, ensure, add, mult
// positive bigints are inferred to be Ord0
mult(one, mono1(4n)) // mult(Ord0, Ord0): Ord0
add(one, 3n) // add(Ord0, Ord0): Ord0
A transfinite sequence is a sequence indexed by an ordinal number.
The index cannot be unwrapped in Seq
-related APIs.
The length
property determines the order type of the sequence.
interface Seq<T> {
length: Ord;
index(index: Ord): T;
}
If all elements of a Seq<T>
equals to the first element, the implementations
of Seq<T>
can set the field isConstant = true
. Some sequence functions will
take isConstant
into account simplified the sequences generated.
If the Seq<T>
has length zero or one, isConstant
should be true
.
import { ... } from "conway/seq";
// order type w, [1, 2, 3, 1, 2, 3, 1, 2, 3, ...]
cycleArray([1, 2, 3]);
// order type w + 2, [ 2, 3, 4, 2, 3, 4, ... | 0, 1 ]
const c2 = concat(cycleArray([2, 3, 4]), fromArray([0, 1]));
c2.index(unit.add(1)); // c2[w + 1] = 1
// Natural numbers reordered with odd numbers before even numbers
// order type w.2, [ 0, 2, 4, 6, ... | 1, 3, 5, 7, ... ]
concat(mapNatural((i) => i * 2), mapNatural((i) => i * 2 + 1));
Real numbers (type alias Real
) are represented as JavaScript
number
or bigint
.
Many surreal and ordinal operations take either a real number or
a Conway
as an input or output, allowing the caller to avoid
wrapping finite values using fromReal
or ensure
and the function
can return an unwrapped real number if possible.
The type alias Conway0
and Ord0
correspond to either a value in
either wrapped or unwrapped form.
The exponents in the array of tuple representations of Conway/Cantor normal forms area always unwrapped reals if possible.
The maybeDowngrade
function takes a Conway
and returns an
unwrapped Real
if possible, otherwise the argument itself is
returned.
type Conway = ...;
type Conway0 = Conway | Real;
type Ord = ...;
type Ord0 = Ord | Real;
import { ... } from "conway/op";
// sub(a: Conway0, b: Conway0): Conway0
mult(sub(num1, num2))
sub(3, 2) // => 1 : Real
When a module containing the operation is imported, it can be used as a class
method of Conway
as well.
The class methods can take unwrapped values as arguments, but they will never return unwrapped values.
// num1, num2 : Conway
// num.sub : (b: Conway0): Conway
num1.sub(num2)
fromReal(3).sub(1) // => fromReal(2) : Conway
fromReal(3).sub(fromReal(1)) // => fromReal(2) : Conway
import { ... } from "conway/op"
zero // 0
one // 1
unit // w (omega)
real(r) // real number value
mono(coeff, exponent) // w^exponent . coeff
mono1(exponent) // w^exponent
new Conway([[p1, c1], [p2, c2], [p3, c3]]); // from a list of [power, coeff] tuples
zero
, one
and unit
are typed as Ord
and this can cause type errors for an
mutable variable intended not to be ordinals. In that case, specify the type
for the variable explicitly.
let sum: Conway = zero;
for (...) {
const d: Conway = ...;
sum = add(sum, d);
}
Applies to both surreal numbers and ordinal numbers.
Operation | Code | Description |
---|---|---|
zero |
Zero | |
one |
One | |
unit |
Omega | |
create([[p0, c0], ...]) or new Conway(...)
|
Construct from Conway/Cantor normal form given an array or Iterable of [power, coefficient] tuples. |
|
mono1(p) |
Monomial (given coefficient of 1 and power p ) |
|
mono(c, p) |
Monomial (given coefficient c and power p ) |
|
N/A | ensure(r) |
Given a real number, convert it to Conway , otherwise return the value itself. |
N/A | fromReal(r) |
Given a real number, construct a Conway representing it. |
Let a, b
be ordinal numbers or surreal numbers.
Operation | Code | Description |
---|---|---|
eq(a, b) |
Equals | |
ne(a, b) |
Not equals | |
gt(a, b) |
Greater than | |
ge(a, b) |
Greater than or equals | |
lt(a, b) |
Less than | |
le(a, b) |
Less than or equals | |
N/A | compare(a, b) |
Array#sort comparator |
Let a
be a surreal number or ordinal number.
Code | Description |
---|---|
Conway |
The type for Conway/Cantor normal forms. |
a.length |
Number of terms |
a.getByIndex(i) |
Get the [power, coefficient] tuple by index i |
a.has(p) |
Has non-zero term for power p ? |
a.get(p) |
Get coefficient (or zero) for power p |
[...a] , a[Symbol.iterator] |
Iterate the [power, coefficient] tuples in descending order bypower |
+a , a[Symbol.toPrimitive]("number") |
Convert the value to a number . If the value is infinite +Infinity or -Infinity is returned |
a.toString() |
Return a readable representation for the term |
a.toJson() |
Return a JSON structure for the term |
Let a, b
be surreal numbers.
Operation | Code | Description |
---|---|---|
add(a, b) |
Addition | |
sub(a, b) |
Subtraction | |
mult(a, b) |
Multiplication | |
divRem(a, b) |
Surreal long division (returns quotient, remainder tuple) | |
divRemIters(a, b, n) |
Repeat surreal long division by n times and returns (quotient, remainder) |
|
neg(a) |
Negation | |
pow(a, b) |
Exponentiation | |
exp(a) |
Exponential | |
log(a, n) |
Logarithm (to the first n terms if infinite sum is needed) |
Let a = inf + r + low
be a surreal number that can be decomposed into a sum of
- purely infinite value
inf
$\ \in \mathbb{J}$ - real value
r
$\ \in \mathbb{R}$ - and purely infinitesimal value
low
$\ \in \mathbf{No}^\prec$
Operation | Code | Description |
---|---|---|
birthday(a) |
Birthday of a surreal number (returns an ordinal number) | |
inf |
a.infinitePart |
Purely infinite part, or zero |
r |
a.realPart |
Real part, or zero |
low |
a.infinitesimalPart |
Purely infinitesimal part, or zero |
N/A | a.realValue |
If a is purely real (inf = low = 0 ), its real part. Otherwise, null
|
isPositive(a) |
Is a positive? |
|
isNegative(a) |
Is a negative? |
|
isZero(a) |
a equals zero? |
|
isOne(a) |
a equals one? |
|
isUnit(a) |
a equals omega? |
|
isAboveReals(a) |
Is a infinite (or inf is positive)? |
|
isBelowNegativeReals(a) |
Is -a infinite (or inf is negative)? |
|
isOrdinal(a) |
a represents an ordinal number? |
Let a, b
be ordinal numbers.
Operation | Code | Description |
---|---|---|
ordinalAdd(a, b) |
Ordinal addition | |
ordinalRightSub(b, a) |
Ordinal right subtraction (solution to |
|
ordinalMult(a, b) |
Ordinal multiplication | |
ordinalDivRem(b, a) |
Ordinal division and remainder (returns [q, r] where |
|
ordinalPow(a, b) |
Ordinal exponentiation | |
add(a, b) |
Natural sum (implemented as surreal sum) | |
mult(a, b) |
Natural product (implemented as surreal product) |
To install dependencies:
bun install
To run:
bun run index.ts