Task: Range modifications




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])
    }
}




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()