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