Intervals, domains and signs
Four classes describe sets of real numbers and the sign of an expression:
| Class | Represents |
|---|---|
Interval | One interval, with exact bounds |
Domain | A union of intervals |
TableOfSigns | The sign of a polynom or of a product of factors |
LogicalSet | A set expression such as `A&B |
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.
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 // 1Besides the bounds, a string can be one of these shortcuts.
| Notation | Meaning |
|---|---|
RR | The 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 |
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.
new Interval('1;5') // throws ParseErrorIntersection
new Interval(']1;5]').intersect(new Interval('[3;8[')).display // [3;5]
new Interval(']1;2[').intersect(new Interval(']3;4[')).isEmpty() // trueintersect 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.
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 // 2Union is written uu. The notation also accepts RR and O/, as well as the following shortcuts.
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
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 // RRA Domain can be built from an Interval, and intervals returns the list of its intervals as copies.
Domain.fromIntervals([new Interval('[0;1]'), new Interval('[1;2]')]).display // [0;2]
new Domain(']1;5[').intervals.length // 1TableOfSigns
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.
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.
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.
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 // 2The 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.
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.
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
| Member | Description |
|---|---|
fromBounds(left: Solution | null, right: Solution | null, leftOpen: boolean, rightOpen: boolean): Interval | Construct an interval from explicit bounds and open/closed flags. A null bound encodes ±∞ and is always forced open, regardless of the flag passed. |
empty(): Interval | The empty set ∅. |
real(): Interval | The whole real line ]-∞; +∞[. |
point(x: Solution): Interval | A closed singleton {x}. |
fromString(value: string): Interval | Parse 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
| Member | Description |
|---|---|
tex: string | LaTeX rendering of the current state (terminal output, not chainable). |
display: string | ASCIIMath rendering of the current state (terminal output, not chainable). |
left: Solution | null | Left bound as a Solution, or null for -∞. |
right: Solution | null | Right bound as a Solution, or null for +∞. |
leftOpen: boolean | True when the left bound is excluded. An infinite bound is always open. |
rightOpen: boolean | True when the right bound is excluded. An infinite bound is always open. |
Methods
| Member | Description |
|---|---|
parse(value: string | Interval): Interval | Replace the interval with a string or another interval. See fromString for the notation. Throws: {ParseError} if the string is not an interval. |
clone(): Interval | Full duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy. |
copy(value: Interval): Interval | Copy 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(): boolean | True for the empty set, and for bounds that enclose nothing (]2;2[, [3;1]). |
isPoint(): boolean | True iff the interval is a closed singleton [x; x]. |
isReal(): boolean | True iff the interval is the whole real line ]-∞; +∞[. |
contains(x: Fraction | number): boolean | True 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): Interval | Intersection 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
| Member | Description |
|---|---|
fromIntervals(intervals: Interval[]): Domain | Build a domain from intervals. They are sorted, and the overlapping or touching ones are merged. |
fromInterval(interval: Interval): Domain | Build a domain from a single interval. |
empty(): Domain | The empty domain ∅. |
real(): Domain | The whole real line ℝ. |
fromString(value: string): Domain | Parse 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
| Member | Description |
|---|---|
tex: string | LaTeX rendering of the current state (terminal output, not chainable). |
display: string | ASCIIMath rendering of the current state (terminal output, not chainable). |
intervals: Interval[] | Read-only access to the normalized interval list. |
length: number | Number of intervals, once merged. |
Methods
| Member | Description |
|---|---|
parse(value: string | Domain | Interval): Domain | Replace 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(): Domain | Full duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy. |
copy(value: Domain): Domain | Copy 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(): boolean | True when the domain has no interval. |
contains(x: Fraction | number): boolean | True when the number belongs to one of the intervals. |
intersect(other: Domain): Domain | O(n·m) pairwise intersection — negligible for the typical 1–3 interval count. The result is normalized (sorted, non-overlapping). |
union(other: Domain): Domain | Returns the union, automatically normalized (overlapping intervals are merged). |
complement(): Domain | Complement of this domain in ℝ. Each interval boundary flips its open/closed sense. |
TableOfSigns
Constructors
new TableOfSigns(value?: InputAlgebra<PolyFactor \| Factor \| Polynom>)
Static members
| Member | Description |
|---|---|
fromPolynom(p: Polynom): TableOfSigns | Build the table of a polynom. |
fromFactor(f: Factor): TableOfSigns | Build the table of a factor. A negative power makes its roots poles, and an even power removes the negative signs. |
fromPolyFactor(pf: PolyFactor): TableOfSigns | Build the table of a product of factors. The table keeps one row per factor, shown with withFactors. |
Properties and getters
| Member | Description |
|---|---|
tex: string | LaTeX 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: string | ASCIIMath rendering of the current state (terminal output, not chainable). Throws: {NotImplementedError} Always; ASCII display is not yet implemented. |
withFactors: TableOfSigns | Show 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
| Member | Description |
|---|---|
parse(value: InputAlgebra<PolyFactor | Factor | Polynom>): TableOfSigns | Replace the table with the one of a polyfactor, a factor, a polynom, or anything a factor accepts. |
clone(): TableOfSigns | Full duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy. |
copy(value: TableOfSigns): TableOfSigns | Copy 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): TableOfSigns | Override the label of the product row (default: f(x)). |
withTkzTabOptions(lgt: number, espcl: number): TableOfSigns | Override tkz-tab layout options. |
positive(): Domain | Intervals where f(x) > 0 (open boundaries at zeros). |
nonNegative(): Domain | Intervals where f(x) ≥ 0 (closed at zeros). |
negative(): Domain | Intervals where f(x) < 0. |
nonPositive(): Domain | Intervals where f(x) ≤ 0. |
domain(): Domain | Definition 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
| Member | Description |
|---|---|
fromString(value: string): LogicalSet | Build 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
| Member | Description |
|---|---|
tex: string | LaTeX rendering of the current state (terminal output, not chainable). |
display: string | ASCIIMath 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
| Member | Description |
|---|---|
parse(value: string): LogicalSet | Replace the expression with a string. Throws: {ParseError} if the expression is malformed. |
clone(): LogicalSet | Full duplicate of this instance, including display state (selected form and options set via as*** / with***). Distinct from copy. |
copy(value: LogicalSet): LogicalSet | Copy 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): LogicalSet | Sets the label used for the exterior (universal complement) zone in Venn output. Default: 'E'. |
evaluate(values: Record<string, boolean>): boolean | Evaluates 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()). |