mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 09:55:19 +00:00
361 lines
10 KiB
C++
361 lines
10 KiB
C++
/*++
|
|
Copyright (c) 2011 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
basic_interval.h
|
|
|
|
Abstract:
|
|
|
|
Basic interval arithmetic template for precise numerals: mpz, mpq, mpbq.
|
|
Only basic support is provided.
|
|
There is no support for:
|
|
- minus and plus infinity bounds.
|
|
- mixed open/closed intervals such as (2, 3]
|
|
The main customer of this package is the algebraic_number module.
|
|
|
|
Author:
|
|
|
|
Leonardo de Moura (leonardo) 2012-12-04.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#pragma once
|
|
|
|
template<typename numeral_manager, bool closed>
|
|
class basic_interval_manager {
|
|
public:
|
|
typedef typename numeral_manager::numeral bound;
|
|
|
|
class interval {
|
|
friend class basic_interval_manager;
|
|
bound m_lower;
|
|
bound m_upper;
|
|
public:
|
|
bound const & lower() const { return m_lower; }
|
|
bound const & upper() const { return m_upper; }
|
|
bound & lower() { return m_lower; }
|
|
bound & upper() { return m_upper; }
|
|
};
|
|
|
|
class scoped_interval {
|
|
basic_interval_manager & m_manager;
|
|
interval m_interval;
|
|
public:
|
|
scoped_interval(basic_interval_manager & m):m_manager(m) {}
|
|
~scoped_interval() { m_manager.del(m_interval); }
|
|
|
|
basic_interval_manager & m() const { return m_manager; }
|
|
operator interval const &() const { return m_interval; }
|
|
operator interval&() { return m_interval; }
|
|
interval const & get() const { return m_interval; }
|
|
interval & get() { return m_interval; }
|
|
void reset() { m().reset(m_interval); }
|
|
void swap(scoped_interval & a) { m().swap(m_interval, a.m_interval); }
|
|
void swap(interval & a) { m().swap(m_interval, a); }
|
|
bound const & lower() const { return m_interval.lower(); }
|
|
bound const & upper() const { return m_interval.upper(); }
|
|
bound & lower() { return m_interval.lower(); }
|
|
bound & upper() { return m_interval.upper(); }
|
|
|
|
friend std::ostream & operator<<(std::ostream & out, scoped_interval const & a) {
|
|
a.m().display(out, a.get());
|
|
return out;
|
|
}
|
|
};
|
|
|
|
protected:
|
|
numeral_manager & m_manager;
|
|
bound m_mul_curr;
|
|
bound m_mul_max;
|
|
bound m_mul_min;
|
|
|
|
public:
|
|
typedef interval numeral; // allow intervals to be used by algorithms parameterized by numeral_manager
|
|
|
|
basic_interval_manager(numeral_manager & m):
|
|
m_manager(m) {
|
|
}
|
|
|
|
~basic_interval_manager() {
|
|
m().del(m_mul_curr);
|
|
m().del(m_mul_max);
|
|
m().del(m_mul_min);
|
|
}
|
|
|
|
numeral_manager & m() const { return m_manager; }
|
|
|
|
/**
|
|
\brief Delete interval
|
|
*/
|
|
void del(interval & a) {
|
|
m().del(a.m_lower);
|
|
m().del(a.m_upper);
|
|
}
|
|
|
|
/**
|
|
\brief Delete and reset lower and upper bounds to 0
|
|
*/
|
|
void reset(interval & a) {
|
|
m().reset(a.m_lower);
|
|
m().reset(a.m_upper);
|
|
}
|
|
|
|
bound const & lower(interval const & a) {
|
|
return a.lower();
|
|
}
|
|
|
|
bound const & upper(interval const & a) {
|
|
return a.upper();
|
|
}
|
|
|
|
/**
|
|
\brief a <- (lower, upper)
|
|
*/
|
|
void set(interval & a, bound const & lower, bound const & upper) {
|
|
SASSERT(m().le(lower, upper));
|
|
m().set(a.m_lower, lower);
|
|
m().set(a.m_upper, upper);
|
|
}
|
|
|
|
/**
|
|
\brief a <- b
|
|
*/
|
|
void set(interval & a, interval const & b) {
|
|
set(a, b.m_lower, b.m_upper);
|
|
}
|
|
|
|
/**
|
|
\brief a <- (n, n)
|
|
|
|
Manager must be configured for closed intervals.
|
|
*/
|
|
void set(interval & a, bound const & n) {
|
|
m().set(a.m_lower, n);
|
|
m().set(a.m_upper, n);
|
|
}
|
|
|
|
void set_lower(interval & a, bound const & n) {
|
|
SASSERT(m().le(n, a.m_upper));
|
|
m().set(a.m_lower, n);
|
|
}
|
|
|
|
void set_upper(interval & a, bound const & n) {
|
|
SASSERT(m().le(a.m_lower, n));
|
|
m().set(a.m_upper, n);
|
|
}
|
|
|
|
void swap(interval & a, interval & b) {
|
|
m().swap(a.m_lower, b.m_lower);
|
|
m().swap(a.m_upper, b.m_upper);
|
|
}
|
|
|
|
/**
|
|
\brief a <- -a
|
|
*/
|
|
void neg(interval & a) {
|
|
m().neg(a.m_lower);
|
|
m().neg(a.m_upper);
|
|
m().swap(a.m_lower, a.m_upper);
|
|
}
|
|
|
|
/**
|
|
\brief Return true if a does not contain any value. We can
|
|
only have empty intervals if the manager is configured to used
|
|
open intervals.
|
|
*/
|
|
bool is_empty(interval const & a) {
|
|
return !closed && m().eq(a.m_lower, a.m_upper);
|
|
}
|
|
|
|
/**
|
|
\brief Return true if all values in the given interval are positive.
|
|
*/
|
|
bool is_pos(interval const & a) { return (closed && m().is_pos(a.m_lower)) || (!closed && m().is_nonneg(a.m_lower)); }
|
|
|
|
/**
|
|
\brief Return true if all values in the given interval are negative.
|
|
*/
|
|
bool is_neg(interval const & a) { return (closed && m().is_neg(a.m_upper)) || (!closed && m().is_nonpos(a.m_upper)); }
|
|
|
|
/**
|
|
\brief Return true if 0 is in the interval.
|
|
*/
|
|
bool contains_zero(interval const & a) {
|
|
return
|
|
(closed && m().is_nonpos(a.m_lower) && m().is_nonneg(a.m_upper)) ||
|
|
(!closed && m().is_neg(a.m_lower) && m().is_pos(a.m_upper));
|
|
}
|
|
|
|
/**
|
|
\brief Return true if all values in interval a are in interval b.
|
|
*/
|
|
bool is_subset(interval const & a, interval const & b) {
|
|
return m().le(b.m_lower, a.m_lower) && m().le(a.m_upper, b.m_upper);
|
|
}
|
|
|
|
/**
|
|
\brief Return true if there is no value v s.t. v \in a and v \in b.
|
|
*/
|
|
bool disjoint(interval const & a, interval const & b) {
|
|
return
|
|
(closed && (m().lt(a.m_upper, b.m_lower) || m().lt(b.m_upper, a.m_lower))) ||
|
|
(!closed && (m().le(a.m_upper, b.m_upper) || m().le(b.m_upper, a.m_lower)));
|
|
}
|
|
|
|
/**
|
|
\brief Return true if all elements in a are smaller than all elements in b.
|
|
*/
|
|
bool precedes(interval const & a, interval const & b) {
|
|
return
|
|
(closed && (m().lt(a.m_upper, b.m_lower))) ||
|
|
(!closed && (m().le(a.m_upper, b.m_lower)));
|
|
}
|
|
|
|
/**
|
|
\brief Return true if all elements in a are smaller than b.
|
|
*/
|
|
bool precedes(interval const & a, bound const & b) {
|
|
return
|
|
(closed && (m().lt(a.m_upper, b))) ||
|
|
(!closed && (m().le(a.m_upper, b)));
|
|
}
|
|
|
|
/**
|
|
\brief Return true if a is smaller than all elements in b.
|
|
*/
|
|
bool precedes(bound const & a, interval const & b) {
|
|
return
|
|
(closed && (m().lt(a, b.m_lower))) ||
|
|
(!closed && (m().le(a, b.m_lower)));
|
|
}
|
|
|
|
/**
|
|
\brief a <- 1/a
|
|
|
|
\pre a.m_lower and m_upper must not be 0.
|
|
\pre bound must be a field.
|
|
*/
|
|
void inv(interval & a) {
|
|
SASSERT(numeral_manager::field());
|
|
SASSERT(!contains_zero(a));
|
|
SASSERT(!m().is_zero(a.m_lower) && !m().is_zero(a.m_upper));
|
|
m().inv(a.m_lower);
|
|
m().inv(a.m_upper);
|
|
m().swap(a.m_lower, a.m_upper);
|
|
}
|
|
|
|
/**
|
|
\brief c <- a + b
|
|
*/
|
|
void add(interval const & a, interval const & b, interval & c) {
|
|
m().add(a.m_lower, b.m_lower, c.m_lower);
|
|
m().add(a.m_upper, b.m_upper, c.m_upper);
|
|
}
|
|
|
|
/**
|
|
\brief c <- a - b
|
|
*/
|
|
void sub(interval const & a, interval const & b, interval & c) {
|
|
m().sub(a.m_lower, b.m_upper, c.m_lower);
|
|
m().sub(a.m_upper, b.m_lower, c.m_upper);
|
|
}
|
|
|
|
private:
|
|
/**
|
|
\brief Init the value of m_mul_max and m_mul_min using m_mul_curr
|
|
*/
|
|
void init_mul_max_min() {
|
|
m().set(m_mul_min, m_mul_curr);
|
|
m().swap(m_mul_max, m_mul_curr);
|
|
}
|
|
|
|
/**
|
|
\brief Update the value of m_mul_max and m_mul_min using m_mul_curr
|
|
*/
|
|
void update_mul_max_min() {
|
|
if (m().lt(m_mul_curr, m_mul_min))
|
|
m().set(m_mul_min, m_mul_curr);
|
|
if (m().gt(m_mul_curr, m_mul_max))
|
|
m().swap(m_mul_max, m_mul_curr);
|
|
}
|
|
|
|
public:
|
|
/**
|
|
\brief c <- a * b
|
|
*/
|
|
void mul(interval const & a, interval const & b, interval & c) {
|
|
m().mul(a.m_lower, b.m_lower, m_mul_curr);
|
|
init_mul_max_min();
|
|
m().mul(a.m_lower, b.m_upper, m_mul_curr);
|
|
update_mul_max_min();
|
|
m().mul(a.m_upper, b.m_lower, m_mul_curr);
|
|
update_mul_max_min();
|
|
m().mul(a.m_upper, b.m_upper, m_mul_curr);
|
|
update_mul_max_min();
|
|
m().swap(c.m_lower, m_mul_min);
|
|
m().swap(c.m_upper, m_mul_max);
|
|
}
|
|
|
|
/**
|
|
\brief c <- a/b
|
|
|
|
\pre b m_lower and m_upper must not be 0
|
|
\pre bound must be a field.
|
|
*/
|
|
void div(interval const & a, interval const & b, interval & c) {
|
|
SASSERT(numeral_manager::field());
|
|
SASSERT(!contains_zero(b));
|
|
SASSERT(!m().is_zero(b.m_lower) && !m().is_zero(b.m_upper));
|
|
m().div(a.m_lower, b.m_lower, m_mul_curr);
|
|
init_mul_max_min();
|
|
m().div(a.m_lower, b.m_upper, m_mul_curr);
|
|
update_mul_max_min();
|
|
m().div(a.m_upper, b.m_lower, m_mul_curr);
|
|
update_mul_max_min();
|
|
m().div(a.m_upper, b.m_upper, m_mul_curr);
|
|
update_mul_max_min();
|
|
m().swap(c.m_lower, m_mul_min);
|
|
m().swap(c.m_upper, m_mul_max);
|
|
}
|
|
|
|
/**
|
|
\brief c <- a^n
|
|
*/
|
|
void power(interval const & a, unsigned n, interval & c) {
|
|
// Let a be of the form (l, u)
|
|
if (n % 2 == 1) {
|
|
// n is odd
|
|
// c <- (l^n, u^n)
|
|
m().power(a.m_lower, n, c.m_lower);
|
|
m().power(a.m_upper, n, c.m_upper);
|
|
}
|
|
else {
|
|
SASSERT(n % 2 == 0);
|
|
m().power(a.m_lower, n, c.m_lower);
|
|
m().power(a.m_upper, n, c.m_upper);
|
|
if (m().is_nonneg(a.m_lower)) {
|
|
// n is even and l >= 0
|
|
// c <- (l^n, u^n)
|
|
return;
|
|
}
|
|
if (m().is_neg(a.m_upper)) {
|
|
// n is even and u < 0
|
|
// c <- (u^n, l^n)
|
|
m().swap(c.m_lower, c.m_upper);
|
|
return;
|
|
}
|
|
// c <- (0, max(l^n, u^n))
|
|
if (m().gt(c.m_lower, c.m_upper))
|
|
m().swap(c.m_lower, c.m_upper);
|
|
m().reset(c.m_lower);
|
|
}
|
|
}
|
|
|
|
void display(std::ostream & out, interval const & a) {
|
|
out << (closed ? "[" : "(") << m().to_string(a.m_lower) << ", " << m().to_string(a.m_upper) << (closed ? "]" : ")");
|
|
}
|
|
};
|
|
|