Range modifications: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added C++ solution)
m (print with quotes)
Line 384: Line 384:
const RangeSequence = Array{UnitRange, 1}
const RangeSequence = Array{UnitRange, 1}


function combine!(seq::RangeSequence, r2)
function combine!(seq::RangeSequence, r::UnitRange)
isempty(seq) && return push!(seq, r2)
isempty(seq) && return push!(seq, r)
r1 = seq[end]
if r.start < seq[end].start
if r2.stop > r1.stop
push!(seq, r)
if r2.start <= r1.stop + 1
newseq = reduce(seq)
seq[end] = r1.start:r2.stop
append!(empty!(seq), newseq)
elseif r.stop > seq[end].stop
if r.start <= seq[end].stop + 1
seq[end] = seq[end].start:r.stop
else
else
push!(seq, r2)
push!(seq, r)
end
end
end
end
Line 413: Line 416:
end
end


reduce(a::RangeSequence) = (seq = UnitRange[]; for r in sort!(a) combine!(seq, r) end; seq)
reduce(a::RangeSequence) = (seq = UnitRange[]; for r in sort(a) combine!(seq, r) end; seq)


insertinteger!(seq, n::Integer) = begin push!(seq, n:n); reduce(seq) end
insertinteger!(seq::RangeSequence, n::Integer) = begin push!(seq, n:n); reduce(seq) end


insertintegerprint!(seq, n) = println(" added $n => ", insertinteger!(seq, n))
function removeinteger!(seq, n::Integer)
removeintegerprint!(seq, n) = println(" removed $n => ", removeinteger!(seq, n))

function removeinteger!(seq::RangeSequence, n::Integer)
for (pos, r) in enumerate(seq)
for (pos, r) in enumerate(seq)
if n in r
if n in r
Line 428: Line 434:
seq[pos] = start+1:stop
seq[pos] = start+1:stop
elseif start < n < stop
elseif start < n < stop
seq[pos] = start+1:stop
seq[pos] = n+1:stop
insert!(seq, pos, stop:n-1)
insert!(seq, pos, start:n-1)
end
end
break
break
Line 438: Line 444:


function print(io::IO, seq::RangeSequence)
function print(io::IO, seq::RangeSequence)
return print(io, join(map(r -> "$(r.start)-$(r.stop)", reduce(seq)), ","))
return print(io, "\"" * join(map(r -> "$(r.start)-$(r.stop)", reduce(seq)), ",") * "\"")
end
end


const seq = parse(RangeSequence, "")
const seq = parse(RangeSequence, "")
println("Start: \"\"")
println("Start: $seq")
insertinteger!(seq, 77)
insertintegerprint!(seq, 77)
insertintegerprint!(seq, 79)
println(" added 77 => ", seq)
insertinteger!(seq, 79)
insertintegerprint!(seq, 78)
removeintegerprint!(seq, 77)
println(" added 79 => ", seq)
insertinteger!(seq, 78)
removeintegerprint!(seq, 78)
removeintegerprint!(seq, 79)
println(" added 78 => ", seq)
removeinteger!(seq, 77)
println(" removed 77 => ", seq)
removeinteger!(seq, 78)
println(" removed 78 => ", seq)
removeinteger!(seq, 79)
println(" removed 79 => ", seq, "\n")


const seq2 = parse(RangeSequence, "1-3, 5-5")
const seq2 = parse(RangeSequence, "1-3, 5-5")
println("Start: $seq2")
println("Start: $seq2")
insertinteger!(seq2, 1)
insertintegerprint!(seq2, 1)
removeintegerprint!(seq2, 4)
println(" added 1 => ", seq2)
removeinteger!(seq2, 4)
insertintegerprint!(seq2, 7)
insertintegerprint!(seq2, 8)
println(" removed 4 => ", seq2)
insertinteger!(seq2, 7)
insertintegerprint!(seq2, 6)
removeintegerprint!(seq2, 7)
println(" added 7 => ", seq2)
insertinteger!(seq2, 8)
println(" added 8 => ", seq2)
insertinteger!(seq2, 6)
println(" added 6 => ", seq2)
removeinteger!(seq2, 7)
println(" removed 7 => ", seq2, "\n")


