And before someone corrects me, an even less embarrassing variant might be: 
    
    
    proc rangeAt*[T](xs: openArray[T]): tuple[minAt, maxAt: int] =
      if xs.len < 1: return (-1, -1)  # raise?
      for i in 1 ..< xs.len:          # note tuple ints init to zero
        if xs[result.minAt] < xs[i]: result.minAt = i
        if xs[result.minAt] > xs[i]: result.minAt = i
    
    
    Run

with then `let (minAt, maxAt) = strs.rangeAt` and tracking `(min|max)` -> 
`strs[(min|max)At]` changes in the string comparison for the prefix length 
algo. Some paired-up value oriented interface for those who want it would also 
make sense: 
    
    
    proc range*[T](xs: openArray[T]): tuple[min, max: T] {.inline.} =
      if xs.len < 1: return           # raise?
      let (minAt, maxAt) = xs.rangeAt
      result.min = xs[minAt]
      result.max = xs[maxAt]
    
    
    Run

in which case maybe no change to the prefix len algo is needed.

While off the topic of the prefix calculation, it perhaps bears noting that 
SSE/AVX calculations can sometimes speed up the value-oriented versions of 
these range computations over arrays of CPU supported types like 
`openArray[float32]` by large factors - 24x even. Basically branchless and 
batching vector min/max instructions can be leveraged, but I am unaware of CPUs 
supporting "minAt" type vector instructions. Recent versions of gcc can 
recognize these min/max type value sweeps and correctly generate optimized 
code, but such pattern recognition can often fail as the code grows just a 
little more complex.

Reply via email to