Full Code of rakeeb-hossain/functools for AI

master 58f775af362d cached
20 files
42.8 KB
13.5k tokens
85 symbols
1 requests
Download .txt
Repository: rakeeb-hossain/functools
Branch: master
Commit: 58f775af362d
Files: 20
Total size: 42.8 KB

Directory structure:
gitextract_s452whe8/

├── .github/
│   └── workflows/
│       └── go.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── doc.go
├── examples/
│   ├── error-handling/
│   │   └── error_handling.go
│   └── portfolio/
│       └── portfolio.go
├── functionals.go
├── functionals_test.go
├── go.mod
├── go.sum
├── spined_buffer.go
├── spined_buffer_test.go
├── spliterator.go
├── spliterator_test.go
├── stream.go
├── stream_test.go
├── terminals.go
└── terminals_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/go.yml
================================================
name: Go

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2

    - name: Set up Go
      uses: actions/setup-go@v2
      with:
        stable: false
        go-version: 1.18.0-beta1

    - name: Build
      run: go build -v ./...

    - name: Test
      run: go test -v ./...


================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Project files
.idea/
java_tests/


================================================
FILE: CONTRIBUTING.md
================================================
# functools Contributing guide

Thank you for considering contributing to functools! To get an overview of the project, read the [README](README.md) and peruse through the codebase. 

### Open an Issue

All contributions should be linked to issues, so if you think a functional component or optimization should be added to functools, start by filing a request there. After discussion with the community, the issue will be assigned to you if deemed a good fit for the library.

### Fork and PR

Make a fork of this repository and commit your approriate changes. Afterwards, submit a PR for review. 

### PR merged!

If approved, your work will be merged and added to the Golang functools library 🎉🎉 Thank you for your contribution!

### Questions?

Email me at rakeeb.hossain1@gmail.com and I'll get back to you ASAP


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Rakeeb Hossain

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# functools