const seq3 = parse(RangeSequence, "1-5, 10-25, 27-30")
const seq3 = parse(RangeSequence, "1-5, 10-25, 27-30")
println("Start: $seq3")
println("Start: $seq3")
insertinteger!(seq3, 26)
insertintegerprint!(seq3, 26)
insertintegerprint!(seq3, 9)
println(" added 26 => ", seq3)
insertinteger!(seq3, 9)
insertintegerprint!(seq3, 7)
removeintegerprint!(seq3, 26)
println(" added 9 => ", seq3)
insertinteger!(seq3, 7)
removeintegerprint!(seq3, 9)
removeintegerprint!(seq3, 7)
println(" added 7 => ", seq3)
removeinteger!(seq3, 26)
println(" removed 26 => ", seq3)
removeinteger!(seq3, 9)
println(" removed 9 => ", seq3)
removeinteger!(seq3, 7)
println(" removed 7 => ", seq3, "\n")


println("Parse \"10-25, 1-5, 27-30\" => ", parse(RangeSequence, "10-25, 1-5, 27-30"))
println("Parse \"10-25, 1-5, 27-30\" => ", parse(RangeSequence, "10-25, 1-5, 27-30"))

Revision as of 23:20, 25 October 2020

Range modifications is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

The increasing range of all integers from a lower to an upper bound, including each boundary, is shown by the lower-boundary separated from the higher-boundary by a single dash. So 64-1234 is the range of all integers from 64 to 1234, (including both 64 and 1234). 10-10 is the range covering the single integer 10.

A sequence of ranges is shown by successive integer ranges that do not overlap, and which have increasing lower bounds. Each range is separated by a single comma. So 13-14,22-22,100999999-101000001 is a sequence of ranges that contain the integers 13 14 22 100999999 101000000 and 101000001.
Empty ranges are removed. An empty sequence has an empty string representation.

Note: There are NO internal spaces in the sequence format.

Task

Given an initial sequence of ranges write programs to add or remove an integer from the sequence and display the resultant sequence.
Note:

  • The initial sequence may be empty.
  • Adding an int that is already covered should not change the sequence.
  • removing an int that is not in the sequence should not change the sequence.
  • The add and remove operations should print their result in the standard form mentioned.
  • Solutions must work by modifying range boundaries, splitting and joining, as well as creating and deleting ranges.
    Do not use algorithms that create and modify arrays of all the integer values within ranges.

Show the results, (including intermediate results), of performing the following steps

Ex0
   Start with ""
       add 77
       add 79
       add 78
       remove 77
       remove 78
       remove 79
       
Ex1
   Start with "1-3,5-5"
       add 1
       remove 4
       add 7
       add 8
       add 6
       remove 7
Ex2
   Start with "1-5,10-25,27-30"
       add 26
       add 9
       add 7
       remove 26
       remove 9
       remove 7

C++

<lang cpp>#include <algorithm>

  1. include <iomanip>
  2. include <iostream>
  3. 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;

}</lang>

Output:
Start: ""
       add 77 => 77-77
       add 79 => 77-77,79-79
       add 78 => 77-79
    remove 77 => 78-79
    remove 78 => 79-79
    remove 79 => 

Start: "1-3,5-5"
       add  1 => 1-3,5-5
    remove  4 => 1-3,5-5
       add  7 => 1-3,5-5,7-7
       add  8 => 1-3,5-5,7-8
       add  6 => 1-3,5-8
    remove  7 => 1-3,5-6,8-8

Start: "1-5,10-25,27-30"
       add 26 => 1-5,10-30
       add  9 => 1-5,9-30
       add  7 => 1-5,7-7,9-30
    remove 26 => 1-5,7-7,9-25,27-30
    remove  9 => 1-5,7-7,10-25,27-30
    remove  7 => 1-5,10-25,27-30

Go

Translation of: Wren

<lang go>package main

import (

   "fmt"
   "strings"

)

type rng struct{ from, to int }

type fn func(rngs *[]rng, n int)

func (r rng) String() string { return fmt.Sprintf("%d-%d", r.from, r.to) }

func rangesAdd(rngs []rng, n int) []rng {

   if len(rngs) == 0 {
       rngs = append(rngs, rng{n, n})
       return rngs
   }
   for i, r := range rngs {
       if n < r.from-1 {
           rngs = append(rngs, rng{})
           copy(rngs[i+1:], rngs[i:])
           rngs[i] = rng{n, n}
           return rngs
       } else if n == r.from-1 {
           rngs[i] = rng{n, r.to}
           return rngs
       } else if n <= r.to {
           return rngs
       } else if n == r.to+1 {
           rngs[i] = rng{r.from, n}
           if i < len(rngs)-1 && (n == rngs[i+1].from || n+1 == rngs[i+1].from) {
               rngs[i] = rng{r.from, rngs[i+1].to}
               copy(rngs[i+1:], rngs[i+2:])
               rngs[len(rngs)-1] = rng{}
               rngs = rngs[:len(rngs)-1]
           }
           return rngs
       } else if i == len(rngs)-1 {
           rngs = append(rngs, rng{n, n})
           return rngs
       }
   }
   return rngs

}

