gitee.com/quant1x/gox@v1.21.2/api/slices_limit.go (about) 1 // Copyright 2018-20 PJ Engineering and Business Solutions Pty. Ltd. All rights reserved. 2 3 package api 4 5 import ( 6 "errors" 7 "fmt" 8 ) 9 10 var ( 11 ErrRangeInvalid = errors.New("range invalid") 12 ErrLengthUndefined = errors.New("limit undefined") 13 ErrLengthNotProvided = errors.New("end is nil so length must be provided") 14 ) 15 16 // ScopeLimit is used to specify a range. Both Start and End are inclusive. 17 // A nil value means no limit, so a Start of nil means 0 18 // and an End of nil means no limit. 19 // The End value must always be equal to or larger than Start. 20 // Negative values are acceptable. A value of -2 means the second last row. 21 type ScopeLimit struct { 22 Start *int 23 End *int 24 } 25 26 // String implements Stringer interface. 27 func (r ScopeLimit) String() string { 28 if r.Start == nil { 29 if r.End == nil { 30 return "ScopeLimit:nil—nil" 31 } 32 return fmt.Sprintf("ScopeLimit:nil—%d", *r.End) 33 } 34 if r.End == nil { 35 return fmt.Sprintf("ScopeLimit:%d—nil", *r.Start) 36 } 37 return fmt.Sprintf("ScopeLimit:%d—%d", *r.Start, *r.End) 38 } 39 40 // NRows returns the number of rows contained by ScopeLimit. 41 // If End is nil, then length must be provided. 42 func (r *ScopeLimit) NRows(length ...int) (int, error) { 43 if len(length) > 0 { 44 s, e, err := r.Limits(length[0]) 45 if err != nil { 46 return 0, err 47 } 48 return e - s + 1, nil 49 } 50 51 if r.End == nil { 52 return 0, ErrLengthNotProvided 53 } 54 55 var s int 56 if r.Start != nil { 57 s = *r.Start 58 } 59 if s < 0 || *r.End < 0 { 60 return 0, ErrRangeInvalid 61 } 62 if *r.End < s { 63 return 0, ErrRangeInvalid 64 } 65 return *r.End - s + 1, nil 66 } 67 68 // Limits is used to return the start and end limits of a ScopeLimit 69 // object for a given Dataframe or Series with length number of rows. 70 func (r *ScopeLimit) Limits(length int) (s int, e int, _ error) { 71 if length <= 0 { 72 return 0, 0, ErrLengthUndefined 73 } 74 75 if r.Start == nil { 76 s = 0 77 } else { 78 if *r.Start < 0 { 79 // negative 80 s = length + *r.Start 81 } else { 82 s = *r.Start 83 } 84 } 85 86 if r.End == nil { 87 e = length - 1 88 } else { 89 if *r.End < 0 { 90 // negative 91 e = length + *r.End 92 } else { 93 e = *r.End 94 } 95 } 96 if s < 0 || e < 0 { 97 return 0, 0, ErrRangeInvalid 98 } 99 if s > e { 100 return 0, 0, ErrRangeInvalid 101 } 102 if s >= length || e >= length { 103 return 0, 0, ErrRangeInvalid 104 } 105 return 106 } 107 108 func (r *ScopeLimit) Limited(length int) (start, end int) { 109 s, e, err := r.Limits(length) 110 if err != nil { 111 panic(err) 112 } 113 return s, e 114 } 115 116 // RangeFinite returns a ScopeLimit that has a finite span. 117 func RangeFinite(start int, end ...int) ScopeLimit { 118 r := ScopeLimit{ 119 Start: &start, 120 } 121 if len(end) > 0 { 122 r.End = &end[0] 123 } 124 return r 125 } 126 127 // IntsToRanges will convert an already (ascending) ordered list of ints to a slice of Ranges. 128 // 129 // Example: 130 // 131 // import "sort" 132 // ints := []int{2,4,5,6,8,10,11,45,46} 133 // sort.Ints(ints) 134 // 135 // fmt.Println(IntsToRanges(ints)) 136 // // Output: R{2,2}, R{4,6}, R{8,8}, R{10,11}, R{45,46} 137 func IntsToRanges(ints []int) []ScopeLimit { 138 var out []ScopeLimit 139 OUTER: 140 for i := 0; i < len(ints); i++ { 141 v1 := ints[i] 142 143 j := i + 1 144 for { 145 if j >= len(ints) { 146 // j doesn't exist 147 v2 := ints[j-1] 148 out = append(out, ScopeLimit{Start: &v1, End: &v2}) 149 break OUTER 150 } else { 151 // j does exist 152 v2 := ints[j] 153 prevVal := ints[j-1] 154 155 if (v2 != prevVal) && (v2 != prevVal+1) { 156 out = append(out, ScopeLimit{Start: &v1, End: &prevVal}) 157 i = j - 1 158 break 159 } 160 j++ 161 continue 162 } 163 } 164 } 165 166 return out 167 }