UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

264 lines 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IntervalDomain = exports.IntervalTop = void 0; const assert_1 = require("../../util/assert"); const logic_1 = require("../../util/logic"); const abstract_domain_1 = require("./abstract-domain"); const lattice_1 = require("./lattice"); const satisfiable_domain_1 = require("./satisfiable-domain"); /* eslint-disable @typescript-eslint/unified-signatures */ /** The Top element of the interval domain as interval [-∞, +∞] */ exports.IntervalTop = [-Infinity, +Infinity]; /** * The interval abstract domain as intervals with possibly infinite bounds representing possible numeric values. * The Bottom element is defined as {@link Bottom} symbol and the Top element is defined as the interval [-∞, +∞]. * @template Value - Type of the constraint in the abstract domain (Top, Bottom, or an actual value) */ class IntervalDomain extends abstract_domain_1.AbstractDomain { constructor(value) { if (Array.isArray(value)) { if (value.some(isNaN) || value[0] > value[1] || value[0] === +Infinity || value[1] === -Infinity) { super(lattice_1.Bottom); } else { super([value[0], value[1]]); } } else { super(value); } } create(value) { return new IntervalDomain(value); } static top() { return new IntervalDomain(exports.IntervalTop); } static bottom() { return new IntervalDomain(lattice_1.Bottom); } static abstract(concrete) { if (concrete === lattice_1.Top) { return IntervalDomain.top(); } else if (concrete.size === 0 || concrete.values().some(isNaN)) { return IntervalDomain.bottom(); } return new IntervalDomain([Math.min(...concrete), Math.max(...concrete)]); } top() { return IntervalDomain.top(); } bottom() { return IntervalDomain.bottom(); } equals(other) { return this.value === other.value || (this.isValue() && other.isValue() && this.value[0] === other.value[0] && this.value[1] === other.value[1]); } leq(other) { return this.value === lattice_1.Bottom || (other.isValue() && other.value[0] <= this.value[0] && this.value[1] <= other.value[1]); } join(other) { const otherValue = other instanceof IntervalDomain ? other.value : other; if (this.value === lattice_1.Bottom) { return this.create(otherValue); } else if (otherValue === lattice_1.Bottom) { return this.create(this.value); } else { return this.create([Math.min(this.value[0], otherValue[0]), Math.max(this.value[1], otherValue[1])]); } } meet(other) { const otherValue = other instanceof IntervalDomain ? other.value : other; if (this.value === lattice_1.Bottom || otherValue === lattice_1.Bottom) { return this.bottom(); } else { return this.create([Math.max(this.value[0], otherValue[0]), Math.min(this.value[1], otherValue[1])]); } } widen(other) { if (this.value === lattice_1.Bottom) { return this.create(other.value); } else if (other.value === lattice_1.Bottom) { return this.create(this.value); } else { return this.create([ this.value[0] <= other.value[0] ? this.value[0] : -Infinity, this.value[1] >= other.value[1] ? this.value[1] : +Infinity ]); } } narrow(other) { if (this.value === lattice_1.Bottom || other.value === lattice_1.Bottom) { return this.bottom(); } else if (Math.max(this.value[0], other.value[0]) > Math.min(this.value[1], other.value[1])) { return this.bottom(); } return this.create([ this.value[0] === -Infinity ? other.value[0] : this.value[0], this.value[1] === +Infinity ? other.value[1] : this.value[1] ]); } concretize(limit) { if (this.value === lattice_1.Bottom) { return new Set(); } else if (!isFinite(this.value[0]) || !isFinite(this.value[1]) || this.value[1] - this.value[0] + 1 > limit) { return lattice_1.Top; } const set = new Set(); for (let x = this.value[0]; x <= this.value[1]; x++) { set.add(x); } return set; } abstract(concrete) { return IntervalDomain.abstract(concrete); } satisfies(value, comparator = satisfiable_domain_1.NumericalComparator.Equal) { switch (comparator) { case satisfiable_domain_1.NumericalComparator.Equal: { if (this.isValue() && this.value[0] <= value && value <= this.value[1]) { return this.value[0] === this.value[1] ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } else { return logic_1.Ternary.Never; } } case satisfiable_domain_1.NumericalComparator.Less: { if (this.isValue() && value < this.value[1]) { return value < this.value[0] ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } else { return logic_1.Ternary.Never; } } case satisfiable_domain_1.NumericalComparator.LessOrEqual: { if (this.isValue() && value <= this.value[1]) { return value <= this.value[0] ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } else { return logic_1.Ternary.Never; } } case satisfiable_domain_1.NumericalComparator.Greater: { if (this.isValue() && this.value[0] <= value) { return this.value[1] <= value ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } else { return logic_1.Ternary.Never; } } case satisfiable_domain_1.NumericalComparator.GreaterOrEqual: { if (this.isValue() && this.value[0] < value) { return this.value[1] < value ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } else { return logic_1.Ternary.Never; } } default: { (0, assert_1.assertUnreachable)(comparator); } } } /** * Adds another abstract value to the current abstract value by adding the two lower and upper bounds, respectively. */ add(other) { const otherValue = other instanceof IntervalDomain ? other.value : other; if (this.value === lattice_1.Bottom || otherValue === lattice_1.Bottom) { return this.bottom(); } else { return this.create([this.value[0] + otherValue[0], this.value[1] + otherValue[1]]); } } /** * Subtracts another abstract value from the current abstract value by subtracting the two lower and upper bounds from each other, respectively. */ subtract(other) { const otherValue = other instanceof IntervalDomain ? other.value : other; if (this.value === lattice_1.Bottom || otherValue === lattice_1.Bottom) { return this.bottom(); } else { return this.create([this.value[0] - otherValue[0], this.value[1] - otherValue[1]]); } } /** * Creates the minimum between the current abstract value and another abstract value by creating the minimum of the two lower and upper bounds, respectively. */ min(other) { const otherValue = other instanceof IntervalDomain ? other.value : other; if (this.value === lattice_1.Bottom || otherValue === lattice_1.Bottom) { return this.bottom(); } else { return this.create([Math.min(this.value[0], otherValue[0]), Math.min(this.value[1], otherValue[1])]); } } /** * Creates the maximum between the current abstract value and another abstract value by creating the maximum of the two lower and upper bounds, respectively. */ max(other) { const otherValue = other instanceof IntervalDomain ? other.value : other; if (this.value === lattice_1.Bottom || otherValue === lattice_1.Bottom) { return this.bottom(); } else { return this.create([Math.max(this.value[0], otherValue[0]), Math.max(this.value[1], otherValue[1])]); } } /** * Extends the lower bound of the current abstract value down to -∞. */ widenDown() { if (this.value === lattice_1.Bottom) { return this.bottom(); } else { return this.create([-Infinity, this.value[1]]); } } /** * Extends the upper bound of the current abstract value up to +∞. */ widenUp() { if (this.value === lattice_1.Bottom) { return this.bottom(); } else { return this.create([this.value[0], +Infinity]); } } toJson() { if (this.value === lattice_1.Bottom) { return this.value.description; } return this.value; } toString() { if (this.value === lattice_1.Bottom) { return lattice_1.BottomSymbol; } return `[${isFinite(this.value[0]) ? this.value[0] : '-∞'}, ${isFinite(this.value[1]) ? this.value[1] : '+∞'}]`; } isTop() { return this.value !== lattice_1.Bottom && this.value[0] === -Infinity && this.value[1] === +Infinity; } isBottom() { return this.value === lattice_1.Bottom; } isValue() { return this.value !== lattice_1.Bottom; } } exports.IntervalDomain = IntervalDomain; //# sourceMappingURL=interval-domain.js.map