func rangesRemove(rngs []rng, n int) []rng {

   if len(rngs) == 0 {
       return rngs
   }
   for i, r := range rngs {
       if n <= r.from-1 {
           return rngs
       } else if n == r.from && n == r.to {
           copy(rngs[i:], rngs[i+1:])
           rngs[len(rngs)-1] = rng{}
           rngs = rngs[:len(rngs)-1]
           return rngs
       } else if n == r.from {
           rngs[i] = rng{n + 1, r.to}
           return rngs
       } else if n < r.to {
           rngs[i] = rng{r.from, n - 1}
           rngs = append(rngs, rng{})
           copy(rngs[i+2:], rngs[i+1:])
           rngs[i+1] = rng{n + 1, r.to}
           return rngs
       } else if n == r.to {
           rngs[i] = rng{r.from, n - 1}
           return rngs
       }
   }
   return rngs

}

func standard(rngs []rng) string {

   if len(rngs) == 0 {
       return ""
   }
   var sb strings.Builder
   for _, r := range rngs {
       sb.WriteString(fmt.Sprintf("%s,", r))
   }
   s := sb.String()
   return s[:len(s)-1]

}

func main() {

   const add = 0
   const remove = 1
   fns := []fn{
       func(prngs *[]rng, n int) {
           *prngs = rangesAdd(*prngs, n)
           fmt.Printf("       add %2d => %s\n", n, standard(*prngs))
       },
       func(prngs *[]rng, n int) {
           *prngs = rangesRemove(*prngs, n)
           fmt.Printf("    remove %2d => %s\n", n, standard(*prngs))
       },
   }
   var rngs []rng
   ops := [][2]int{{add, 77}, {add, 79}, {add, 78}, {remove, 77}, {remove, 78}, {remove, 79}}
   fmt.Printf("Start: %q\n", standard(rngs))
   for _, op := range ops {
       fns[op[0]](&rngs, op[1])
   }
   rngs = []rng{{1, 3}, {5, 5}}
   ops = [][2]int{{add, 1}, {remove, 4}, {add, 7}, {add, 8}, {add, 6}, {remove, 7}}
   fmt.Printf("\nStart: %q\n", standard(rngs))
   for _, op := range ops {
       fns[op[0]](&rngs, op[1])
   }
   rngs = []rng{{1, 5}, {10, 25}, {27, 30}}
   ops = [][2]int{{add, 26}, {add, 9}, {add, 7}, {remove, 26}, {remove, 9}, {remove, 7}}
   fmt.Printf("\nStart: %q\n", standard(rngs))
   for _, op := range ops {
       fns[op[0]](&rngs, op[1])
   }

}</lang>

Output:
Start: ""
       add 77 => 77-77
       add 79 => 77-77,79-79
       add 78 => 77-79
    remove 77 => 78-79
    remove 78 => 79-79
    remove 79 => 

Start: "1-3,5-5"
       add  1 => 1-3,5-5
    remove  4 => 1-3,5-5
       add  7 => 1-3,5-5,7-7
       add  8 => 1-3,5-5,7-8
       add  6 => 1-3,5-8
    remove  7 => 1-3,5-6,8-8

Start: "1-5,10-25,27-30"
       add 26 => 1-5,10-30
       add  9 => 1-5,9-30
       add  7 => 1-5,7-7,9-30
    remove 26 => 1-5,7-7,9-25,27-30
    remove  9 => 1-5,7-7,10-25,27-30
    remove  7 => 1-5,10-25,27-30

Julia

Julia has iterator classes called a type of Range, such as integer UnitRanges, that are like the "10-10" of the task but are stated as 10:10, with a colon not a minus sign. This implementation uses Julia's UnitRange class internally. <lang julia>import Base.parse, Base.print, Base.reduce

const RangeSequence = Array{UnitRange, 1}

function combine!(seq::RangeSequence, r::UnitRange)

   isempty(seq) && return push!(seq, r)
   if r.start < seq[end].start
       push!(seq, r)
       newseq = reduce(seq)
       append!(empty!(seq), newseq)
   elseif r.stop > seq[end].stop
       if r.start <= seq[end].stop + 1
           seq[end] = seq[end].start:r.stop
       else
           push!(seq, r)
       end
   end
   return seq

end

