]> git.lizzy.rs Git - go-anidb.git/blobdiff - misc/episoderange.go
misc: (*EpisodeRange).Split(*Episode) -- Splits a range
[go-anidb.git] / misc / episoderange.go
index fbc5cd35d6e98977568370f11ce69582ea6be0bb..729d2c8ea2965366d22a691bdf9cebf306115c01 100644 (file)
@@ -12,6 +12,14 @@ type EpisodeRange struct {
        End   *Episode    // The end of the range; may be nil, which represents an endless range
 }
 
+func EpisodeToRange(ep *Episode) *EpisodeRange {
+       return &EpisodeRange{
+               Type:  ep.Type,
+               Start: ep,
+               End:   ep,
+       }
+}
+
 // Converts the EpisodeRange into AniDB API range format.
 func (er *EpisodeRange) String() string {
        return er.Format(er.scale())
@@ -106,7 +114,13 @@ func (a *EpisodeRange) Merge(b *EpisodeRange) (c *EpisodeRange) {
        if a.touches(b) {
                c = &EpisodeRange{Type: a.Type}
 
-               if a.Start.Number <= b.Start.Number {
+               if a.Start.Number == b.Start.Number {
+                       if a.Start.Part <= b.Start.Part {
+                               c.Start = a.Start
+                       } else {
+                               c.Start = b.Start
+                       }
+               } else if a.Start.Number < b.Start.Number {
                        c.Start = a.Start
                } else {
                        c.Start = b.Start
@@ -115,7 +129,13 @@ func (a *EpisodeRange) Merge(b *EpisodeRange) (c *EpisodeRange) {
                switch {
                case a.End == nil || b.End == nil:
                        c.End = nil
-               case a.End.Number >= b.End.Number:
+               case a.End.Number == b.End.Number:
+                       if a.End.Part >= b.End.Part {
+                               c.End = a.End
+                       } else {
+                               c.End = b.End
+                       }
+               case a.End.Number > b.End.Number:
                        c.End = a.End
                default:
                        c.End = b.End
@@ -124,6 +144,132 @@ func (a *EpisodeRange) Merge(b *EpisodeRange) (c *EpisodeRange) {
        return
 }
 
+// Check if the given range is not nil, has a defined start
+// and, if it has an end, that the end ends after the start.
+func (er *EpisodeRange) Valid() bool {
+       switch {
+       case er == nil, er.Start == nil:
+               return false
+       case er.End == nil:
+               return true
+       case er.Start.Number < er.End.Number:
+               return true
+       case er.Start.Number > er.End.Number:
+               return false
+       case er.Start.Part <= er.End.Part:
+               return true
+       default:
+               return false
+       }
+}
+
+// Simplifies the Start/End ranges if one contains the other.
+// Sets the pointers to be identical if the range is modified.
+//
+// Modifies in-place, returns itself.
+func (er *EpisodeRange) Simplify() *EpisodeRange {
+       switch {
+       case er.Start.ContainsEpisodes(er.End):
+               er.End = er.Start
+       case er.End != nil && er.End.ContainsEpisodes(er.Start):
+               er.Start = er.End
+       }
+       return er
+}
+
+// Splits the range into one or two ranges, using the given
+// Episode as the split point. The Episode is not included in
+// the resulting ranges.
+func (er *EpisodeRange) Split(ep *Episode) []*EpisodeRange {
+       if !er.ContainsEpisodes(ep) { // implies same type
+               return []*EpisodeRange{er}
+       }
+       if !er.Valid() {
+               return []*EpisodeRange{nil, nil}
+       }
+
+       a := *er.Start
+
+       inf := er.End == nil
+       b := Episode{}
+       if !inf {
+               b = *er.End
+       }
+
+       end := &b
+       if inf {
+               end = nil
+       }
+
+       switch {
+       case a.ContainsEpisodes(ep) && b.ContainsEpisodes(ep):
+               return []*EpisodeRange{nil, nil}
+       case a.ContainsEpisodes(ep):
+               if ep.Part >= 0 {
+                       a.Inc()
+               } else {
+                       a.IncNumber()
+               }
+               if a.Number == b.Number && b.Parts > 0 {
+                       a.Parts = b.Parts
+               }
+
+               r := &EpisodeRange{
+                       Type:  er.Type,
+                       Start: &a,
+                       End:   end,
+               }
+               return []*EpisodeRange{nil, r.Simplify()}
+       case b.ContainsEpisodes(ep):
+               if ep.Part >= 0 {
+                       b.Dec()
+               } else {
+                       b.DecNumber()
+               }
+               if b.Number == a.Number {
+                       if a.Parts > 0 {
+                               b.Parts = a.Parts
+                               b.Part = a.Parts - 1
+                       } else if b.Part < 0 {
+                               b.Part = a.Part
+                       }
+               }
+               r := &EpisodeRange{
+                       Type:  er.Type,
+                       Start: &a,
+                       End:   &b,
+               }
+               return []*EpisodeRange{r.Simplify(), nil}
+       default:
+               ra := &EpisodeRange{
+                       Type:  er.Type,
+                       Start: &a,
+                       End:   ep,
+               }
+               rb := &EpisodeRange{
+                       Type:  er.Type,
+                       Start: ep,
+                       End:   end,
+               }
+
+               ra = ra.Split(ep)[0]
+               rb = rb.Split(ep)[1]
+
+               if ra.Valid() {
+                       ra.Simplify()
+               } else {
+                       ra = nil
+               }
+               if rb.Valid() {
+                       rb.Simplify()
+               } else {
+                       rb = nil
+               }
+
+               return []*EpisodeRange{ra, rb}
+       }
+}
+
 // Returns true if both ranges are of the same type and
 // have identical start/end positions
 func (a *EpisodeRange) Equals(b *EpisodeRange) bool {
@@ -135,8 +281,9 @@ func (a *EpisodeRange) Equals(b *EpisodeRange) bool {
        }
 
        if a.Type == b.Type {
-               if a.End == b.End || (a.End != nil && b.End != nil && a.End.Number == b.End.Number) {
-                       if a.Start == b.Start || a.Start.Number == b.Start.Number {
+               if a.End == b.End || (a.End != nil && b.End != nil &&
+                       a.End.Number == b.End.Number && a.End.Part == b.End.Part) {
+                       if a.Start == b.Start || a.Start.Number == b.Start.Number && a.Start.Part == b.Start.Part {
                                return true
                        }
                }
@@ -144,46 +291,131 @@ func (a *EpisodeRange) Equals(b *EpisodeRange) bool {
        return false
 }
 
+// CORNER CASE: e.g. 1.3,2.0 (or 1.3,2) always touch,
+// even if there's an unlisted 1.4 between them; unless
+// the part count is known.
 func (a *EpisodeRange) touches(b *EpisodeRange) bool {
        if a == nil || b == nil || a.Type != b.Type {
                return false
        }
 
        switch {
+       case a == b:
+               // log.Println("same pointers")
+               return true
+       case a.Start == b.Start, a.End != nil && a.End == b.End:
+               // log.Println("share pointers")
+               return true
+
        case a.End == nil:
                switch {
                case b.End == nil:
-                       // both infinite
+                       // log.Println("both infinite")
                        return true
 
-               case b.End.Number >= a.Start.Number-1:
-                       // {b  [ }  a ...
-                       // start-1 so it's still true when they're only adjacent
+               case b.End.Number == a.Start.Number:
+                       switch {
+                       // either is whole, or parts are adjacent/overlap
+                       case b.End.Part == -1, a.Start.Part == -1,
+                               b.End.Part >= a.Start.Part-1:
+                               // log.Printf("{ %s [} %s ...", b.End, a.Start)
+                               return true
+                       }
+               // only if start of next range is whole or is first part
+               case b.End.Number == a.Start.Number-1 && a.Start.Part <= 0:
+                       switch {
+                       // end is whole, or is last part, or part count is unknown
+                       case b.End.Part == -1, b.End.Parts == 0,
+                               b.End.Part == b.End.Parts:
+                               // log.Printf("{ %s }[ %s ...", b.End, a.Start)
+                               return true
+                       }
+               case b.End.Number > a.Start.Number:
+                       // log.Printf("{ %s [ } %s ...", b.End, a.Start)
                        return true
                }
 
        case b.End == nil:
                switch {
-               case a.End.Number >= b.Start.Number-1:
-                       // [a  { ]  b ...
+               case a.End.Number == b.Start.Number:
+                       switch {
+                       case a.End.Part == -1, b.Start.Part == -1,
+                               a.End.Part >= b.Start.Part-1:
+                               // log.Printf("[ %s {] %s ...", a.End, b.Start)
+                               return true
+                       }
+               case a.End.Number == b.Start.Number-1 && b.Start.Part <= 0:
+                       switch {
+                       case a.End.Part == -1, a.End.Parts == 0,
+                               a.End.Part == a.End.Parts:
+                               // log.Printf("[ %s ]{ %s ...", a.End, b.Start)
+                               return true
+                       }
+               case a.End.Number > b.Start.Number:
+                       // log.Printf("[ %s { ] %s ...", a.End, b.Start)
                        return true
                }
 
-       case a.Start.Number == b.Start.Number || a.End.Number == b.End.Number:
+       case a.Start.Number == b.Start.Number:
                // touching
-               return true
+               switch {
+               // either is whole, or parts are immediately adjacent
+               case a.Start.Part == -1, b.Start.Part == -1,
+                       a.Start.Part == b.Start.Part,
+                       a.Start.Part == b.Start.Part-1,
+                       a.Start.Part == b.Start.Part+1:
+                       // log.Printf("[{ %s - %s ]}", a.End, b.Start)
+                       return true
+               }
+       case a.End.Number == b.End.Number:
+               switch {
+               case a.End.Part == -1, b.End.Part == -1,
+                       a.End.Part == b.End.Part,
+                       a.End.Part == b.End.Part-1,
+                       a.End.Part == b.End.Part+1:
+                       // log.Printf("{[ %s - %s }]", b.End, a.Start)
+                       return true
+               }
 
        case a.End.Number < b.End.Number:
                switch {
-               case a.End.Number >= b.Start.Number-1:
-                       // [a  { ]  b}
+               case a.End.Number == b.Start.Number:
+                       switch {
+                       case a.End.Part == -1, b.Start.Part == -1,
+                               a.End.Part >= b.Start.Part-1:
+                               // log.Printf("[ %s {] %s }", a.End, b.Start)
+                               return true
+                       }
+               case a.End.Number == b.Start.Number-1 && b.Start.Part <= 0:
+                       switch {
+                       case b.End.Part == -1, b.End.Parts == 0,
+                               b.End.Part == b.End.Parts:
+                               // log.Printf("[ %s ]{ %s }", a.End, b.Start)
+                               return true
+                       }
+               case a.End.Number > b.Start.Number:
+                       // log.Printf("[ %s { ] %s }", a.End, b.Start)
                        return true
                }
 
        case b.End.Number < a.End.Number:
                switch {
-               case b.End.Number >= a.Start.Number-1:
-                       // {b  [ }  a]
+               case b.End.Number == a.Start.Number:
+                       switch {
+                       case b.End.Part == -1, a.Start.Part == -1,
+                               b.End.Part >= a.Start.Part-1:
+                               // log.Printf("{ %s [} %s ]", b.End, a.Start)
+                               return true
+                       }
+               case b.End.Number == a.Start.Number-1 && a.Start.Part <= 0:
+                       switch {
+                       case b.End.Part == -1, b.End.Parts == 0,
+                               b.End.Part == b.End.Parts:
+                               // log.Printf("{ %s }[ %s ]", b.End, a.Start)
+                               return true
+                       }
+               case b.End.Number > a.Start.Number:
+                       // log.Printf("{ %s [ } %s ]", b.End, a.Start)
                        return true
                }
        }