Task: Range modifications


C++: #include <algorithm>
#include <iomanip>
#include <iostream>
#include <list>

struct range {
    range(int lo, int hi) : low(lo), high(hi) {}
    int low;
    int high;
};

std::ostream& operator<<(std::ostream& out, const range& r) {
    return out << r.low << '-' << r.high;
}

class ranges {
public:
    ranges() {}
    explicit ranges(std::initializer_list<range> init) : ranges_(init) {}
    void add(int n);
    void remove(int n);
    bool empty() const { return ranges_.empty(); }
private:
    friend std::ostream& operator<<(std::ostream& out, const ranges& r);
    std::list<range> ranges_;
};

void ranges::add(int n) {
    for (auto i = ranges_.begin(); i != ranges_.end(); ++i) {
        if (n + 1 < i->low) {
            ranges_.emplace(i, n, n);
            return;
        }
        if (n > i->high + 1)
            continue;
        if (n + 1 == i->low)
            i->low = n;
        else if (n == i->high + 1)
            i->high = n;
        else
            return;
        if (i != ranges_.begin()) {
            auto prev = std::prev(i);
            if (prev->high + 1 == i->low) {
                i->low = prev->low;
                ranges_.erase(prev);
            }
        }
        auto next = std::next(i);
        if (next != ranges_.end() && next->low - 1 == i->high) {
            i->high = next->high;
            ranges_.erase(next);
        }
        return;
    }
    ranges_.emplace_back(n, n);
}

void ranges::remove(int n) {
    for (auto i = ranges_.begin(); i != ranges_.end(); ++i) {
        if (n < i->low)
            return;
        if (n == i->low) {
            if (++i->low > i->high)
                ranges_.erase(i);
            return;
        }
        if (n == i->high) {
            if (--i->high < i->low)
                ranges_.erase(i);
            return;
        }
        if (n > i->low & n < i->high) {
            int low = i->low;
            i->low = n + 1;
            ranges_.emplace(i, low, n - 1);
            return;
        }
    }
}

std::ostream& operator<<(std::ostream& out, const ranges& r) {
    if (!r.empty()) {
        auto i = r.ranges_.begin();
        out << *i++;
        for (; i != r.ranges_.end(); ++i)
            out << ',' << *i;
    }
    return out;
}

void test_add(ranges& r, int n) {
    r.add(n);
    std::cout << "       add " << std::setw(2) << n << " => " << r << '\n';
}

void test_remove(ranges& r, int n) {
    r.remove(n);
    std::cout << "    remove " << std::setw(2) << n << " => " << r << '\n';
}

void test1() {
    ranges r;
    std::cout << "Start: \"" << r << "\"\n";
    test_add(r, 77);
    test_add(r, 79);
    test_add(r, 78);
    test_remove(r, 77);
    test_remove(r, 78);
    test_remove(r, 79);
}

void test2() {
    ranges r{{1,3}, {5,5}};
    std::cout << "Start: \"" << r << "\"\n";
    test_add(r, 1);
    test_remove(r, 4);
    test_add(r, 7);
    test_add(r, 8);
    test_add(r, 6);
    test_remove(r, 7);
}

void test3() {
    ranges r{{1,5}, {10,25}, {27,30}};
    std::cout << "Start: \"" << r << "\"\n";
    test_add(r, 26);
    test_add(r, 9);
    test_add(r, 7);
    test_remove(r, 26);
    test_remove(r, 9);
    test_remove(r, 7);
}

int main() {
    test1();
    std::cout << '\n';
    test2();
    std::cout << '\n';
    test3();
    return 0;
}






Python: class Sequence():
    
    def __init__(self, sequence_string):
        self.ranges = self.to_ranges(sequence_string)
        assert self.ranges == sorted(self.ranges), "Sequence order error"
        
    def to_ranges(self, txt):
        return [[int(x) for x in r.strip().split('-')]
                for r in txt.strip().split(',') if r]
    
    def remove(self, rem):
        ranges = self.ranges
        for i, r in enumerate(ranges):
            if r[0] <= rem <= r[1]:
                if r[0] == rem:     
                    if r[1] > rem:
                        r[0] += 1
                    else:
                        del ranges[i]
                elif r[1] == rem:   
                    if r[0] < rem:
                        r[1] -= 1
                    else:
                        del ranges[i]
                else:               
                    r[1], splitrange = rem - 1, [rem + 1, r[1]]
                    ranges.insert(i + 1, splitrange)
                break
            if r[0] > rem:  
                break
        return self
            
    def add(self, add):
        ranges = self.ranges
        for i, r in enumerate(ranges):
            if r[0] <= add <= r[1]:     
                break
            elif r[0] - 1 == add:      
                r[0] = add
                break
            elif r[1] + 1 == add:      
                r[1] = add
                break
            elif r[0] > add:      
                ranges.insert(i, [add, add])
                break
        else:
            ranges.append([add, add])
            return self
        return self.consolidate()
    
    def consolidate(self):
        "Combine overlapping ranges"
        ranges = self.ranges
        for this, that in zip(ranges, ranges[1:]):
            if this[1] + 1 >= that[0]:  
                if this[1] >= that[1]:  
                    this[:], that[:] = [], this
                else:   
                    this[:], that[:] = [], [this[0], that[1]]
        ranges[:] = [r for r in ranges if r]
        return self
    def __repr__(self):
        rr = self.ranges
        return ",".join(f"{r[0]}-{r[1]}" for r in rr)

def demo(opp_txt):
    by_line = opp_txt.strip().split('\n')
    start = by_line.pop(0)
    ex = Sequence(start.strip().split()[-1][1:-1])    
    lines = [line.strip().split() for line in by_line]
    opps = [((ex.add if word[0] == "add" else ex.remove), int(word[1]))
            for word in lines]
    print(f"Start: \"{ex}\"")
    for op, val in opps:
        print(f"    {op.__name__:>6} {val:2} => {op(val)}")
    print()
                    
if __name__ == '__main__':
    demo()
    demo()
    demo()