function parse(::Type{RangeSequence}, s)

   seq = UnitRange[]
   entries = sort!(split(s, r"\s*,\s*"))
   for e in entries
       startstop = split(e, r"\:|\-")
       if length(startstop) == 2
           start, stop = tryparse(Int, startstop[1]), tryparse(Int, startstop[2])
           start, stop = start <= stop ? (start, stop) : (stop, start)
           start != nothing && stop != nothing && push!(seq, start:stop)
       elseif (n = tryparse(Int, startstop[1])) != nothing
           push!(seq, n:n)
       end
   end
   return reduce(seq)

end

reduce(a::RangeSequence) = (seq = UnitRange[]; for r in sort(a) combine!(seq, r) end; seq)

insertinteger!(seq::RangeSequence, n::Integer) = begin push!(seq, n:n); reduce(seq) end

insertintegerprint!(seq, n) = println(" added $n => ", insertinteger!(seq, n)) removeintegerprint!(seq, n) = println(" removed $n => ", removeinteger!(seq, n))

function removeinteger!(seq::RangeSequence, n::Integer)

   for (pos, r) in enumerate(seq)
       if n in r
           start, stop = r.start, r.stop
           if start == stop == n
               deleteat!(seq, pos:pos)
           elseif stop == n
               seq[pos] = start:stop-1
           elseif start == n
               seq[pos] = start+1:stop
           elseif start < n < stop
               seq[pos] = n+1:stop
               insert!(seq, pos, start:n-1)
           end
           break
       end
   end
   return seq

end

function print(io::IO, seq::RangeSequence)

   return print(io, "\"" * join(map(r -> "$(r.start)-$(r.stop)", reduce(seq)), ",") * "\"")

end

const seq = parse(RangeSequence, "") println("Start: $seq") insertintegerprint!(seq, 77) insertintegerprint!(seq, 79) insertintegerprint!(seq, 78) removeintegerprint!(seq, 77) removeintegerprint!(seq, 78) removeintegerprint!(seq, 79)

const seq2 = parse(RangeSequence, "1-3, 5-5") println("Start: $seq2") insertintegerprint!(seq2, 1) removeintegerprint!(seq2, 4) insertintegerprint!(seq2, 7) insertintegerprint!(seq2, 8) insertintegerprint!(seq2, 6) removeintegerprint!(seq2, 7)

const seq3 = parse(RangeSequence, "1-5, 10-25, 27-30") println("Start: $seq3") insertintegerprint!(seq3, 26) insertintegerprint!(seq3, 9) insertintegerprint!(seq3, 7) removeintegerprint!(seq3, 26) removeintegerprint!(seq3, 9) removeintegerprint!(seq3, 7)

println("Parse \"10-25, 1-5, 27-30\" => ", parse(RangeSequence, "10-25, 1-5, 27-30")) println("Parse \"3-1,15-5,25-10,30-27\" => ", parse(RangeSequence, "3-1,15-5,25-10,30-27"))

</lang>

Output:
Start: ""
    added 77 => 77-77
    added 79 => 77-77,79-79
    added 78 => 77-79
    removed 77 => 78-79
    removed 78 => 79-79
    removed 79 =>

Start: 1-3,5-5
    added 1 => 1-3,5-5
    removed 4 => 1-3,5-5
    added 7 => 1-3,5-5,7-7
    added 8 => 1-3,5-5,7-8
    added 6 => 1-3,5-8
    removed 7 => 1-3,5-6,8-8

Start: 1-5,10-25,27-30
    added 26 => 1-5,10-30
    added 9 => 1-5,9-30
    added 7 => 1-5,7-7,9-30
    removed 26 => 1-5,7-7,9-25,27-30
    removed 9 => 1-5,7-7,10-25,27-30
    removed 7 => 1-5,10-25,27-30

Parse "10-25, 1-5, 27-30" => 1-5,10-25,27-30
Parse "3-1,15-5,25-10,30-27" => 1-3,5-25,27-30

Phix

<lang Phix>requires("0.8.2") -- (uses latest apply() functionality)

