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  }