github.com/primecitizens/pcz/std@v0.2.1/core/iter/utils.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  
     4  package iter
     5  
     6  import (
     7  	"github.com/primecitizens/pcz/std/core/assert"
     8  )
     9  
    10  // Contains returns true if there is x in iter.
    11  func Contains[T comparable, Iter Core[T]](iter Iter, x T) bool {
    12  	for i := 0; ; i++ {
    13  		item, ok := iter.Nth(i)
    14  		if !ok {
    15  			return false
    16  		}
    17  
    18  		if x == item {
    19  			return true
    20  		}
    21  	}
    22  }
    23  
    24  // Index returns the index in range [0, n) from i.
    25  //
    26  // assuming n > 0:
    27  //
    28  //   - when i >= 0, validate it
    29  //   - when i < 0, index from the n
    30  //
    31  // NOTE: the returned index is only valid when valid is true.
    32  func Index(i, n int) (index int, valid bool) {
    33  	if i >= 0 {
    34  		return i, i < n
    35  	}
    36  
    37  	if n > 0 {
    38  		i = n + i
    39  		return i, i >= 0 && i < n
    40  	}
    41  
    42  	return -1, false
    43  }
    44  
    45  // Bound returns value used for slice bounding.
    46  //
    47  // assuming n >= 0:
    48  //
    49  //   - i >= 0 && i < n: Bound(i, n) = Index(i, n)
    50  //   - i == n: returns (i, true)
    51  //   - i < 0: Bound(i, n) = Index(i, n) + 1
    52  //
    53  // NOTE: the returned bound is only valid when valid is true.
    54  func Bound(i, n int) (bound int, valid bool) {
    55  	if i >= 0 {
    56  		return i, i <= n
    57  	}
    58  
    59  	if n < 0 {
    60  		return -1, false
    61  	}
    62  
    63  	i = n + i + 1
    64  	return i, i >= 0 && i <= n
    65  }
    66  
    67  // Range returns the [start:end] in range [0, n]
    68  func Range(start, end, n int) (int, int, bool) {
    69  	if start >= 0 && end >= 0 {
    70  		return start, end, start <= end && end <= n
    71  	}
    72  
    73  	if start < 0 {
    74  		start = n + start + 1
    75  		if start < 0 {
    76  			return -1, -1, false
    77  		}
    78  	}
    79  
    80  	if end < 0 {
    81  		end = n + end + 1
    82  		if end < 0 {
    83  			return -1, -1, false
    84  		}
    85  	}
    86  
    87  	return start, end, start <= end && end <= n
    88  }
    89  
    90  // Each calls fn on each element from the iter.
    91  func Each[Iter Core[Elem], Elem any](
    92  	iter Iter, fn func(i int, v Elem) bool,
    93  ) int {
    94  	return EachEx(iter, fn, func(i int, v Elem, fn func(i int, v Elem) bool) bool {
    95  		return fn(i, v)
    96  	})
    97  }
    98  
    99  // EachEx is like Each but calls fn with an extra arg.
   100  func EachEx[Iter Core[Elem], Elem, Arg any](
   101  	iter Iter, arg Arg, fn func(i int, v Elem, arg Arg) bool,
   102  ) int {
   103  	var (
   104  		val Elem
   105  		ok  bool
   106  	)
   107  
   108  	for i := 0; ; i++ {
   109  		if val, ok = iter.Nth(i); ok {
   110  			if fn(i, val, arg) {
   111  				continue
   112  			}
   113  		}
   114  
   115  		return i
   116  	}
   117  }
   118  
   119  // Step calls fn on every next step element from the iter.
   120  func Step[Elem any](
   121  	iter Core[Elem], from, step int, fn func(i int, v Elem) bool,
   122  ) int {
   123  	var (
   124  		val Elem
   125  		ok  bool
   126  	)
   127  
   128  	if step == 0 {
   129  		assert.Throw("invald", "zero", "step")
   130  	}
   131  
   132  	if step < 0 {
   133  		if _, ok = iter.(CanBackward); ok {
   134  			assert.Throw("no", "backward", "iteration", "support")
   135  		}
   136  	}
   137  
   138  	if _, ok = iter.(CanSkip); ok && (step > 1 || step < -1) {
   139  		if step < 0 {
   140  			for i := from; ; i-- {
   141  				if val, ok = iter.Nth(i); ok {
   142  					if i == from {
   143  						if fn(i, val) {
   144  							from += step
   145  							continue
   146  						}
   147  					} else {
   148  						continue
   149  					}
   150  				}
   151  
   152  				return i
   153  			}
   154  		} else {
   155  			for i := from; ; i++ {
   156  				if val, ok = iter.Nth(i); ok {
   157  					if i == from {
   158  						if fn(i, val) {
   159  							from += step
   160  							continue
   161  						}
   162  					} else {
   163  						continue
   164  					}
   165  				}
   166  
   167  				return from
   168  			}
   169  		}
   170  
   171  		assert.Unreachable()
   172  	}
   173  
   174  	for ; ; from += step {
   175  		if val, ok = iter.Nth(from); ok {
   176  			if fn(from, val) {
   177  				continue
   178  			}
   179  		}
   180  
   181  		return from
   182  	}
   183  }
   184  
   185  // StepEx is like Step but calls fn with an extra arg.
   186  //
   187  // It panics if from or step is invalid for the iter.
   188  func StepEx[Elem, Arg any](
   189  	iter Core[Elem], from, step int, arg Arg, fn func(i int, v Elem, arg Arg) bool,
   190  ) {
   191  	var (
   192  		val Elem
   193  		ok  bool
   194  	)
   195  
   196  	if step == 0 {
   197  		assert.Throw("invald", "zero", "step")
   198  	}
   199  
   200  	if step < 0 {
   201  		if _, ok = iter.(CanBackward); ok {
   202  			assert.Throw("no", "backward", "iteration", "support")
   203  		}
   204  	}
   205  
   206  	if _, ok = iter.(CanSkip); ok {
   207  		if step < 0 {
   208  			for i := from; ; i-- {
   209  				if val, ok = iter.Nth(i); ok {
   210  					if i == from {
   211  						if fn(i, val, arg) {
   212  							from += step
   213  							continue
   214  						}
   215  					} else {
   216  						continue
   217  					}
   218  				}
   219  
   220  				return
   221  			}
   222  
   223  			assert.Unreachable()
   224  		}
   225  
   226  		for i := from; ; i++ {
   227  			if val, ok = iter.Nth(i); ok {
   228  				if i == from {
   229  					if fn(i, val, arg) {
   230  						from += step
   231  						continue
   232  					}
   233  				} else {
   234  					continue
   235  				}
   236  			}
   237  
   238  			return
   239  		}
   240  
   241  		assert.Unreachable()
   242  	}
   243  
   244  	for ; ; from += step {
   245  		if val, ok = iter.Nth(from); ok {
   246  			if fn(from, val, arg) {
   247  				continue
   248  			}
   249  		}
   250  
   251  		return
   252  	}
   253  }