function add(sequence ranges, atom v) -- -- eg {} + 9 --> Template:9,9 -- [1] -- Template:3,5 + 9 --> {{3,5},{9,9}} -- [1] -- Template:3,5 + 4 --> as-is -- [2] -- Template:3,5 + 2 --> Template:2,5 -- [3] -- {{3,5},{7,9}} + 6 --> Template:3,9 -- [4] -- {{3,5},{8,9}} + 6 --> {{3,6},{8,9}} -- [5] -- {{3,5},{8,9}} + 7 --> {{3,5},{7,9}} -- [3] -- Template:3,5 + 6 --> Template:3,6 -- [6] -- Template:3,5 + 1 --> {{1,1},{3,5}} -- [7] --

   integer l = length(ranges)
   for i=1 to l+1 do
       if i>l then       ranges &= Template:V,v             exit  -- [1]
       end if
       atom nl,{lo,hi} = ranges[i]
       if v>=lo and v<=hi then                         exit  -- [2]
       elsif v=lo-1 then ranges[i][1] = v              exit  -- [3]
       elsif v=hi+1 then
           if i<l then
               {nl,hi} = ranges[i+1]
               if nl=v+1 then
                         ranges[i..i+1] = Template:Lo,hi    exit  -- [4]
               else
                         ranges[i][2] = v              exit  -- [5]
               end if
           else
                         ranges[i][2] = v              exit  -- [6]
           end if
       elsif v<lo then   ranges[i..i-1] = Template:V,v      exit  -- [7]
       end if
   end for
   return ranges

end function

function del(sequence ranges, atom v) -- -- eg Template:1,2 - 1 --> Template:2,2 -- [1] -- Template:2,2 - 2 --> {} -- [2] -- Template:1,2 - 2 --> Template:1,1 -- [3] -- Template:1,3 - 2 --> {{1,1},{3,3}} -- [4] -- Template:2,3 - 1 --> as-is -- [5] --

   for i=1 to length(ranges) do
       atom {lo,hi} = ranges[i]
       if v>=lo and v<=hi then
           if v=lo then
               if v<hi then    ranges[i][1] = lo+1                 -- [1]
                       else    ranges[i..i] = {}   end if          -- [2]
           elsif v==hi then    ranges[i][2] = hi-1                 -- [3]
                       else    ranges[i..i] = {{lo,v-1},{v+1,hi}}  -- [4]
           end if      exit
       elsif v<hi then exit end if                                 -- [5]
   end for
   return ranges

end function

constant tests = split(""" Start with ""

   add 77
   add 79
   add 78
   remove 77
   remove 78
   remove 79

Start with "1-3,5-5"

   add 1
   remove 4
   add 7
   add 8
   add 6
   remove 7

Start with "1-5,10-25,27-30"

   add 26
   add 9
   add 7
   remove 26
   remove 9
   remove 7

Start with "13-14,22-22,100000999999-100001000000,100001000003-999999999999"

   remove 22
   remove 100000999999
   remove 100001000000
      add 100001000001
      add 100001000002
   remove 100001000002
   remove 100001000001

""","\n",no_empty:=true)

string range = "" sequence ranges -- range internal form for i=1 to length(tests) do

   string ti = tests[i]
   if match("Start with",ti) then
       Template:Range = scanf(trim(ti),"Start with \"%s\"")
       ranges = split(range,",")
       ranges = vslice(apply(true,scanf,{ranges,{"%d-%d"}}),1)
       printf(1,"\n               Start with: \"%s\"\n",{range})
       --                                     ^^  ^^
   else
       Template:String op, atom v = scanf(trim(ti),"%s %d")
       integer rid = routine_id(substitute(op,"remove","del"))
       ranges = rid(ranges,v)
       range = join(apply(true,sprintf,{{"%d-%d"},ranges}),",")
       printf(1," %9s %-12d -> \"%s\"\n",{op,v,range})
       --                      ^^  ^^
   end if

end for</lang>

Output:

(Note that all double-quotes in the output were deliberately added in the last two printf() statements, mainly to prove there are no unnecessary spaces, etc, and are (see ^^) obviously trivial to remove.)

               Start with: ""
       add 77           -> "77-77"
       add 79           -> "77-77,79-79"
       add 78           -> "77-79"
    remove 77           -> "78-79"
    remove 78           -> "79-79"
    remove 79           -> ""

               Start with: "1-3,5-5"
       add 1            -> "1-3,5-5"
    remove 4            -> "1-3,5-5"
       add 7            -> "1-3,5-5,7-7"
       add 8            -> "1-3,5-5,7-8"
       add 6            -> "1-3,5-8"
    remove 7            -> "1-3,5-6,8-8"

               Start with: "1-5,10-25,27-30"
       add 26           -> "1-5,10-30"
       add 9            -> "1-5,9-30"
       add 7            -> "1-5,7-7,9-30"
    remove 26           -> "1-5,7-7,9-25,27-30"
    remove 9            -> "1-5,7-7,10-25,27-30"
    remove 7            -> "1-5,10-25,27-30"

               Start with: "13-14,22-22,100000999999-100001000000,100001000003-999999999999"
    remove 22           -> "13-14,100000999999-100001000000,100001000003-999999999999"
    remove 100000999999 -> "13-14,100001000000-100001000000,100001000003-999999999999"
    remove 100001000000 -> "13-14,100001000003-999999999999"
       add 100001000001 -> "13-14,100001000001-100001000001,100001000003-999999999999"
       add 100001000002 -> "13-14,100001000001-999999999999"
    remove 100001000002 -> "13-14,100001000001-100001000001,100001000003-999999999999"
    remove 100001000001 -> "13-14,100001000003-999999999999"

