Skip to content

Intervals, domains and signs ​

Four classes describe sets of real numbers and the sign of an expression:

ClassRepresents
IntervalOne interval, with exact bounds
DomainA union of intervals
TableOfSignsThe sign of a polynom or of a product of factors
LogicalSetA set expression such as `A&B
js
import {Interval, Domain, TableOfSigns, LogicalSet} from 'pimath'

Interval ​

An Interval is written with the French notation: ]a;b[ is open, [a;b] is closed, ]-oo;3] is unbounded. The bounds are exact Solution objects, so ]-sqrt(2);3/2[ is valid.

js
const I = new Interval(']1;5]')

I.display // ]1;5]
I.tex // ]1;5]
I.contains(5) // true
I.contains(1) // false
I.leftOpen // true
I.left.display // 1

Besides the bounds, a string can be one of these shortcuts.

NotationMeaning
RRThe real line ]-oo;+oo[
RR_+, RR_-[0;+oo[, ]-oo;0]
RR_+**, RR_-**]0;+oo[, ]-oo;0[
{a}The single point a
O/The empty set
js
new Interval('RR_+').display // [0;+oo[
new Interval('{3}').display // {3}
new Interval('O/').isEmpty() // true
new Interval(']-sqrt(2);3/2[').contains(1) // true
Interval.real().tex // ]-\infty;+\infty[

An unrecognised string throws a ParseError.

js
new Interval('1;5') // throws ParseError

Intersection ​

js
new Interval(']1;5]').intersect(new Interval('[3;8[')).display // [3;5]
new Interval(']1;2[').intersect(new Interval(']3;4[')).isEmpty() // true

intersect never returns null: the result is an empty interval when the two do not overlap.

Domain ​

A Domain is a union of intervals. It is always kept sorted, and intervals that overlap or touch are merged.

js
const D = new Domain(']-oo;1[uu]3;+oo[')

D.display // ]-oo;1[uu]3;+oo[
D.tex // ]-\infty;1[ \cup ]3;+\infty[
D.contains(2) // false
D.contains(0) // true
D.length // 2

Union is written uu. The notation also accepts RR and O/, as well as the following shortcuts.

js
new Domain('RR^**').display // RR\{0}
new Domain('RR\\{1;2}').display // RR\{1;2}
new Domain('{1;2;3}').display // {1}uu{2}uu{3}

Set operations ​

js
const A = new Domain(']-oo;3]')
const B = new Domain(']1;5[')

A.intersect(B).display // ]1;3]
A.union(B).display // ]-oo;5[
A.complement().display // ]3;+oo[
Domain.empty().complement().display // RR

A Domain can be built from an Interval, and intervals returns the list of its intervals as copies.

js
Domain.fromIntervals([new Interval('[0;1]'), new Interval('[1;2]')]).display // [0;2]
new Domain(']1;5[').intervals.length // 1

TableOfSigns ​

A TableOfSigns is built from a Polynom, a Factor or a PolyFactor. It computes the roots, the sign between them, and the intervals where the expression is positive or negative.

js
const T = new TableOfSigns(new Polynom('x^2-1'))

T.roots.map(r => r.display) // [ '-1', '1' ]
T.signs // [ '+', 'z', '-', 'z', '+' ]
T.positive().display // ]-oo;-1[uu]1;+oo[
T.negative().display // ]-1;1[
T.nonNegative().display // ]-oo;-1]uu[1;+oo[
T.nonPositive().display // [-1;1]
T.zeros().map(r => r.display) // [ '-1', '1' ]

signs has one entry per interval and one per root: the even positions are the signs between the roots, and the odd positions are the value at each root. The signs are +, -, z (zero), d (the value is undefined at a pole), h (hatched, the expression is not defined on the whole interval) and t (a factor has no root there).

With a quotient, the roots of the denominator are poles.

js
const Q = new TableOfSigns(new PolyFactor('(x-1)/(x+2)'))

Q.signs // [ '+', 'd', '-', 'z', '+' ]
Q.poles().map(r => r.display) // [ '-2' ]
Q.zeros().map(r => r.display) // [ '1' ]
Q.domain().display // RR\{-2}
Q.positive().display // ]-oo;-2[uu]1;+oo[

LaTeX ​

tex writes the code of a tkz-tab figure, inside a tikzpicture environment. withFactors adds a row for each factor of a PolyFactor, withLabel renames the row of the product and withTkzTabOptions(lgt, espcl) changes the size of the table. These options are chainable.

js
new TableOfSigns(new Polynom('x^2-1')).withLabel('g(x)').tex.includes('g(x)') // true
new TableOfSigns(new PolyFactor('(x-1)(x+2)')).withFactors.factors.length // 2

The display output is not implemented and throws a NotImplementedError.

LogicalSet ​

A LogicalSet is an expression on named sets: & is the intersection, | the union, ! the complement and - the difference.

js
const S = new LogicalSet('A&B')

S.display // A ∩ B
S.tex // A \cap B
new LogicalSet('A&!B').tex // A \cap \overline{ B }
S.variables // [ 'A', 'B' ]

& and | have the same priority and are applied from left to right: A|B&C is (A|B)&C. Use parentheses for any other grouping. A malformed expression throws a ParseError.

Evaluate and draw ​

evaluate tells whether an element belongs to the set, given its membership of each variable. venn lists the zones of a Venn diagram that belong to the set.

js
const S = new LogicalSet('A&B')

S.evaluate({A: true, B: true}) // true
S.evaluate({A: true, B: false}) // false
new LogicalSet('A|B').vennAB() // [ 'A', 'AB', 'B' ]
new LogicalSet('!A').vennAB() // [ 'B', 'E' ]
new LogicalSet('!A').withUniverse('U').vennAB() // [ 'B', 'U' ]
new LogicalSet('A&B&C').vennABC() // [ 'ABC' ]

A zone is named by the letters of the sets it belongs to, in alphabetical order. The zone outside of every set is E, or the name given to withUniverse.

Members ​

Interval ​

Constructors

  • new Interval(value?: string \| Interval)

Static members

MemberDescription
fromBounds(left: Solution | null, right: Solution | null, leftOpen: boolean, rightOpen: boolean): IntervalConstruct an interval from explicit bounds and open/closed flags. A null bound encodes ±∞ and is always forced open, regardless of the flag passed.
empty(): IntervalThe empty set ∅.
real(): IntervalThe whole real line ]-∞; +∞[.
point(x: Solution): IntervalA closed singleton {x}.
fromString(value: string): IntervalParse a French-notation interval string. Supported forms: - O/ → empty set - RR → ]-∞; +∞[ - RR_+, RR_- → [0; +∞[ or ]-∞; 0] - RR_+**, RR_-** → ]0; +∞[ or ]-∞; 0[ - {a} → singleton {a} - ]a;b[, [a;b], [a;b[, ]a;b] → general (a, b parsed via Solution.fromString or ±oo) RR^** (= ℝ*) is NOT a single interval — use Domain.fromString instead. Throws: {ParseError} on unrecognised format.

Properties and getters

MemberDescription
tex: stringLaTeX rendering of the current state (terminal output, not chainable).
display: stringASCIIMath rendering of the current state (terminal output, not chainable).
left: Solution | nullLeft bound as a Solution, or null for -∞.
right: Solution | nullRight bound as a Solution, or null for +∞.
leftOpen: booleanTrue when the left bound is excluded. An infinite bound is always open.
rightOpen: booleanTrue when the right bound is excluded. An infinite bound is always open.

Methods

MemberDescription
parse(value: string | Interval): IntervalReplace the interval with a string or another interval. See fromString for the notation. Throws: {ParseError} if the string is not an interval.
clone(): IntervalFull duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy.
copy(value: Interval): IntervalCopy the mathematical state of another instance into this (display state is NOT transferred — the receiver keeps its own formatting). Entry point used when constructing/parsing from a same-type input. Distinct from clone. Classes without a real implementation throw NotImplementedError.
isEmpty(): booleanTrue for the empty set, and for bounds that enclose nothing (]2;2[, [3;1]).
isPoint(): booleanTrue iff the interval is a closed singleton [x; x].
isReal(): booleanTrue iff the interval is the whole real line ]-∞; +∞[.
contains(x: Fraction | number): booleanTrue when the number is in the interval. The bounds are compared by their decimal value, and an empty interval or a NaN contains nothing.
intersect(other: Interval): IntervalIntersection of two intervals. Returns an empty interval (not null) when the intervals do not overlap — check isEmpty() on the result.

Domain ​

Constructors

  • new Domain(value?: string \| Domain \| Interval)

Static members

MemberDescription
fromIntervals(intervals: Interval[]): DomainBuild a domain from intervals. They are sorted, and the overlapping or touching ones are merged.
fromInterval(interval: Interval): DomainBuild a domain from a single interval.
empty(): DomainThe empty domain ∅.
real(): DomainThe whole real line ℝ.
fromString(value: string): DomainParse a French-notation domain string. Supported forms: - O/ → ∅ - RR → ℝ - RR^** → ℝ* = ]-∞;0[ ∪ ]0;+∞[ - RR\{a;b;…} → ℝ minus a finite set of points - {a} → closed singleton (delegated to Interval.fromString) - {a;b;…} → union of closed singletons - I1uuI2uu… → union of intervals (each parsed via Interval.fromString) - Any single-interval string → delegated to Interval.fromString Throws: {ParseError} on unrecognised format.

Properties and getters

MemberDescription
tex: stringLaTeX rendering of the current state (terminal output, not chainable).
display: stringASCIIMath rendering of the current state (terminal output, not chainable).
intervals: Interval[]Read-only access to the normalized interval list.
length: numberNumber of intervals, once merged.

Methods

MemberDescription
parse(value: string | Domain | Interval): DomainReplace the domain with a string, an interval or another domain. See fromString for the notation. Throws: {ParseError} if the string is not a domain.
clone(): DomainFull duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy.
copy(value: Domain): DomainCopy the mathematical state of another instance into this (display state is NOT transferred — the receiver keeps its own formatting). Entry point used when constructing/parsing from a same-type input. Distinct from clone. Classes without a real implementation throw NotImplementedError.
isEmpty(): booleanTrue when the domain has no interval.
contains(x: Fraction | number): booleanTrue when the number belongs to one of the intervals.
intersect(other: Domain): DomainO(n·m) pairwise intersection — negligible for the typical 1–3 interval count. The result is normalized (sorted, non-overlapping).
union(other: Domain): DomainReturns the union, automatically normalized (overlapping intervals are merged).
complement(): DomainComplement of this domain in ℝ. Each interval boundary flips its open/closed sense.

TableOfSigns ​

Constructors

  • new TableOfSigns(value?: InputAlgebra<PolyFactor \| Factor \| Polynom>)

Static members

MemberDescription
fromPolynom(p: Polynom): TableOfSignsBuild the table of a polynom.
fromFactor(f: Factor): TableOfSignsBuild the table of a factor. A negative power makes its roots poles, and an even power removes the negative signs.
fromPolyFactor(pf: PolyFactor): TableOfSignsBuild the table of a product of factors. The table keeps one row per factor, shown with withFactors.

Properties and getters

MemberDescription
tex: stringLaTeX output using tkz-tab. Wrap in a tikzpicture environment. Options: - withFactors — add a sign row per factor (only for PolyFactor input) - withLabel — change the label of the product row (default: f(x)) - withTkzTabOptions — override lgt and espcl (default: 2, 1.2)
display: stringASCIIMath rendering of the current state (terminal output, not chainable). Throws: {NotImplementedError} Always; ASCII display is not yet implemented.
withFactors: TableOfSignsShow individual factor sign rows in the tkz-tab output (only for PolyFactor).
roots: Solution[]Sorted roots (zeros, poles, holes).
signs: TableOfSignsValues[]Sign array of length 2n+1 (even = interval sign, odd = sign at root).
factors: { factor: Factor; roots: Solution[]; signs: TableOfSignsValues[]; }[]Per-factor sign rows (only populated when built from a PolyFactor). Sign arrays are aligned to the global root list: a 't' value at an odd (root) position means the factor has no root there — that global root belongs to another factor.

Methods

MemberDescription
parse(value: InputAlgebra<PolyFactor | Factor | Polynom>): TableOfSignsReplace the table with the one of a polyfactor, a factor, a polynom, or anything a factor accepts.
clone(): TableOfSignsFull duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy.
copy(value: TableOfSigns): TableOfSignsCopy the mathematical state of another instance into this (display state is NOT transferred — the receiver keeps its own formatting). Entry point used when constructing/parsing from a same-type input. Distinct from clone. Classes without a real implementation throw NotImplementedError.
withLabel(label: string): TableOfSignsOverride the label of the product row (default: f(x)).
withTkzTabOptions(lgt: number, espcl: number): TableOfSignsOverride tkz-tab layout options.
positive(): DomainIntervals where f(x) > 0 (open boundaries at zeros).
nonNegative(): DomainIntervals where f(x) ≥ 0 (closed at zeros).
negative(): DomainIntervals where f(x) < 0.
nonPositive(): DomainIntervals where f(x) ≤ 0.
domain(): DomainDefinition domain: excludes poles ('d'), holes ('h'), and unresolved slots (''). Includes zeros ('z') and sign-changing roots at both interval and root positions — so the zero polynomial, whose sole interval slot carries 'z', correctly yields ℝ.
zeros(): Solution[]Roots where f(x) = 0.
poles(): Solution[]Points where f is undefined (vertical asymptotes).

LogicalSet ​

Constructors

  • new LogicalSet(value?: string)

Static members

MemberDescription
fromString(value: string): LogicalSetBuild a set expression from a string such as "A&B|!C" (& intersection, | union, ! complement, - difference). Throws: {ParseError} if the expression is malformed.

Properties and getters

MemberDescription
tex: stringLaTeX rendering of the current state (terminal output, not chainable).
display: stringASCIIMath rendering of the current state (terminal output, not chainable).
rpn: RpnToken[]The expression in reverse Polish notation, as the tokens produced by the parser.
variables: string[]The names of the sets used in the expression, without duplicates, in order of appearance.

Methods

MemberDescription
parse(value: string): LogicalSetReplace the expression with a string. Throws: {ParseError} if the expression is malformed.
clone(): LogicalSetFull duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy.
copy(value: LogicalSet): LogicalSetCopy the mathematical state of another instance into this (display state is NOT transferred — the receiver keeps its own formatting). Entry point used when constructing/parsing from a same-type input. Distinct from clone. Classes without a real implementation throw NotImplementedError.
withUniverse(name: string): LogicalSetSets the label used for the exterior (universal complement) zone in Venn output. Default: 'E'.
evaluate(values: Record<string, boolean>): booleanEvaluates the expression as a predicate: given boolean membership values for each variable, returns whether the set expression holds. Missing keys default to false (not a member). The values argument is not mutated. The - operator (set difference) is supported: A - B is true iff the element is in A and not in B. Malformed RPN (underflowed stack) returns false rather than throwing.
venn(...variables: string[]): string[]Returns the Venn diagram zones for n sets that satisfy the expression. Each variable must be a single character; zone names are sorted concatenations of those characters (e.g. 'AB' = A∩B only). The exterior zone is labelled by withUniverse() (default 'E'). Variables in the expression but absent from variables are treated as ∅. Throws: {InvalidArgumentError} if no variables are provided or any is not a single character.
vennAB(): string[]Shortcut for venn('A', 'B'). Zone names: 'A', 'B', 'AB', plus the exterior label (default 'E', configurable via withUniverse()).
vennABC(): string[]Shortcut for venn('A', 'B', 'C'). Zone names: 'A', 'B', 'C', 'AB', 'AC', 'BC', 'ABC', plus the exterior label (default 'E', configurable via withUniverse()).

Released under the MIT License.