[![Go](https://github.com/rakeeb-hossain/functools/actions/workflows/go.yml/badge.svg)](https://github.com/rakeeb-hossain/functools/actions/workflows/go.yml)

functools is a simple Go library that brings you your favourite functional paradigms without sacrificing type-safety using 
`interface{}` or `reflect`

Made possible by Go 1.18 using the newly introduced generics.

## Features
 
- Any
- All
- Count
- Filter
- ForEach
- Map
- Reduce
- ReduceRight
- Sum
- Chunk

## Installation

`go get -u github.com/rakeeb-hossain/functools`

## Usage

```go
import (
    "github.com/rakeeb-hossain/functools"
    "fmt"
)

type User struct {
	username     string
	hasPortfolio bool
}

var users = []User{
		{"gopher", true},
		{"rakeeb", false},
		{"jack", true}}

func main() {
    // Count users with linked portfolios
    fmt.Printf("num users with linked portfolios: %d", 
        functools.Count(users, func(u User) bool { return u.hasPortfolio }))

    // Print usernames of users with linked portfolios
    functools.ForEach(
        functools.Filter(users, func(u User) bool { return u.hasPortfolio }),
        func(u User) { fmt.Printf("%s has a linked portfolio\n", u.username) })
}
```

## Documentation

https://pkg.go.dev does not yet support Go 1.18 packages that use generics: https://github.com/golang/go/issues/48264

For now, documentation is provided via comments and by running `go doc -all` from the package directory. 

## Contributing

Please see [CONTRIBUTING](CONTRIBUTING.md)


================================================
FILE: doc.go
================================================
// Package functools provides type-safe functional helpers using Go 1.18 generics.
//
// functools is intended to allow writing concise functionally-inspired code without sacrificing performance or type-safety,
// as is the case for functional libraries pre-Go generics that had to use `interface{}` or `reflect`.

package functools


================================================
FILE: examples/error-handling/error_handling.go
================================================
// This showcases how we can handle errors using functools helpers, despite there being no explicit error
// return types by the functools functions. We make use of passing errors via closures.
//
// This is more explicit than it likely would be in practice just to showcase what safe handling capabilities
// this functools still enables. In practice, checking for errant input as a pre-step before dividing would likely
// be more effective.
package main

import (
	"errors"
	"github.com/rakeeb-hossain/functools"
	"log"
)

type fraction struct {
	dividend int
	divisor  int
}

func main() {
	fractions := []fraction{{5, 1}, {3, 6}, {2, 0}}

	// We handle errors by populating an error which we pass to our mapper function via a closure.
	// We also return a pointer to a float64 instead of a float64 itself, so we can handle nil types
	// in case we encounter an error.
	var err error
	safeDivide := func(f fraction) *float64 {
		if f.divisor == 0 {
			err = errors.New("cannot divide by 0")
			return nil
		}
		res := float64(f.dividend) / float64(f.divisor)
		return &res
	}
	rationalResults := functools.Map(fractions, safeDivide)
	if err != nil {
		log.Println(err)
	}

	// We can sum the safe rational results using a custom Reduce function
	res := functools.Reduce(rationalResults, 0.0, func(accum float64, n *float64) float64 {
		if n == nil {
			return accum
		}
		return accum + *n
	})

	log.Printf("the safe sum of fractions %v is %f\n", fractions, res)
}


================================================
FILE: examples/portfolio/portfolio.go
================================================
// This showcases several applications of functools helpers to a store of user objects and their associated
// portfolio holdings.
package main

import (
	"github.com/rakeeb-hossain/functools"
	"log"
)

type user struct {
	username     string
	age          int
	hasPortfolio bool
}

type holding struct {
	ticker     string
	boughtTime int
	quantity   float64
	price      float64
}

type portfolio struct {
	holdings []holding
}

var (
	users = []user{
		{"gopher", 21, true},
		{"rakeeb", 20, false},
		{"jack", 22, true}}

	usersPortfolioMap = map[string]portfolio{
		"gopher": {[]holding{{"TSLA", 1639768692, 4.5, 1000}, {"ABNB", 1639163892, 2.5, 200}}},
		"jack":   {[]holding{{"BTC", 1512933492, 5, 1000}, {"ETH", 1639768692, 10, 100}}}}
)

func main() {
	// Count users with linked portfolios
	log.Printf("num users with linked portfolios: %d", functools.Count(users, func(u user) bool { return u.hasPortfolio }))

	// Print usernames of users with linked portfolios
	functools.ForEach(
		functools.Filter(users, func(u user) bool { return u.hasPortfolio }),
		func(u user) { log.Printf("%s has a linked portfolio\n", u.username) },
	)

	// For users with connected portfolios, get portfolio values
	usersWithPortfolio := functools.Filter(users, func(u user) bool { return u.hasPortfolio })
	userPortfolioValues := functools.Map(usersWithPortfolio, func(u user) float64 {
		return functools.Reduce(usersPortfolioMap[u.username].holdings, 0, func(accum float64, h holding) float64 {
			return accum + h.quantity*h.price
		})
	})

	for i, _ := range usersWithPortfolio {
		log.Printf("user %s has portfolio value %f\n", usersWithPortfolio[i].username, userPortfolioValues[i])
	}

	// Get total price of assets in all connected portfolios
	totalVal := functools.Sum(userPortfolioValues)
	log.Printf("total asset value: %f", totalVal)
}


================================================
FILE: functionals.go
================================================
// Contains classic generic functional methods

package functools

//
//// Map consumes a slice of a generic type and returns a slice with the supplied mapping function
//// applied to each element.
////
//// mapper should be error-safe. It should handle any errors internally and return the desired type.
//// If other arguments are required by mapper, mapper should be made a closure with the appropriate
//// variables referenced.
//func Map_[A any, B any](mapper func(A) B, iter Spliterator[A]) (res Spliterator[B]) {
//	res.tryAdvance = func(fn func(B)) bool {
//		_mapper := func(a A) {
//			fn(mapper(a))
//		}
//		return iter.tryAdvance(_mapper)
//	}
//	// res.trySplit = iter.trySplit
//
//	return res
//}
//
//// Stateful op
//
//func Sorted[T any](iter Spliterator[T]) (res Spliterator[T]) {
//	return res
//}
//
////func ChunkIter[T any](iter Iterator[T], len int) Iterator[[]T] {
////	return func() (lst []T, b bool) {
////		res, ok := Next(iter)
////		if !ok {
////			return lst, ok
////		}
////
////		lst = make([]T, len)
////		lst[0] = res
////		for i := 1; i < len; i++ {
////			res, ok := Next(iter)
////			if !ok {
////				return lst, true
////			}
////			lst[i] = res
////		}
////		return lst, true
////	}
////}
//
//// Filter consumes a slice of a generic type and returns a slice with only the elements which returned true after
//// applying the predicate.
////
//// Elements are returned in the same order that they were supplied in the slice.
////
//// predicate should be error-safe. It should handle any errors internally and return only a bool.
//// If other arguments are required by predicate, predicate should be made a closure with the appropriate
//// variables referenced.
//func Filter[T any, A ~[]T](slice A, predicate func(T) bool) A {
//	res := make(A, 0, len(slice))
//	for _, v := range slice {
//		if predicate(v) {
//			res = append(res, v)
//		}
//	}
//	return res
//}
//
//func FilterIter[T any](iter Iterator[T], predicate func(T) bool) Iterator[T] {
//	return func() (t T, b bool) {
//		for val, ok := Next(iter); ok; val, ok = Next(iter) {
//			if !ok || predicate(val) {
//				return val, ok
//			}
//		}
//		return t, b // b is false here
//	}
//}
//
//// Reduce consumes a slice of a generic type and an initial value. It reduces the slice to a single value by applying
//// the binary reducer function to each successive element in the slice.
////
//// Vacuously, empty slices return the initial value provided.
////
//// reducer should be error-safe. It should handle any errors internally and return the desired type.
//// If other arguments are required by reducer, reducer should be made a closure with the appropriate
//// variables referenced.
//func Reduce[T any, R any](iter Spliterator[T], initial R, reducer func(R, T) R) R {
//	accum := initial
//	return accum
//}
//
//// ReduceRight consumes a slice of a generic type and an initial value. It
//// reduces the slice to a single value by applying the binary reducer function
//// to each element in the slice. ReduceRight differs from Reduce by iterating
//// from the last element to the first element.
////
//// Vacuously, empty slices return the initial value provided.
////
//// reducer should error-safe. It should handle any errors internally and return
//// the desired type. If other arguments are required by reducer, reducer should
//// be made a closure with the appropriate variables referenced.
//func ReduceRight[T any, A ~[]T, R any](slice A, initial R, reducer func(R, T) R) R {
//	accum := initial
//	for i := len(slice) - 1; i >= 0; i-- {
//		accum = reducer(accum, slice[i])
//	}
//	return accum
//}
//
//// ForEach applies fun to each element in slice
////
//// fun should be error-safe and handle errors internally. If other arguments are required by predicate,
//// predicate should be made a closure with the appropriate variables referenced.
//func ForEach[T any, A ~[]T](slice A, fun func(T)) {
//	for _, v := range slice {
//		fun(v)
//	}
//}
//
//// Count consumes a slice of a generic type and counts the elements that return true after applying
//// the predicate.
////
//// Vacuously, empty slices return 0 regardless of the predicate.
////
//// predicate should be error-safe. It should handle any errors internally and return only a bool.
//// If other arguments are required by predicate, predicate should be made a closure with the appropriate
//// variables referenced.
//func Count[T any, A ~[]T](slice A, predicate func(T) bool) int {
//	res := 0
//	for _, v := range slice {
//		if predicate(v) {
//			res++
//		}
//	}
//	return res
//}


================================================
FILE: functionals_test.go
================================================
package functools

//type user struct {
//	age int
//}
//
//// Filter tests
//func TestGeqFilter(t *testing.T) {
//	slice := []user{{17}, {21}, {18}, {32}, {49}, {76}}
//	geqTwentyOne := func(u user) bool { return u.age >= 21 }
//	res := Map(Filter(slice, geqTwentyOne), func(u user) int { return u.age })
//	expect := []int{21, 32, 49, 76}
//
//	for i, _ := range res {
//		if i >= len(expect) || res[i] != expect[i] {
//			t.Errorf("TestGeqFilter was incorrect, got: %v, expected: %v", res, expect)
//			return
//		}
//	}
//
//	mapper := func(val user) int { return val.age }
//	Map(func(val int) int { return val + 1 }, Map(mapper, SliceIter[user](slice)))
//}
//
//// Map tests
//func TestAddMap(t *testing.T) {
//	slice := []int{1, 2, 3}
//	adder := func(val int) int { return val + 1 }
//	res := Map(slice, adder)
//	expect := []int{2, 3, 4}
//
//	for i, _ := range res {
//		if i >= len(expect) || res[i] != expect[i] {
//			t.Errorf("TestAddMap was incorrect, got: %v, expected: %v", res, expect)
//			return
//		}
//	}
//}
//
//func TestAddMapIter(t *testing.T) {
//	slice := Iter([]int{1, 2, 3})
//	adder := func(val int) int { return val + 1 }
//	res := Slice(MapIter(slice, adder))
//	expect := []int{2, 3, 4}
//
//	for i, _ := range res {
//		if i >= len(expect) || res[i] != expect[i] {
//			t.Errorf("TestAddMapIter was incorrect, got: %v, expected: %v", res, expect)
//			return
//		}
//	}
//}
//
//func TestUserMap(t *testing.T) {
//	slice := []user{{32}, {29}, {42}}
//	ageTransformer := func(val user) int { return val.age }
//	res := Map[user, []user, int](slice, ageTransformer)
//	expect := []int{32, 29, 42}
//
//	for i, _ := range res {
//		if i >= len(expect) || res[i] != expect[i] {
//			t.Errorf("TestUserMap was incorrect, got: %v, expected: %v", res, expect)
//			return
//		}
//	}
//}
//
//// Reduce tests
//func TestReduceSum(t *testing.T) {
//	slice := []int{1, 2, 3}
//	adder := func(a, b int) int { return a + b }
//	res := Reduce(slice, 0, adder)
//	expect := 6
//
//	if res != expect {
//		t.Errorf("TestReduceSum was incorrect, got: %d, expected: %d", res, expect)
//	}
//}
//
//func TestReduceUserAge(t *testing.T) {
//	slice := []user{{32}, {29}, {42}}
//	adder := func(accum int, val user) int { return accum + val.age }
//	res := Reduce[user, []user, int](slice, 0, adder)
//	expect := 103
//
//	if res != expect {
//		t.Errorf("TestReduceUserAge was incorrect, got: %d, expected: %d", res, expect)
//	}
//}
//
//// ReduceRight tests
//type reduceRightCase[T any, R comparable] struct {
//	name    string
//	slice   []T
//	initial R
//	reducer func(R, T) R
//	want    R
//}
//
//func TestReduceRight(t *testing.T) {
//	t.Run("integers", func(t *testing.T) {
//
//		cases := []reduceRightCase[int, int]{
//			{
//				name:    "addition",
//				slice:   []int{1, 2, 3},
//				initial: 0,
//				reducer: func(a, b int) int { return a + b },
//				want:    6,
//			},
//			{
//				name:    "subtraction",
//				slice:   []int{1, 2, 3},
//				initial: 0,
//				reducer: func(a, b int) int { return a - b },
//				want:    -6,
//			},
//			{
//				name:    "multiplication",
//				slice:   []int{1, 2, 3},
//				initial: 1,
//				reducer: func(a, b int) int { return a * b },
//				want:    6,
//			},
//		}
//
//		for _, c := range cases {
//			t.Run(c.name, func(t *testing.T) {
//				got := ReduceRight(c.slice, c.initial, c.reducer)
//
//				if got != c.want {
//					t.Errorf("got %v, want %v", got, c.want)
//				}
//			})
//		}
//	})
//
//	t.Run("integers and floats", func(t *testing.T) {
//		cases := []reduceRightCase[int, float64]{
//			{
//				name:    "division",
//				slice:   []int{1, 2, 3},
//				initial: 1.0,
//				reducer: func(accum float64, curr int) float64 { return float64(curr) / accum },
//				want:    1.5,
//			},
//		}
//
//		for _, c := range cases {
//			t.Run(c.name, func(t *testing.T) {
//				got := ReduceRight(c.slice, c.initial, c.reducer)
//
//				if got != c.want {
//					t.Errorf("got %v, want %v", got, c.want)
//				}
//			})
//		}
//	})
//}
//
//// ForEach tests
//func TestClosureForEach(t *testing.T) {
//	slice := []int{1, 2, 3}
//	res := 0
//	ForEach(slice, func(val int) { res += val })
//	expect := 6
//
//	if res != expect {
//		t.Errorf("TestClosureForEach was incorrect, got: %d, expected: %d", res, expect)
//	}
//}
//
//// Count tests
//func TestGeqCount(t *testing.T) {
//	slice := []int{1, 100, 200, 3, 14, 21, 32}
//	res := Count(slice, func(val int) bool { return val >= 21 })
//	expected := 4
//
//	if res != expected {
//		t.Errorf("TestLtAny with %v was incorrect, got: %d, expected: %d", slice, res, expected)
//	}
//}


================================================
FILE: go.mod
================================================
module github.com/rakeeb-hossain/functools

go 1.18

require golang.org/x/exp v0.0.0-20220428152302-39d4317da171 // indirect


================================================
FILE: go.sum
================================================
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=


================================================
FILE: spined_buffer.go
================================================
package functools

import (
	"fmt"
)

const FirstBuffPower int = 4
const MinSpineSize int = 2 // must be >= 1
const SpineExtendCount int = 1

type AbstractBuffer[T any] interface {
	Push(T)
	At(int)
	Flatten()
	Len() int
	Capacity() int
}

// SpinedBuffer is an optimization on a regular slice that doesn't require copying elements on re-sizing.
// This has good performance in cases where an unknown size stream is being processed, since copying from
// re-sizing is minimized.
type SpinedBuffer[T any] struct {
	// Spine data structures
	// We optimistically assume everything will fit into currBuff in most cases
	currBuff []T
	spines   [][]T

	// Spine state management
	sizeOfPrevBuffers []int
	spineIdx          int
	flatIdx           int
	sizePower         int
	capacity          int
	inflated          bool
}

// Checks if copy is required and copies currBuff to spines
func (s *SpinedBuffer[T]) inflateSpine() {
	if s.spineIdx == 0 && s.flatIdx == s.capacity {
		// Create spines
		s.spines = make([][]T, MinSpineSize)

		// Assign currBuff to first spine and set sizeOfPrevBuffers
		s.spines[0] = s.currBuff[:] // should be O(1) since just copying slice
		s.sizeOfPrevBuffers = make([]int, 1, MinSpineSize)
		s.sizeOfPrevBuffers[0] = s.flatIdx

		// Update subsequent spines
		for i := 1; i < MinSpineSize; i++ {
			s.sizePower++
			s.spines[i] = make([]T, 0, 1<<s.sizePower)
			s.capacity += 1 << s.sizePower
		}
	}
	s.inflated = true
}

func CreateSpinedBuffer[T any]() (s SpinedBuffer[T]) {
	s.spineIdx = 0
	s.flatIdx = 0
	s.sizePower = FirstBuffPower
	s.capacity = 1 << FirstBuffPower

	s.currBuff = make([]T, s.capacity)
	s.spines = nil
	s.sizeOfPrevBuffers = nil

	return s
}

func (s SpinedBuffer[T]) Len() int {
	return s.flatIdx
}

func (s SpinedBuffer[T]) Capacity() int {
	return s.capacity
}

func (s *SpinedBuffer[T]) Push(elem T) {
	if s.flatIdx < cap(s.currBuff) {
		// Assign elem to currBuff
		s.currBuff[s.flatIdx] = elem
		s.flatIdx++
	} else {
		if !s.inflated {
			s.inflateSpine()
		}
		// Check if len == cap
		if len(s.spines[s.spineIdx]) == (1 << (s.spineIdx + FirstBuffPower)) {
			// Check if we need to extend capacity
			if s.flatIdx == s.capacity {
				// Allocate new array into spines, update capacity, and increment spineIdx
				for i := 0; i < SpineExtendCount; i++ {
					s.sizePower++
					newBuff := make([]T, 0, 1<<s.sizePower)
					s.capacity += 1 << s.sizePower

					// NOTE: this is where the main optimization happens; only need to copy over existing slice
					// pointers, NOT their respective entries
					s.spines = append(s.spines, newBuff)
				}
			}
			s.spineIdx++

			// Assign value to new spine
			s.spines[s.spineIdx] = append(s.spines[s.spineIdx], elem)
			s.flatIdx++

			// Create new sizeOfPrevBuffers entry
			s.sizeOfPrevBuffers = append(s.sizeOfPrevBuffers, 0)
		} else {
			s.spines[s.spineIdx] = append(s.spines[s.spineIdx], elem)
			s.flatIdx++
		}
		s.sizeOfPrevBuffers[len(s.sizeOfPrevBuffers)-1] = s.flatIdx
	}
}

func (s SpinedBuffer[T]) Flatten() []T {
	if s.spineIdx == 0 {
		return s.currBuff[:s.flatIdx]
	}

	res := make([]T, s.flatIdx)
	currIdx := 0
	for i := 0; i <= s.spineIdx; i++ {
		j := 0
		for ; currIdx < s.sizeOfPrevBuffers[i]; currIdx++ {
			res[currIdx] = s.spines[i][j]
			j++
		}
	}

	return res
}

func (s SpinedBuffer[T]) At(index int) (res T) {
	if index < s.flatIdx && index >= 0 {
		if s.spineIdx == 0 {
			res = s.currBuff[index]
		} else {
			// binary-search for upper-bound; gives index of first elem in sizeOfPrevBuffers that is >= index
			// this index is guaranteed to be valid since sizeOfPrevBuffers last elem is s.flatIdx and index < s.flatIdx
			spineSizeIdx := upperBoundGuaranteed(index, s.sizeOfPrevBuffers)

			// Equality-case where index actually belongs to next spine
			if s.sizeOfPrevBuffers[spineSizeIdx] == index {
				res = s.spines[spineSizeIdx+1][0]
			} else {
				offset := index // case where index belongs to first spine so spineSizeIdx == 0
				if spineSizeIdx > 0 {
					offset = index - s.sizeOfPrevBuffers[spineSizeIdx-1]
				}
				res = s.spines[spineSizeIdx][offset]
			}

		}
	}
	return res
}

func (s SpinedBuffer[T]) PrintStats() {
	fmt.Printf("spineIdx: %d\n", s.spineIdx)
	fmt.Printf("flatIdx: %d\n", s.flatIdx)
	fmt.Printf("capacity: %d\n", s.capacity)
	fmt.Printf("sizePower: %d\n", s.sizePower)
}

func upperBoundGuaranteed(val int, arr []int) int {
	lo := 0
	hi := len(arr)
	for lo < hi {
		mid := (hi-lo)/2 + lo

		if arr[mid] >= val {
			hi = mid
		} else {
			lo = mid + 1
		}
	}
	return lo
}


================================================
FILE: spined_buffer_test.go
================================================
package functools

import (
	"math/rand"
	"reflect"
	"testing"
)

func TestSpinedBuffer_Push(t *testing.T) {
	NUM_TRIALS := 10000
	buff := CreateSpinedBuffer[int]()
	arr := make([]int, 0, NUM_TRIALS)

	for i := 1; i <= NUM_TRIALS; i++ {
		buff.Push(i)
		arr = append(arr, i)
		if !reflect.DeepEqual(arr, buff.Flatten()) {
			buff.PrintStats()
			t.Errorf("error at %d", i)
		}
	}
}

func TestSpinedBuffer_At(t *testing.T) {
	NUM_TRIALS := 10000
	buff := CreateSpinedBuffer[int]()

	for i := 1; i <= NUM_TRIALS; i++ {
		buff.Push(i)

		if buff.Len() != i {
			t.Errorf("error at %d", i)
		}

		r := rand.Int() % buff.Len()
		if buff.At(r) != (r + 1) {
			t.Errorf("error at index %d on iteration %d", r, i)
		}
	}
}

const BENCHMARK_SIZE int = 10000

func BenchmarkCreateSpinedBuffer_Push(b *testing.B) {
	buff := CreateSpinedBuffer[int]()

	for i := 0; i < BENCHMARK_SIZE; i++ {
		buff.Push(i)
	}
}

func BenchmarkSlicePush(b *testing.B) {
	slice := make([]int, 0)

	for i := 0; i < BENCHMARK_SIZE; i++ {
		slice = append(slice, i)
	}
}

//func BenchmarkArrayPush(b *testing.B) {
//	arr := [BENCHMARK_SIZE]int{}
//
//	for i := 0; i < BENCHMARK_SIZE; i++ {
//		arr[i] = i
//	}
//}


================================================
FILE: spliterator.go
================================================
package functools

import (
	"golang.org/x/exp/constraints"
)

type Spliterator[T any] struct {
	tryAdvance       func(func(T)) bool
	forEachRemaining func(func(T))
	trySplit         func() (Spliterator[T], bool)
	characteristics  uint
}

func EmptyIter[T any]() (res Spliterator[T]) {
	res.tryAdvance = func(func(T)) bool {
		return false
	}
	res.forEachRemaining = func(func(T)) {}
	res.trySplit = func() (r Spliterator[T], b bool) {
		return r, b
	}
	return res
}

func sliceIterRec[T any, A ~[]T](slice A, lo int, hi int) (res Spliterator[T]) {
	res.tryAdvance = func(fn func(T)) bool {
		if lo >= hi {
			return false
		}
		fn(slice[lo])
		lo++
		return true
	}

	res.forEachRemaining = func(fn func(T)) {
		for ; lo < hi; lo++ {
			fn(slice[lo])
		}
	}

	res.trySplit = func() (s Spliterator[T], b bool) {
		mid := (hi-lo)/2 + lo
		if mid != lo {
			s, b = sliceIterRec[T, A](slice, mid, hi), true
			// Modify current sliceIter before returning
			hi = mid
		}
		return s, b
	}
	return res
}

func SliceIter[T any, A ~[]T](slice A) (res Spliterator[T]) {
	return sliceIterRec[T, A](slice, 0, len(slice))
}

func RuleIter[T any, A ~func() (T, bool)](rule A) (res Spliterator[T]) {
	res.tryAdvance = func(fn func(T)) bool {
		r, b := rule()
		if b {
			fn(r)
		}
		return b
	}
	res.forEachRemaining = func(fn func(T)) {
		for r, b := rule(); b; r, b = rule() {
			fn(r)
		}
	}
	res.trySplit = func() (s Spliterator[T], b bool) {
		return s, b
	}
	return res
}

func sign[T constraints.Integer](x T) int8 {
	if x > 0 {
		return 1
	} else if x < 0 {
		return -1
	} else {
		return 0
	}
}

func RangeIter[T constraints.Integer](start, stop T, step T) (res Spliterator[T]) {
	// Check to ensure no infinite-loop
	if sign(stop-start)*sign(step) < 0 {
		return EmptyIter[T]()
	}

	res.tryAdvance = func(fn func(T)) bool {
		if start >= stop {
			return false
		}
		fn(start)
		start += step
		return true
	}
	res.forEachRemaining = func(fn func(T)) {
		for ; start < stop; start += step {
			fn(start)
		}
	}
	res.trySplit = func() (s Spliterator[T], b bool) {
		mid := (stop-start)/2 + start
		if mid != start {
			s, b = RangeIter[T](mid, stop, step), true
			// Modify stop for this iter
			stop = mid
		}
		return s, b
	}
	return res
}

func ChanIter[T any, C ~chan T](ch C) (res Spliterator[T]) {
	res.tryAdvance = func(fn func(T)) bool {
		v, ok := <-ch
		if ok {
			fn(v)
		}
		return ok
	}
	//res.forNextK = func(fn func(T)) {
	//	for elem := range ch {
	//		fn(elem)
	//	}
	//}
	res.trySplit = func() (s Spliterator[T], b bool) {
		return s, b
	}
	return res
}

// Iterator is a generic iterator on a slice that lazily evaluates the next element in the slice.
// This is used to lazily evaluate a slice's next value, allowing several applications of functional
// methods on a single list while only incurring a O(1) memory overhead.
type Iterator[T any] func() (T, bool)

// Iter consumes a generic slice and generates a forward-advancing Iterator
//
// Iter is passed a copy of the slice. This does not copy the contents of the slice, but the size of
// the slice is fixed. Therefore, modifications of element the internal slice will affect the Iterator
func Iter[T any, A ~[]T](slice A) Iterator[T] {
	index := 0
	return func() (t T, b bool) {
		if index >= len(slice) {
			return t, b // b is false here
		}
		index++
		return slice[index-1], true
	}
}

// ReverseIter consumes a generic slice and generates a reverse-advancing Iterator
func ReverseIter[T any, A ~[]T](slice A) Iterator[T] {
	index := len(slice) - 1
	return func() (t T, b bool) {
		if index < 0 || index >= len(slice) {
			return t, b // b is false here
		}
		index--
		return slice[index+1], true
	}
}

// Slice converts a generic Iterator to a slice of the appropriate type
func Slice[T any](iter Iterator[T]) []T {
	res := make([]T, 0)
	for val, ok := iter(); ok; val, ok = iter() {
		res = append(res, val)
	}
	return res
}

// Next is an alias for advancing the Iterator
func Next[T any](iter Iterator[T]) (T, bool) {
	return iter()
}


================================================
FILE: spliterator_test.go
================================================
package functools

import (
	"reflect"
	"testing"
)

type iterTestCase[T any] struct {
	name  string
	slice []T
}

func TestIter(t *testing.T) {
	t.Run("integer", func(t *testing.T) {
		cases := []iterTestCase[int]{
			{
				"integer1",
				[]int{1, 2, 3},
			},
			{
				"integer2",
				[]int{100, 0, -1, -1000},
			},
			{
				"empty slice",
				[]int{},
			},
		}

		for _, c := range cases {
			iter := Iter(c.slice)
			for i, _ := range c.slice {
				val, ok := Next(iter)
				if !ok {
					t.Errorf("got %v, want %v", nil, c.slice[i])
					continue
				}
				if val != c.slice[i] {
					t.Errorf("got %v, want %v", val, c.slice[i])
				}
			}

			// Make sure iter is empty
			val, ok := Next(iter)
			if ok {
				t.Errorf("got %v, want %v", val, nil)
			}
		}
	})

	t.Run("slice of string slices", func(t *testing.T) {
		cases := []iterTestCase[[]string]{
			{
				"empty string slice of slices",
				[][]string{},
			},
			{
				"string1",
				[][]string{{"asdf", "rakeeb", "gopher"}, {"kevin", "trevor"}},
			},
		}

		for _, c := range cases {
			iter := Iter(c.slice)

			for i, _ := range c.slice {
				val, ok := Next(iter)
				if !ok {
					t.Errorf("got %v, want %v", nil, c.slice[i])
				}
				if !reflect.DeepEqual(val, c.slice[i]) {
					t.Errorf("got %v, want %v", val, c.slice[i])
				}
			}

			// Make sure iter is empty
			val, ok := Next(iter)
			if ok {
				t.Errorf("got %v, want %v", val, nil)
			}
		}
	})
}

func TestReverseIter(t *testing.T) {
	t.Run("integer", func(t *testing.T) {
		cases := []iterTestCase[int]{
			{
				"integer1",
				[]int{1, 2, 3},
			},
			{
				"integer2",
				[]int{100, 0, -1, -1000},
			},
			{
				"empty slice",
				[]int{},
			},
		}

		for _, c := range cases {
			iter := ReverseIter(c.slice)

			for i := len(c.slice) - 1; i >= 0; i-- {
				val, ok := Next(iter)
				if !ok {
					t.Errorf("got %v, want %v", nil, c.slice[i])
				}
				if val != c.slice[i] {
					t.Errorf("got %v, want %v", val, c.slice[i])
				}
			}

			// Make sure iter is empty
			val, ok := Next(iter)
			if ok {
				t.Errorf("got %v, want %v", val, nil)
			}
		}
	})

	t.Run("slice of string slices", func(t *testing.T) {
		cases := []iterTestCase[[]string]{
			{
				"empty string slice of slices",
				[][]string{},
			},
			{
				"string1",
				[][]string{{"asdf", "rakeeb", "gopher"}, {"kevin", "trevor"}},
			},
		}

		for _, c := range cases {
			iter := ReverseIter(c.slice)

			for i := len(c.slice) - 1; i >= 0; i-- {
				val, ok := Next(iter)
				if !ok {
					t.Errorf("got %v, want %v", nil, c.slice[i])
				}
				if !reflect.DeepEqual(val, c.slice[i]) {
					t.Errorf("got %v, want %v", val, c.slice[i])
				}
			}

			// Make sure iter is empty
			val, ok := Next(iter)
			if ok {
				t.Errorf("got %v, want %v", val, nil)
			}
		}
	})
}

func reverse[T any](slice []T) {
	start := 0
	last := len(slice) - 1
	for start < last {
		slice[start], slice[last] = slice[last], slice[start]
		start++
		last--
	}
}

func TestSlice(t *testing.T) {
	t.Run("integer", func(t *testing.T) {
		cases := []iterTestCase[int]{
			{
				"integer1",
				[]int{1, 2, 3},
			},
			{
				"integer2",
				[]int{100, 0, -1, -1000},
			},
			{
				"empty slice",
				[]int{},
			},
		}

		for _, c := range cases {
			slice := Slice(Iter(c.slice))

			if !reflect.DeepEqual(slice, c.slice) {
				t.Errorf("got %v, want %v", slice, c.slice)
			}
		}

		for _, c := range cases {
			slice := Slice(ReverseIter(c.slice))
			reverse(slice)

			if !reflect.DeepEqual(slice, c.slice) {
				t.Errorf("got %v, want %v", slice, c.slice)
			}
		}
	})

	t.Run("slice of string slices", func(t *testing.T) {
		cases := []iterTestCase[[]string]{
			{
				"empty string slice of slices",
				[][]string{},
			},
			{
				"string1",
				[][]string{{"asdf", "rakeeb", "gopher"}, {"kevin", "trevor"}},
			},
		}

		for _, c := range cases {
			slice := Slice(Iter(c.slice))

			if !reflect.DeepEqual(slice, c.slice) {
				t.Errorf("got %v, want %v", slice, c.slice)
			}
		}

		for _, c := range cases {
			slice := Slice(ReverseIter(c.slice))
			reverse(slice)

			if !reflect.DeepEqual(slice, c.slice) {
				t.Errorf("got %v, want %v", slice, c.slice)
			}
		}
	})
}


================================================
FILE: stream.go
================================================
package functools

const (
	SIZED = 1 << iota
	SORTED
	DISTINCT
	ORDERED
	UNORDERED
)

type StreamStage[T any] interface {
	spliterator() Spliterator[T]

	isStateful() bool
	getParallelism() int
	characteristics() uint

	opEvalParallelLazy(int)
}

// StatelessOp struct embedding
type InheritUpstream[T any] struct {
	upstream *StreamStage[T]
}

func (s InheritUpstream[T]) getParallelism() int {
	return (*s.upstream).getParallelism()
}

func (s InheritUpstream[T]) characteristics() uint {
	return (*s.upstream).characteristics()
}

func (s InheritUpstream[T]) opEvalParallelLazy(n int) {
	(*s.upstream).opEvalParallelLazy(n)
}

type StatelessOp struct{}

func (s StatelessOp) isStateful() bool {
	return false
}

// SourceStage definition
type SourceStage[T any] struct {
	StatelessOp
	src         Spliterator[T]
	parallelism int
}

func Stream[T any](spliterator Spliterator[T]) StreamStage[T] {
	return SourceStage[T]{src: spliterator}
}

func ParallelStream[T any](spliterator Spliterator[T], parallelism int) StreamStage[T] {
	return SourceStage[T]{src: spliterator, parallelism: parallelism}
}

func (s SourceStage[T]) spliterator() Spliterator[T] {
	return s.src
}

func (s SourceStage[T]) getParallelism() int {
	return s.parallelism
}

func (s SourceStage[T]) characteristics() uint {
	return s.src.characteristics
}

func (s SourceStage[T]) opEvalParallelLazy(n int) {

}

// All this stuff should probably go into a separate file

// Helpers
func UpstreamToBuffer[T any](src StreamStage[T]) []T {
	slice := make([]T, 0)
	src.spliterator().forEachRemaining(func(e T) {
		slice = append(slice, e)
	})
	return slice
}

// Map
type MapOp[TIn any, TOut any] struct {
	StatelessOp
	InheritUpstream[TIn]
	mapper func(TIn) TOut
}

func Map[TIn any, TOut any](mapper func(TIn) TOut, upstream StreamStage[TIn]) StreamStage[TOut] {
	return MapOp[TIn, TOut]{
		StatelessOp{},
		InheritUpstream[TIn]{upstream: &upstream},
		mapper,
	}
}

func mapSpliterator[T any, O any](mapper func(T) O, src Spliterator[T]) (res Spliterator[O]) {
	res.tryAdvance = func(fn func(O)) bool {
		wrapper_fn := func(e T) {
			v := mapper(e)
			fn(v)
		}
		return src.tryAdvance(wrapper_fn)
	}
	res.forEachRemaining = func(fn func(O)) {
		wrapper_fn := func(e T) {
			v := mapper(e)
			fn(v)
		}
		src.forEachRemaining(wrapper_fn)
	}
	// Recursive split!!!
	res.trySplit = func() (Spliterator[O], bool) {
		r, b := src.trySplit()
		if !b {
			return Spliterator[O]{}, false
		} else {
			return mapSpliterator[T, O](mapper, r), true
		}
	}
	return res
}

func (m MapOp[TIn, TOut]) spliterator() (res Spliterator[TOut]) {
	s := (*m.InheritUpstream.upstream).spliterator()
	return mapSpliterator[TIn, TOut](m.mapper, s)
}

// SortOp
type SortOp[T any] struct {
	InheritUpstream[T]
	cmp func(T, T) bool
}

func Sort[T any](cmp func(T, T) bool, upstream StreamStage[T]) StreamStage[T] {
	return SortOp[T]{
		InheritUpstream[T]{upstream: &upstream},
		cmp,
	}
}

func quicksort[T any](cmp func(T, T) bool, slice []T) {
	for i, _ := range slice {
		min_so_far := slice[i]
		min_ind := i
		for j := i + 1; j < len(slice); j++ {
			if cmp(slice[j], min_so_far) {
				min_so_far = slice[j]
				min_ind = j
			}
		}
		tmp := slice[i]
		slice[i] = slice[min_ind]
		slice[min_ind] = tmp
	}
}

func (m SortOp[T]) spliteratorRec(src Spliterator[T]) (res Spliterator[T]) {
	done := false
	buffer := make([]T, 0, 2)
	index := 0
	res.tryAdvance = func(fn func(T)) bool {
		if !done {
			src.forEachRemaining(func(e T) {
				buffer = append(buffer, e)
			})
			quicksort(m.cmp, buffer)
			done = true
		}
		if index >= len(buffer) {
			return false
		}
		fn(buffer[index])
		index++
		return true
	}
	res.forEachRemaining = func(fn func(T)) {
		if !done {
			src.forEachRemaining(func(e T) {
				buffer = append(buffer, e)
			})
			quicksort(m.cmp, buffer)
			done = true
		}
		for _, x := range buffer {
			fn(x)
		}
	}
	res.trySplit = func() (Spliterator[T], bool) {
		r, b := src.trySplit()
		if !b {
			return r, b
		}
		return m.spliteratorRec(r), b
	}
	return res
}

func (m SortOp[T]) spliterator() (res Spliterator[T]) {
	s := (*m.upstream).spliterator()
	return m.spliteratorRec(s)
}

func (s SortOp[T]) isStateful() bool {
	return true
}

func (s SortOp[T]) characteristics() uint {
	return (*s.upstream).characteristics() | SIZED | SORTED | ORDERED
}

func (s SortOp[T]) opEvalParallelLazy(n int) {
	(*s.upstream).opEvalParallelLazy(n)
}


================================================
FILE: stream_test.go
================================================
package functools

import (
	"testing"
)

func isPrime(n int) bool {
	if n <= 1 {
		return false
	}
	for i := 2; i < n; i++ {
		if n%i == 0 {
			return false
		}
	}
	return true
}

func TestAny(t *testing.T) {
	slice := make([]int, 10000000)
	for i, _ := range slice {
		slice[i] = i
	}
	iter := ParallelStream(SliceIter(slice), 5)
	// s2 := Sort(func(x int, y int) bool { return x < y }, s1)

	print(Any(isPrime, iter))
}

func BenchmarkMap(b *testing.B) {
	slice := make([]int, 10000000)
	for i, _ := range slice {
		slice[i] = i
	}
	iter := ParallelStream(SliceIter(slice), 100)
	s1 := Map(func(e int) int { return e * -1 }, iter)
	// s2 := Sort(func(x int, y int) bool { return x < y }, s1)
	Sum(s1)
}

func BenchmarkMapSeq(b *testing.B) {
	slice := make([]int, 10000000)
	for i, _ := range slice {
		slice[i] = i
	}
	iter := Stream(SliceIter(slice))
	s1 := Map(func(e int) int { return e * -1 }, iter)
	// s2 := Sort(func(x int, y int) bool { return x < y }, s1)
	Sum(s1)
}

func BenchmarkMapFor(b *testing.B) {
	slice := make([]int, 10000000)
	for i, _ := range slice {
		slice[i] = i
	}
	for i, _ := range slice {
		slice[i] *= -1
	}
	res := 0
	for _, v := range slice {
		res += v
	}
}


================================================
FILE: terminals.go
================================================
// Terminal ops

package functools

import "sync"

// Helpers
func buildNSplits[T any](n uint32, src Spliterator[T]) []Spliterator[T] {
	if n <= 1 {
		return []Spliterator[T]{src}
	}
	// Round N down to a power of 2
	var mask uint32 = 1 << 31
	for n&mask == 0 {
		mask >>= 1
	}
	// Alloc results slice
	res := make([]Spliterator[T], 0, mask)

	// In-order traversal of split tree
	var buildNSplitsRec func(uint32, Spliterator[T])
	buildNSplitsRec = func(n uint32, src Spliterator[T]) {
		if n == 1 {
			res = append(res, src)
		} else {
			split, ok := src.trySplit()
			buildNSplitsRec(n/2, src)
			if ok {
				buildNSplitsRec(n/2, split)
			}
		}
	}
	buildNSplitsRec(mask, src)

	return res
}

// ForEach
// TODO: figure out if you can abstract most of this. opEvalParallelLazy, buildNSplits, etc. always happen so we might be able to make ForEachOp implement a TerminalOp interface and abstract these
func ForEach[T any](fn func(T), stream StreamStage[T]) {
	n := stream.getParallelism()
	if n <= 1 {
		stream.spliterator().forEachRemaining(fn)
	} else {
		// Evaluate up to last stateful op
		stream.opEvalParallelLazy(n)

		// Get n splits
		splits := buildNSplits(uint32(n), stream.spliterator())
		n = len(splits)

		// Perform go-routines
		var wg sync.WaitGroup

		for i := 0; i < n; i++ {
			wg.Add(1)

			go func(i int) {
				defer wg.Done()
				// TODO: abstract into evalSequential so you don't need to rewrite this everytime
				splits[i].forEachRemaining(fn)
			}(i)
		}
		wg.Wait()
	}
}

// Summable encompasses all builtin types with the + operator defined on them or any type aliases
// of these types
type Summable interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64 |
		~string
}

// Sum
func Sum[T Summable](stream StreamStage[T]) T {
	n := stream.getParallelism()
	if n <= 1 {
		var res T
		stream.spliterator().forEachRemaining(func(e T) {
			res += e
		})
		return res
	} else {
		// Evaluate up to last stateful op
		stream.opEvalParallelLazy(n)

		// Get n splits
		splits := buildNSplits(uint32(n), stream.spliterator())
		n = len(splits)

		var wg sync.WaitGroup
		var mutex sync.Mutex
		var res T

		for i := 0; i < n; i++ {
			wg.Add(1)

			go func(i int) {
				defer wg.Done()

				var tmp T
				splits[i].forEachRemaining(func(e T) {
					tmp += e
				})
				mutex.Lock()
				res += tmp
				mutex.Unlock()
			}(i)
		}
		wg.Wait()

		return res
	}
}

// Any
func Any[T any](pred func(T) bool, stream StreamStage[T]) bool {
	n := stream.getParallelism()
	res := false

	if n <= 1 {
		wrapPred := func(e T) {
			if pred(e) {
				res = true
			}
		}
		s := stream.spliterator()
		for ok := s.tryAdvance(wrapPred); ok && !res; ok = s.tryAdvance(wrapPred) {
		}
		return res
	} else {
		// Evaluate up to last stateful op
		stream.opEvalParallelLazy(n)

		// Get n splits
		splits := buildNSplits(uint32(n), stream.spliterator())
		n = len(splits)

		var wg sync.WaitGroup
		var mutex sync.Mutex

		for i := 0; i < n; i++ {
			wg.Add(1)

			go func(i int) {
				defer wg.Done()

				tmp := false
				wrapPred := func(e T) {
					if pred(e) {
						tmp = true
					}
				}

				for ok := splits[i].tryAdvance(wrapPred); ok && !tmp && !res; ok = splits[i].tryAdvance(wrapPred) {
				}

				if tmp {
					mutex.Lock()
					res = tmp
					mutex.Unlock()
				}
			}(i)
		}
		wg.Wait()

		return res
	}
}

// CollectSlice

// Reduce

//// All consumes a slice of a generic type and applies the predicate to each element in the slice.
//// All return true if and only if no element returns false after applying the predicate.
////
//// Vacuously, empty slices return true regardless of the predicate.
////
//// predicate should be error-safe. It should handle any errors internally and return only a bool.
//// If other arguments are required by predicate, predicate should be made a closure with the appropriate
//// variables referenced.
//func All[T any, A ~[]T](slice A, predicate func(T) bool) bool {
//	for _, v := range slice {
//		if !predicate(v) {
//			return false
//		}
//	}
//	return true
//}
//
//// Any consumes a slice of a generic type and applies the predicate to each element in the slice.
//// If any element returns true after applying the predicate, Any returns true.
////
//// Vacuously, empty slices return false regardless of the predicate.
////
//// predicate should be error-safe. It should handle any errors internally and return only a bool.
//// If other arguments are required by predicate, predicate should be made a closure with the appropriate
//// variables referenced.
//func Any[T any, A ~[]T](slice A, predicate func(T) bool) bool {
//	for _, v := range slice {
//		if predicate(v) {
//			return true
//		}
//	}
//	return false
//}
//
//
//// Sum consumes a slice of a Summable type and sums the elements
////
//// Vacuously, empty slices return the zero value of the provided Summable
//func Sum[S Summable, A ~[]S](slice A) S {
//	var res S
//	for _, v := range slice {
//		res += v
//	}
//	return res
//}


================================================
FILE: terminals_test.go
================================================
package functools

// All tests
//func TestGeqAll(t *testing.T) {
//	slice := []int{100, 25, 20, 31, 30}
//	if All(slice, func(val int) bool { return val >= 21 }) {
//		t.Errorf("TestGeqAll with %v was incorrect, got: %v, expected: %v", slice, true, false)
//	}
//	slice[2] = 21
//	if !All(slice, func(val int) bool { return val >= 21 }) {
//		t.Errorf("TestGeqAll with %v was incorrect, got: %v, expected: %v", slice, false, true)
//	}
//}
//
//// Any tests
//func TestLtAny(t *testing.T) {
//	slice := []int{20, 31, 22}
//
//	if !Any(slice, func(val int) bool { return val < 21 }) {
//		t.Errorf("TestLtAny with %v was incorrect, got: %v, expected: %v", slice, false, true)
//	}
//	slice[0] = 21
//	if Any(slice, func(val int) bool { return val < 21 }) {
//		t.Errorf("TestLtAny with %v was incorrect, got: %v, expected: %v", slice, true, false)
//	}
//}
//
//func TestIntSum(t *testing.T) {
//	slice := []int{1, 2, 3}
//	res := Sum(slice)
//	expect := 6
//
//	if res != expect {
//		t.Errorf("TestIntSum was incorrect, got: %d, expected: %d", res, expect)
//	}
//}
//
//func TestUintptrSum(t *testing.T) {
//	slice := []uintptr{1, 2, 3}
//	res := Sum(slice)
//	expect := uintptr(6)
//
//	if res != expect {
//		t.Errorf("TestUintptrSum was incorrect, got: %d, expected: %d", res, expect)
//	}
//}
//
//func TestFloatSum(t *testing.T) {
//	slice := []float64{0.668, 0.666, 0.666}
//	res := Sum(slice)
//	expect := 2.0
//
//	if res != expect {
//		t.Errorf("TestFloatSum was incorrect, got: %f, expected: %f", res, expect)
//	}
//}
//
//func TestStringSum(t *testing.T) {
//	slice := []string{"a", "b", "c"}
//	res := Sum(slice)
//	expect := "abc"
//
//	if res != expect {
//		t.Errorf("TestStringSum was incorrect, got: %s, expected: %s", res, expect)
//	}
//}
//
//func TestByteSum(t *testing.T) {
//	slice := []byte{1, 2, 3}
//	res := Sum(slice)
//	expect := byte(6)
//
//	if res != expect {
//		t.Errorf("TestByteSum was incorrect, got: %d, expected: %d", res, expect)
//	}
//}
Download .txt
gitextract_s452whe8/

├── .github/
│   └── workflows/
│       └── go.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── doc.go
├── examples/
│   ├── error-handling/
│   │   └── error_handling.go
│   └── portfolio/
│       └── portfolio.go
├── functionals.go
├── functionals_test.go
├── go.mod
├── go.sum
├── spined_buffer.go
├── spined_buffer_test.go
├── spliterator.go
├── spliterator_test.go
├── stream.go
├── stream_test.go
├── terminals.go
└── terminals_test.go
Download .txt
SYMBOL INDEX (85 symbols across 9 files)

FILE: examples/error-handling/error_handling.go
  type fraction (line 15) | type fraction struct
  function main (line 20) | func main() {

FILE: examples/portfolio/portfolio.go
  type user (line 10) | type user struct
  type holding (line 16) | type holding struct
  type portfolio (line 23) | type portfolio struct
  function main (line 38) | func main() {

FILE: spined_buffer.go
  constant FirstBuffPower (line 7) | FirstBuffPower int = 4
  constant MinSpineSize (line 8) | MinSpineSize int = 2
  constant SpineExtendCount (line 9) | SpineExtendCount int = 1
  type AbstractBuffer (line 11) | type AbstractBuffer interface
  type SpinedBuffer (line 22) | type SpinedBuffer struct
  method inflateSpine (line 38) | func (s *SpinedBuffer[T]) inflateSpine() {
  function CreateSpinedBuffer (line 58) | func CreateSpinedBuffer[T any]() (s SpinedBuffer[T]) {
  method Len (line 71) | func (s SpinedBuffer[T]) Len() int {
  method Capacity (line 75) | func (s SpinedBuffer[T]) Capacity() int {
  method Push (line 79) | func (s *SpinedBuffer[T]) Push(elem T) {
  method Flatten (line 119) | func (s SpinedBuffer[T]) Flatten() []T {
  method At (line 137) | func (s SpinedBuffer[T]) At(index int) (res T) {
  method PrintStats (line 162) | func (s SpinedBuffer[T]) PrintStats() {
  function upperBoundGuaranteed (line 169) | func upperBoundGuaranteed(val int, arr []int) int {

FILE: spined_buffer_test.go
  function TestSpinedBuffer_Push (line 9) | func TestSpinedBuffer_Push(t *testing.T) {
  function TestSpinedBuffer_At (line 24) | func TestSpinedBuffer_At(t *testing.T) {
  constant BENCHMARK_SIZE (line 42) | BENCHMARK_SIZE int = 10000
  function BenchmarkCreateSpinedBuffer_Push (line 44) | func BenchmarkCreateSpinedBuffer_Push(b *testing.B) {
  function BenchmarkSlicePush (line 52) | func BenchmarkSlicePush(b *testing.B) {

FILE: spliterator.go
  type Spliterator (line 7) | type Spliterator struct
  function EmptyIter (line 14) | func EmptyIter[T any]() (res Spliterator[T]) {
  function sliceIterRec (line 25) | func sliceIterRec[T any, A ~[]T](slice A, lo int, hi int) (res Spliterat...
  function SliceIter (line 53) | func SliceIter[T any, A ~[]T](slice A) (res Spliterator[T]) {
  function RuleIter (line 57) | func RuleIter[T any, A ~func() (T, bool)](rule A) (res Spliterator[T]) {
  function sign (line 76) | func sign[T constraints.Integer](x T) int8 {
  function RangeIter (line 86) | func RangeIter[T constraints.Integer](start, stop T, step T) (res Splite...
  function ChanIter (line 117) | func ChanIter[T any, C ~chan T](ch C) (res Spliterator[T]) {
  type Iterator (line 139) | type Iterator
  function Iter (line 145) | func Iter[T any, A ~[]T](slice A) Iterator[T] {
  function ReverseIter (line 157) | func ReverseIter[T any, A ~[]T](slice A) Iterator[T] {
  function Slice (line 169) | func Slice[T any](iter Iterator[T]) []T {
  function Next (line 178) | func Next[T any](iter Iterator[T]) (T, bool) {

FILE: spliterator_test.go
  type iterTestCase (line 8) | type iterTestCase struct
  function TestIter (line 13) | func TestIter(t *testing.T) {
  function TestReverseIter (line 85) | func TestReverseIter(t *testing.T) {
  function reverse (line 157) | func reverse[T any](slice []T) {
  function TestSlice (line 167) | func TestSlice(t *testing.T) {

FILE: stream.go
  constant SIZED (line 4) | SIZED = 1 << iota
  constant SORTED (line 5) | SORTED
  constant DISTINCT (line 6) | DISTINCT
  constant ORDERED (line 7) | ORDERED
  constant UNORDERED (line 8) | UNORDERED
  type StreamStage (line 11) | type StreamStage interface
  type InheritUpstream (line 22) | type InheritUpstream struct
  method getParallelism (line 26) | func (s InheritUpstream[T]) getParallelism() int {
  method characteristics (line 30) | func (s InheritUpstream[T]) characteristics() uint {
  method opEvalParallelLazy (line 34) | func (s InheritUpstream[T]) opEvalParallelLazy(n int) {
  type StatelessOp (line 38) | type StatelessOp struct
    method isStateful (line 40) | func (s StatelessOp) isStateful() bool {
  type SourceStage (line 45) | type SourceStage struct
  function Stream (line 51) | func Stream[T any](spliterator Spliterator[T]) StreamStage[T] {
  function ParallelStream (line 55) | func ParallelStream[T any](spliterator Spliterator[T], parallelism int) ...
  method spliterator (line 59) | func (s SourceStage[T]) spliterator() Spliterator[T] {
  method getParallelism (line 63) | func (s SourceStage[T]) getParallelism() int {
  method characteristics (line 67) | func (s SourceStage[T]) characteristics() uint {
  method opEvalParallelLazy (line 71) | func (s SourceStage[T]) opEvalParallelLazy(n int) {
  function UpstreamToBuffer (line 78) | func UpstreamToBuffer[T any](src StreamStage[T]) []T {
  type MapOp (line 87) | type MapOp struct
  function Map (line 93) | func Map[TIn any, TOut any](mapper func(TIn) TOut, upstream StreamStage[...
  function mapSpliterator (line 101) | func mapSpliterator[T any, O any](mapper func(T) O, src Spliterator[T]) ...
  method spliterator (line 128) | func (m MapOp[TIn, TOut]) spliterator() (res Spliterator[TOut]) {
  type SortOp (line 134) | type SortOp struct
  function Sort (line 139) | func Sort[T any](cmp func(T, T) bool, upstream StreamStage[T]) StreamSta...
  function quicksort (line 146) | func quicksort[T any](cmp func(T, T) bool, slice []T) {
  method spliteratorRec (line 162) | func (m SortOp[T]) spliteratorRec(src Spliterator[T]) (res Spliterator[T...
  method spliterator (line 203) | func (m SortOp[T]) spliterator() (res Spliterator[T]) {
  method isStateful (line 208) | func (s SortOp[T]) isStateful() bool {
  method characteristics (line 212) | func (s SortOp[T]) characteristics() uint {
  method opEvalParallelLazy (line 216) | func (s SortOp[T]) opEvalParallelLazy(n int) {

FILE: stream_test.go
  function isPrime (line 7) | func isPrime(n int) bool {
  function TestAny (line 19) | func TestAny(t *testing.T) {
  function BenchmarkMap (line 30) | func BenchmarkMap(b *testing.B) {
  function BenchmarkMapSeq (line 41) | func BenchmarkMapSeq(b *testing.B) {
  function BenchmarkMapFor (line 52) | func BenchmarkMapFor(b *testing.B) {

FILE: terminals.go
  function buildNSplits (line 8) | func buildNSplits[T any](n uint32, src Spliterator[T]) []Spliterator[T] {
  function ForEach (line 40) | func ForEach[T any](fn func(T), stream StreamStage[T]) {
  type Summable (line 70) | type Summable interface
  function Sum (line 78) | func Sum[T Summable](stream StreamStage[T]) T {
  function Any (line 120) | func Any[T any](pred func(T) bool, stream StreamStage[T]) bool {
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (49K chars).
[
  {
    "path": ".github/workflows/go.yml",
    "chars": 391,
    "preview": "name: Go\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n\n  build:\n    runs-on: ub"
  },
  {
    "path": ".gitignore",
    "chars": 305,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Ou"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 816,
    "preview": "# functools Contributing guide\n\nThank you for considering contributing to functools! To get an overview of the project, "
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2021 Rakeeb Hossain\n\nPermission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "README.md",
    "chars": 1509,
    "preview": "# functools\n\n[![Go](https://github.com/rakeeb-hossain/functools/actions/workflows/go.yml/badge.svg)](https://github.com/"
  },
  {
    "path": "doc.go",
    "chars": 333,
    "preview": "// Package functools provides type-safe functional helpers using Go 1.18 generics.\n//\n// functools is intended to allow "
  },
  {
    "path": "examples/error-handling/error_handling.go",
    "chars": 1469,
    "preview": "// This showcases how we can handle errors using functools helpers, despite there being no explicit error\n// return type"
  },
  {
    "path": "examples/portfolio/portfolio.go",
    "chars": 1839,
    "preview": "// This showcases several applications of functools helpers to a store of user objects and their associated\n// portfolio"
  },
  {
    "path": "functionals.go",
    "chars": 4588,
    "preview": "// Contains classic generic functional methods\n\npackage functools\n\n//\n//// Map consumes a slice of a generic type and re"
  },
  {
    "path": "functionals_test.go",
    "chars": 4619,
    "preview": "package functools\n\n//type user struct {\n//\tage int\n//}\n//\n//// Filter tests\n//func TestGeqFilter(t *testing.T) {\n//\tslic"
  },
  {
    "path": "go.mod",
    "chars": 125,
    "preview": "module github.com/rakeeb-hossain/functools\n\ngo 1.18\n\nrequire golang.org/x/exp v0.0.0-20220428152302-39d4317da171 // indi"
  },
  {
    "path": "go.sum",
    "chars": 207,
    "preview": "golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=\ngolang.org/x/exp v0."
  },
  {
    "path": "spined_buffer.go",
    "chars": 4543,
    "preview": "package functools\n\nimport (\n\t\"fmt\"\n)\n\nconst FirstBuffPower int = 4\nconst MinSpineSize int = 2 // must be >= 1\nconst Spin"
  },
  {
    "path": "spined_buffer_test.go",
    "chars": 1180,
    "preview": "package functools\n\nimport (\n\t\"math/rand\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestSpinedBuffer_Push(t *testing.T) {\n\tNUM_TRIALS"
  },
  {
    "path": "spliterator.go",
    "chars": 4020,
    "preview": "package functools\n\nimport (\n\t\"golang.org/x/exp/constraints\"\n)\n\ntype Spliterator[T any] struct {\n\ttryAdvance       func(f"
  },
  {
    "path": "spliterator_test.go",
    "chars": 4182,
    "preview": "package functools\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype iterTestCase[T any] struct {\n\tname  string\n\tslice []T\n}\n\nfunc "
  },
  {
    "path": "stream.go",
    "chars": 4410,
    "preview": "package functools\n\nconst (\n\tSIZED = 1 << iota\n\tSORTED\n\tDISTINCT\n\tORDERED\n\tUNORDERED\n)\n\ntype StreamStage[T any] interface"
  },
  {
    "path": "stream_test.go",
    "chars": 1194,
    "preview": "package functools\n\nimport (\n\t\"testing\"\n)\n\nfunc isPrime(n int) bool {\n\tif n <= 1 {\n\t\treturn false\n\t}\n\tfor i := 2; i < n; "
  },
  {
    "path": "terminals.go",
    "chars": 5050,
    "preview": "// Terminal ops\n\npackage functools\n\nimport \"sync\"\n\n// Helpers\nfunc buildNSplits[T any](n uint32, src Spliterator[T]) []S"
  },
  {
    "path": "terminals_test.go",
    "chars": 1983,
    "preview": "package functools\n\n// All tests\n//func TestGeqAll(t *testing.T) {\n//\tslice := []int{100, 25, 20, 31, 30}\n//\tif All(slice"
  }
]

About this extraction

This page contains the full source code of the rakeeb-hossain/functools GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (42.8 KB), approximately 13.5k tokens, and a symbol index with 85 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!