Python

<lang 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:     # range min
                   if r[1] > rem:
                       r[0] += 1
                   else:
                       del ranges[i]
               elif r[1] == rem:   # range max
                   if r[0] < rem:
                       r[1] -= 1
                   else:
                       del ranges[i]
               else:               # inside, range extremes.
                   r[1], splitrange = rem - 1, [rem + 1, r[1]]
                   ranges.insert(i + 1, splitrange)
               break
           if r[0] > rem:  # Not in sorted list
               break
       return self
           
   def add(self, add):
       ranges = self.ranges
       for i, r in enumerate(ranges):
           if r[0] <= add <= r[1]:     # already included
               break
           elif r[0] - 1 == add:      # rough extend to here
               r[0] = add
               break
           elif r[1] + 1 == add:      # rough extend to here
               r[1] = add
               break
           elif r[0] > add:      # rough insert here
               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]:  # Ranges interract
               if this[1] >= that[1]:  # this covers that
                   this[:], that[:] = [], this
               else:   # that extends this
                   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])    # Sequence("1-3,5-5")
   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("""
       Start with ""
           add 77
           add 79
           add 78
           remove 77
           remove 78
           remove 79
        """)
   demo("""
       Start with "1-3,5-5"
           add 1
           remove 4
           add 7
           add 8
           add 6
           remove 7
        """)
   demo("""
       Start with "1-5,10-25,27-30"
           add 26
           add 9
           add 7
           remove 26
           remove 9
           remove 7
       """)

</lang>

Output:
Start: ""
       add 77 => 77-77
       add 79 => 77-77,79-79
       add 78 => 77-79
    remove 77 => 78-79
    remove 78 => 79-79
    remove 79 => 

Start: "1-3,5-5"
       add  1 => 1-3,5-5
    remove  4 => 1-3,5-5
       add  7 => 1-3,5-5,7-7
       add  8 => 1-3,5-5,7-8
       add  6 => 1-3,5-8
    remove  7 => 1-3,5-6,8-8

Start: "1-5,10-25,27-30"
       add 26 => 1-5,10-30
       add  9 => 1-5,9-30
       add  7 => 1-5,7-7,9-30
    remove 26 => 1-5,7-7,9-25,27-30
    remove  9 => 1-5,7-7,10-25,27-30
    remove  7 => 1-5,10-25,27-30

Raku

Works with: Rakudo version 2020.09

Quite a bit of this is just transforming syntax back and forth. Raku already has Ranges and Sequences as first class objects, though the syntax is different.

Demonstrate the task required examples: adding and removing integers, as well as an example adding and removing ranges to / from the sequence, in both native Raku syntax and the "Stringy" syntax required by the task.

Demo with some extremely large values / ranges. Capable of working with infinite ranges by default.

Won't handle negative numbers as written, mostly due the need to work around the syntax requirements for output.

<lang perl6>my @seq;

-> $op, $string { printf "%20s -> %s\n", $op, $string } for

 'Start',     to-string( @seq  = canonicalize "" ),
 'add 77',    to-string( @seq .= &add(77) ),
 'add 79',    to-string( @seq .= &add(79) ),
 'add 78',    to-string( @seq .= &add(78) ),
 'remove 77', to-string( @seq .= &remove(77) ),
 'remove 78', to-string( @seq .= &remove(78) ),
 'remove 79', to-string( @seq .= &remove(79) );

say ; -> $op, $string { printf "%20s -> %s\n", $op, $string } for

 'Start',    to-string( @seq  = canonicalize "1-3,5-5" ),
 'add 1',    to-string( @seq .= &add(1) ),
 'remove 4', to-string( @seq .= &remove(4) ),
 'add 7',    to-string( @seq .= &add(7) ),
 'add 8',    to-string( @seq .= &add(8) ),
 'add 6',    to-string( @seq .= &add(6) ),
 'remove 7', to-string( @seq .= &remove(7) );

say ; -> $op, $string { printf "%20s -> %s\n", $op, $string } for

 'Start',     to-string( @seq  = canonicalize "1-5,10-25,27-30" ),
 'add 26',    to-string( @seq .= &add(26) ),
 'add 9',     to-string( @seq .= &add(9) ),
 'add 7',     to-string( @seq .= &add(7) ),
 'remove 26', to-string( @seq .= &remove(26) ),
 'remove 9',  to-string( @seq .= &remove(9) ),
 'remove 7',  to-string( @seq .= &remove(7) );

