@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
232 lines • 8.79 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 value_abstract_domain_1 = require("./value-abstract-domain");
/** 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(Number.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);
}
get lower() {
return (this.isValue() ? this.value[0] : lattice_1.Bottom);
}
get upper() {
return (this.isValue() ? this.value[1] : lattice_1.Bottom);
}
from(...values) {
if (values.length === 0) {
return this.bottom();
}
return this.create([Math.min(...values), Math.max(...values)]);
}
static top() {
return new this(exports.IntervalTop);
}
static bottom() {
return new this(lattice_1.Bottom);
}
static from(...values) {
if (values.length === 0) {
return this.bottom();
}
return new this([Math.min(...values), Math.max(...values)]);
}
top() {
return this.create(exports.IntervalTop);
}
bottom() {
return this.create(lattice_1.Bottom);
}
equalsValue(other) {
return this.lower === other.lower && this.upper === other.upper;
}
leqValue(other) {
return other.lower <= this.lower && this.upper <= other.upper;
}
joinValue(other) {
return this.create([Math.min(this.lower, other.lower), Math.max(this.upper, other.upper)]);
}
meetValue(other) {
return this.create([Math.max(this.lower, other.lower), Math.min(this.upper, other.upper)]);
}
widenValue(other) {
return this.create([
this.lower <= other.lower ? this.lower : -Infinity,
this.upper >= other.upper ? this.upper : +Infinity
]);
}
narrowValue(other) {
if (Math.max(this.lower, other.lower) > Math.min(this.upper, other.upper)) {
return this.bottom();
}
return this.create([
this.lower === -Infinity ? other.lower : this.lower,
this.upper === +Infinity ? other.upper : this.upper
]);
}
satisfies(value, comparator = value_abstract_domain_1.NumericalComparator.Equal) {
switch (comparator) {
case value_abstract_domain_1.NumericalComparator.Equal: {
if (this.isValue() && this.lower <= value && value <= this.upper) {
return this.lower === this.upper ? logic_1.Ternary.Always : logic_1.Ternary.Maybe;
}
else {
return logic_1.Ternary.Never;
}
}
case value_abstract_domain_1.NumericalComparator.Less: {
if (this.isValue() && value < this.upper) {
return value < this.lower ? logic_1.Ternary.Always : logic_1.Ternary.Maybe;
}
else {
return logic_1.Ternary.Never;
}
}
case value_abstract_domain_1.NumericalComparator.LessOrEqual: {
if (this.isValue() && value <= this.upper) {
return value <= this.lower ? logic_1.Ternary.Always : logic_1.Ternary.Maybe;
}
else {
return logic_1.Ternary.Never;
}
}
case value_abstract_domain_1.NumericalComparator.Greater: {
if (this.isValue() && this.lower <= value) {
return this.upper <= value ? logic_1.Ternary.Always : logic_1.Ternary.Maybe;
}
else {
return logic_1.Ternary.Never;
}
}
case value_abstract_domain_1.NumericalComparator.GreaterOrEqual: {
if (this.isValue() && this.lower < value) {
return this.upper < value ? logic_1.Ternary.Always : logic_1.Ternary.Maybe;
}
else {
return logic_1.Ternary.Never;
}
}
default: {
(0, assert_1.assertUnreachable)(comparator);
}
}
}
negate() {
if (this.isValue()) {
return this.create([-this.value[1], -this.value[0]]);
}
return this.bottom();
}
add(other) {
const otherValue = other instanceof abstract_domain_1.AbstractDomain ? other.value : other;
if (this.isValue() && otherValue !== lattice_1.Bottom) {
return this.create([this.lower + otherValue[0], this.upper + otherValue[1]]);
}
return this.bottom();
}
subtract(other) {
const otherValue = other instanceof abstract_domain_1.AbstractDomain ? other.value : other;
if (this.isValue() && otherValue !== lattice_1.Bottom) {
return this.create([this.lower - otherValue[0], this.upper - otherValue[1]]);
}
return this.bottom();
}
multiply(other) {
const otherValue = other instanceof abstract_domain_1.AbstractDomain ? other.value : other;
if (this.isValue() && otherValue !== lattice_1.Bottom) {
const products = [
this.value[0] * otherValue[0],
this.value[0] * otherValue[1],
this.value[1] * otherValue[0],
this.value[1] * otherValue[1]
];
return this.create([Math.min(...products), Math.max(...products)]);
}
return this.bottom();
}
divide(other) {
const otherValue = other instanceof abstract_domain_1.AbstractDomain ? other.value : other;
if (this.isValue() && otherValue !== lattice_1.Bottom) {
if (otherValue[0] <= 0 && 0 <= otherValue[1]) {
return this.top();
}
return this.multiply(this.create([1 / otherValue[1], 1 / otherValue[0]]));
}
return this.bottom();
}
min(other) {
const otherValue = other instanceof abstract_domain_1.AbstractDomain ? other.value : other;
if (this.isValue() && otherValue !== lattice_1.Bottom) {
return this.create([Math.min(this.lower, otherValue[0]), Math.min(this.upper, otherValue[1])]);
}
return this.bottom();
}
max(other) {
const otherValue = other instanceof abstract_domain_1.AbstractDomain ? other.value : other;
if (this.isValue() && otherValue !== lattice_1.Bottom) {
return this.create([Math.max(this.lower, otherValue[0]), Math.max(this.upper, otherValue[1])]);
}
return this.bottom();
}
/**
* Extends the lower bound of the current abstract value down to -∞.
*/
widenDown() {
if (this.isValue()) {
return this.create([-Infinity, this.upper]);
}
return this.bottom();
}
/**
* Extends the upper bound of the current abstract value up to +∞.
*/
widenUp() {
if (this.isValue()) {
return this.create([this.lower, +Infinity]);
}
return this.bottom();
}
jsonify() {
return this.value;
}
stringify() {
return `[${Number.isFinite(this.lower) ? this.lower : '-∞'}, ${Number.isFinite(this.upper) ? this.upper : '+∞'}]`;
}
isTop() {
return this.value !== lattice_1.Bottom && this.lower === -Infinity && this.upper === +Infinity;
}
isBottom() {
return this.value === lattice_1.Bottom;
}
isValue() {
return this.value !== lattice_1.Bottom;
}
isFinite() {
return this.isValue() && Number.isFinite(this.lower) && Number.isFinite(this.upper);
}
}
exports.IntervalDomain = IntervalDomain;
//# sourceMappingURL=interval-domain.js.map