@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
264 lines • 10.1 kB
JavaScript
"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