say ; -> $op, $string { printf "%30s -> %s\n", $op, $string } for

 'Start',                  to-string( @seq  = canonicalize "6-57,160-251,2700-7000000" ),
 'add "2502-2698"',        to-string( @seq .= &add("2502-2698") ),
 'add 41..69',             to-string( @seq .= &add(41..69) ),
 'remove 17..30',          to-string( @seq .= &remove(17..30) ),
 'remove 4391..6527',      to-string( @seq .= &remove("4391-6527") ),
 'add 2699',               to-string( @seq .= &add(2699) ),
 'add 76',                 to-string( @seq .= &add(76) ),
 'add 78',                 to-string( @seq .= &add(78) ),
 'remove "70-165"',        to-string( @seq .= &remove("70-165") ),
 'remove 16..31',          to-string( @seq .= &remove(16..31) ),
 'add 1.417e16 .. 3.2e21', to-string( @seq .= &add(1.417e16.Int .. 3.2e21.Int) ),
 'remove "4001-Inf"',      to-string( @seq .= &remove("4001-Inf") );


sub canonicalize (Str $ranges) { sort consolidate |sort parse-range $ranges }

sub parse-range (Str $_) { .comb(/\d+|'Inf'/).map: { +$^α .. +$^ω } }

sub to-string (@ranges) { qq|"{ @ranges».minmax».join('-').join(',') }"| }

multi add (@ranges, Int $i) { samewith @ranges, $i .. $i } multi add (@ranges, Str $s) { samewith @ranges, |parse-range($s) } multi add (@ranges, Range $r) { @ranges > 0 ?? (sort consolidate |sort |@ranges, $r) !! $r }

multi remove (@ranges, Int $i) { samewith @ranges, $i .. $i } multi remove (@ranges, Str $s) { samewith @ranges, |parse-range($s) } multi remove (@ranges, Range $r) {

   gather for |@ranges -> $this {
       if $r.min <= $this.min {
           if $r.max >= $this.min and $r.max < $this.max {
               take $r.max + 1 .. $this.max
           }
           elsif $r.max < $this.min {
               take $this
           }
       }
       else {
           if $r.max >= $this.max and $r.min <= $this.max {
               take $this.min .. $r.min - 1
           }
           elsif $r.max < $this.max and $r.min > $this.min {
               take $this.min .. $r.min - 1;
               take $r.max + 1 .. $this.max
           }
           else {
               take $this
           }
       }
   }

}

multi consolidate() { () }

multi consolidate($this is copy, **@those) {

   sub infix:<∪> (Range $a, Range $b) { Range.new($a.min,max($a.max,$b.max)) }
   sub infix:<∩> (Range $a, Range $b) { so $a.max >= $b.min }
   my @ranges = sort gather {
       for consolidate |@those -> $that {
           next unless $that;
           if $this ∩ $that { $this ∪= $that }
           else             { take $that }
       }
       take $this;
   }
   for reverse ^(@ranges - 1) {
       if @ranges[$_].max == @ranges[$_ + 1].min - 1 {
           @ranges[$_] = @ranges[$_].min .. @ranges[$_ + 1].max;
           @ranges[$_ + 1]:delete
       }
   }
   @ranges

}</lang>

Output:
               Start -> ""
              add 77 -> "77-77"
              add 79 -> "77-77,79-79"
              add 78 -> "77-79"
           remove 77 -> "78-79"
           remove 78 -> "79-79"
           remove 79 -> ""

               Start -> "1-3,5-5"
               add 1 -> "1-3,5-5"
            remove 4 -> "1-3,5-5"
               add 7 -> "1-3,5-5,7-7"
               add 8 -> "1-3,5-5,7-8"
               add 6 -> "1-3,5-8"
            remove 7 -> "1-3,5-6,8-8"

               Start -> "1-5,10-25,27-30"
              add 26 -> "1-5,10-30"
               add 9 -> "1-5,9-30"
               add 7 -> "1-5,7-7,9-30"
           remove 26 -> "1-5,7-7,9-25,27-30"
            remove 9 -> "1-5,7-7,10-25,27-30"
            remove 7 -> "1-5,10-25,27-30"

                         Start -> "6-57,160-251,2700-7000000"
               add "2502-2698" -> "6-57,160-251,2502-2698,2700-7000000"
                    add 41..69 -> "6-69,160-251,2502-2698,2700-7000000"
                 remove 17..30 -> "6-16,31-69,160-251,2502-2698,2700-7000000"
             remove 4391..6527 -> "6-16,31-69,160-251,2502-2698,2700-4390,6528-7000000"
                      add 2699 -> "6-16,31-69,160-251,2502-4390,6528-7000000"
                        add 76 -> "6-16,31-69,76-76,160-251,2502-4390,6528-7000000"
                        add 78 -> "6-16,31-69,76-76,78-78,160-251,2502-4390,6528-7000000"
               remove "70-165" -> "6-16,31-69,166-251,2502-4390,6528-7000000"
                 remove 16..31 -> "6-15,32-69,166-251,2502-4390,6528-7000000"
        add 1.417e16 .. 3.2e21 -> "6-15,32-69,166-251,2502-4390,6528-7000000,14170000000000000-3200000000000000000000"
             remove "4001-Inf" -> "6-15,32-69,166-251,2502-4000"

