Opened 11 years ago

Closed 11 years ago

Last modified 11 years ago

#6095 closed Bugs (fixed)

boost::icl::is_empty fails for certain open intervals

Reported by: Marvin Sielenkemper <m.sielenkemper@…> Owned by: Joachim Faulhaber
Milestone: Boost 1.48.0 Component: ICL
Version: Boost 1.47.0 Severity: Problem
Keywords: icl, open interval, integer overflow Cc:

Description

While playing around with intervals over int with INT_MAX and INT_MIN bounds, I encountered some assertion failures in the library. After a little debugging the problem boiled down to the following test:

    BOOST_AUTO_TEST_CASE(isEmptyTest)
    {
        typedef int                         Value;
        typedef boost::icl::interval<Value> Interval;
        typedef std::numeric_limits<Value>  Limits;

        Value const max(Limits::max());

        BOOST_CHECK(!is_empty(Interval::open(max - 2, max)));
        BOOST_CHECK( is_empty(Interval::open(max - 1, max)));
        BOOST_CHECK( is_empty(Interval::open(max,     max)));
    }

The last check fails due to an integer overflow in the implementation of is_empty where the lower bound gets incremented.

I was able to fix this problem for is_empty but there are many places in the ICL where domain_next or domain_prior is used and there might be more problems lurking there.

My motivation for these experiments was to get a properly total interval map, i.e. one where the iterators start at INT_MIN and end at INT_MAX. To get this, I 'primed' the empty map with a closed interval ranging over the complete domain value range. But then the problems started.

So at least for me these overflow problems are not just of academical interest.

Attachments (2)

interval.hpp.diff (1.1 KB ) - added by Marvin Sielenkemper <m.sielenkemper@…> 11 years ago.
patch to check for overflow in is_empty
interval.hpp.2.diff (1.1 KB ) - added by Marvin Sielenkemper <m.sielenkemper@…> 11 years ago.
b <= a || b <= a + 1 fix

Download all attachments as: .zip

Change History (10)

by Marvin Sielenkemper <m.sielenkemper@…>, 11 years ago

Attachment: interval.hpp.diff added

patch to check for overflow in is_empty

comment:1 by Joachim Faulhaber, 11 years ago

Hi Marvin,

thank you for your report and the attached patch. I agree that the behavior of icl::intervals at open borders and for limited domain types is kind of nasty. Nevertheless after examining your information I am not convinced that we can speak of a bug here and that a code change is needed.

In your test case you propose that

  BOOST_CHECK( is_empty(Interval::open(max, max)));

is supposed to succeed. But an open interval Interval::open(max, x) has an open lower bound of max, which means that values of its domain_type do not exist that can be elements of the interval. My interpretation of intervals of this kind is that they are ill formed rather than empty. So the user has to take care via program logic that these types of intervals are not constructed in the same way she has to take care that ++i is not called if i == numeric_limits<int>::max() already.

Best regards, Joachim

comment:2 by Marvin Sielenkemper <m.sielenkemper@…>, 11 years ago

Hi Joachim,

as a mathematician I have to disagree: what distinguishes the interval Interval::open(max, max) from Interval::open(0, 0)? For both there exist no elements, that is the point of being empty. Even intervals like Interval::closed(17, 4) are allowed and deemed empty!

As a programmer I might be able to accept the responsibility to avoid such cases, but only if I were able to do so. I encountered the problem not while putting open intervals into the container but closed ones. And for those borders like min and max certainly make sense.

What I was trying to do was

    BOOST_AUTO_TEST_CASE(totalRangeTest)
    {
        typedef int                                                              Value;
        typedef boost::icl::interval<Value>                                      Interval;
        typedef std::numeric_limits<Value>                                       Limits;
        typedef boost::icl::interval_map<Value, int, boost::icl::total_enricher> Container;

        Value const min(Limits::min());
        Value const max(Limits::max());

        boost::icl::interval_map<Value, int, boost::icl::total_enricher> intervals;

        intervals += std::make_pair(Interval::closed(min, max), 0);
        intervals += std::make_pair(Interval::right_open(0, 10),  3);

        BOOST_CHECK_EQUAL(intervals.iterative_size(), 3);
   }

and this fails BOOST_ASSERT(this->_map.find(inter_val) == this->_map.end()); in interval_base_map.hpp, line 530 while trying to calculate the new third element of the container (Interval::closed(10, max)) when inserting the second interval.

Drilling down on that, I found is_empty and its problematic behaviour.

Thank you for your response!

Best regards,
Marvin

comment:3 by Joachim Faulhaber, 11 years ago

Keywords: icl open interval integer overflow added
Milestone: To Be DeterminedBoost 1.48.0
Status: newassigned

Hi Marvin,

I hope you forgive me my temporal {arrog|ignor}ance ;) Your use case shows, that the user can hardly avoid hitting the overflow trap of open intervals that are computed internally in the library in your test case. Also your fix is a nifty one. So thanks again for the report. I will apply your fix and put you on the credits list :) Send in more use cases if you find more bugs related to the [min,max]-preinitialized usage.

Cheers,
Joachim

comment:4 by Marvin Sielenkemper <m.sielenkemper@…>, 11 years ago

Hi Joachim,

no problem :)

But I would like to propose a maybe even more nifty fix:
An open interval ]a, b[ is empty if b <= a || b <= a + 1.

This does not only avoid the second increment, it even avoids the first one in many cases due to the short circuit ||-operator and does never run into the overflow case:
If the first check fails, we know that a < b and hence that there is at least one element to increment to without overflowing.

So this should even work for bound types that are less forgiving than int when hitting their bounds, (e.g. throw exceptions).

Best regards,
Marvin

by Marvin Sielenkemper <m.sielenkemper@…>, 11 years ago

Attachment: interval.hpp.2.diff added
b <= a
b <= a + 1 fix

comment:5 by Joachim Faulhaber, 11 years ago

Hi Marvin,

yes, this is nice and simple. I changed the code accordingly.

Thanx again,
Joachim

comment:6 by anonymous, 11 years ago

Resolution: fixed
Status: assignedclosed

comment:7 by anonymous, 11 years ago

Component: ICLBuilding Boost
Type: BugsPatches

comment:8 by Joachim Faulhaber, 11 years ago

Component: Building BoostICL
Type: PatchesBugs
Note: See TracTickets for help on using tickets.