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  }