Wren

Library: Wren-fmt

<lang ecmascript>import "/fmt" for Fmt

var rangesAdd = Fn.new { |ranges, n|

   if (ranges.count == 0) {
       ranges.add(n..n)
       return
   }
   for (i in 0...ranges.count) {
       var r = ranges[i]
       if (n < r.from - 1) {
           ranges.insert(i, n..n)
           return
       } else if (n == r.from-1) {
           ranges[i] = n..r.to
           return
       } else if (n <= r.to) {
           return
       } else if (n == r.to+1) {
           ranges[i] = r.from..n
           if (i < ranges.count - 1 && (n == ranges[i+1].from || n + 1 == ranges[i+1].from)) {
               ranges[i] = r.from..ranges[i+1].to
               ranges.removeAt(i+1)
           }
           return
       } else if (i == ranges.count - 1) {
           ranges.add(n..n)
           return
       }
   }

}

var rangesRemove = Fn.new { |ranges, n|

   if (ranges.count == 0) return
   for (i in 0...ranges.count) {
       var r = ranges[i]
       if (n <= r.from - 1) {
           return
       } else if (n == r.from && n == r.to) {
           ranges.removeAt(i)
           return
       } else if (n == r.from) {
           ranges[i] = n+1..r.to
           return
       } else if (n < r.to) {
           ranges[i] = r.from..n-1
           ranges.insert(i+1, n+1..r.to)
           return
       } else if (n == r.to) {
           ranges[i] = r.from..n-1
           return
       }
   }

}

var standard = Fn.new { |ranges| Fmt.v("s", 0, ranges, 0, ",", "").replace("..", "-") }

var add = 0 var remove = 1 var fns = [

   Fn.new { |ranges, n|
       rangesAdd.call(ranges, n)
       Fmt.print("       add $2d => $n", n, standard.call(ranges))
   },
   Fn.new { |ranges, n|
       rangesRemove.call(ranges, n)
       Fmt.print("    remove $2d => $n", n, standard.call(ranges))
   }

]

var ranges = [] var ops = [ [add, 77], [add, 79], [add, 78], [remove, 77], [remove, 78], [remove, 79] ] Fmt.print("Start: $q", standard.call(ranges)) for (op in ops) fns[op[0]].call(ranges, op[1])

ranges = [1..3, 5..5] ops = [ [add, 1], [remove, 4], [add, 7], [add, 8], [add, 6], [remove, 7] ] Fmt.print("\nStart: $q", standard.call(ranges)) for (op in ops) fns[op[0]].call(ranges, op[1])

ranges = [1..5, 10..25, 27..30] ops = [ [add, 26], [add, 9], [add, 7], [remove, 26], [remove, 9], [remove, 7] ] Fmt.print("\nStart: $q", standard.call(ranges)) for (op in ops) fns[op[0]].call(ranges, op[1]) </lang>

Output:
Start: ""
       add 77 => 77-77
       add 79 => 77-77,79-79
       add 78 => 77-79
    remove 77 => 78-79
    remove 78 => 79-79
    remove 79 =>  

Start: "1-3,5-5"
       add  1 => 1-3,5-5
    remove  4 => 1-3,5-5
       add  7 => 1-3,5-5,7-7
       add  8 => 1-3,5-5,7-8
       add  6 => 1-3,5-8
    remove  7 => 1-3,5-6,8-8

Start: "1-5,10-25,27-30"
       add 26 => 1-5,10-30
       add  9 => 1-5,9-30
       add  7 => 1-5,7-7,9-30
    remove 26 => 1-5,7-7,9-25,27-30
    remove  9 => 1-5,7-7,10-25,27-30
    remove  7 => 1-5,10-25,27-30