Bootstrap Go library with EUPL-1.2, goreleaser, mkdocs, and sorting functions
Co-authored-by: Snider <631881+Snider@users.noreply.github.com>
This commit is contained in:
parent
fc74550e7c
commit
195924fb9e
17 changed files with 1548 additions and 1 deletions
30
.github/workflows/docs.yml
vendored
Normal file
30
.github/workflows/docs.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: Deploy Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
pip install mkdocs-material
|
||||||
|
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
run: |
|
||||||
|
mkdocs gh-deploy --force
|
||||||
32
.github/workflows/release.yml
vendored
Normal file
32
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: stable
|
||||||
|
|
||||||
|
- name: Run GoReleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser
|
||||||
|
version: latest
|
||||||
|
args: release --clean
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
30
.github/workflows/test.yml
vendored
Normal file
30
.github/workflows/test.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: stable
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
|
- name: Upload coverage
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
files: ./coverage.txt
|
||||||
|
fail_ci_if_error: false
|
||||||
58
.gitignore
vendored
Normal file
58
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
# 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/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# MkDocs
|
||||||
|
site/
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
71
.goreleaser.yml
Normal file
71
.goreleaser.yml
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||||
|
# Make sure to check the documentation at https://goreleaser.com
|
||||||
|
|
||||||
|
# The lines below are called `modelines`. See `:help modeline`
|
||||||
|
# Feel free to remove those if you don't want/need to use them.
|
||||||
|
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||||
|
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
# You may remove this if you don't use go modules.
|
||||||
|
- go mod tidy
|
||||||
|
# you may remove this if you don't need go generate
|
||||||
|
- go generate ./...
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- windows
|
||||||
|
- darwin
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
# Optional: Exclude specific combinations
|
||||||
|
ignore:
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm64
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- format: tar.gz
|
||||||
|
# this name template makes the OS and Arch compatible with the results of `uname`.
|
||||||
|
name_template: >-
|
||||||
|
{{ .ProjectName }}_
|
||||||
|
{{- title .Os }}_
|
||||||
|
{{- if eq .Arch "amd64" }}x86_64
|
||||||
|
{{- else if eq .Arch "386" }}i386
|
||||||
|
{{- else }}{{ .Arch }}{{ end }}
|
||||||
|
{{- if .Arm }}v{{ .Arm }}{{ end }}
|
||||||
|
# use zip for windows archives
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- "^docs:"
|
||||||
|
- "^test:"
|
||||||
|
- "^ci:"
|
||||||
|
- "^chore:"
|
||||||
|
- "^build:"
|
||||||
|
- Merge pull request
|
||||||
|
- Merge branch
|
||||||
|
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
version_template: "{{ incpatch .Version }}-next"
|
||||||
|
|
||||||
|
release:
|
||||||
|
github:
|
||||||
|
owner: Snider
|
||||||
|
name: Poindexter
|
||||||
|
draft: false
|
||||||
|
prerelease: auto
|
||||||
190
LICENSE
Normal file
190
LICENSE
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||||
|
EUPL © the European Union 2007, 2016
|
||||||
|
|
||||||
|
This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined below) which is provided under the
|
||||||
|
terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such
|
||||||
|
use is covered by a right of the copyright holder of the Work).
|
||||||
|
The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following
|
||||||
|
notice immediately following the copyright notice for the Work:
|
||||||
|
Licensed under the EUPL
|
||||||
|
or has expressed by any other means his willingness to license under the EUPL.
|
||||||
|
|
||||||
|
1.Definitions
|
||||||
|
In this Licence, the following terms have the following meaning:
|
||||||
|
— ‘The Licence’:this Licence.
|
||||||
|
— ‘The Original Work’:the work or software distributed or communicated by the Licensor under this Licence, available
|
||||||
|
as Source Code and also as Executable Code as the case may be.
|
||||||
|
— ‘Derivative Works’:the works or software that could be created by the Licensee, based upon the Original Work or
|
||||||
|
modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work
|
||||||
|
required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in
|
||||||
|
the country mentioned in Article 15.
|
||||||
|
— ‘The Work’:the Original Work or its Derivative Works.
|
||||||
|
— ‘The Source Code’:the human-readable form of the Work which is the most convenient for people to study and
|
||||||
|
modify.
|
||||||
|
— ‘The Executable Code’:any code which has generally been compiled and which is meant to be interpreted by
|
||||||
|
a computer as a program.
|
||||||
|
— ‘The Licensor’:the natural or legal person that distributes or communicates the Work under the Licence.
|
||||||
|
— ‘Contributor(s)’:any natural or legal person who modifies the Work under the Licence, or otherwise contributes to
|
||||||
|
the creation of a Derivative Work.
|
||||||
|
— ‘The Licensee’ or ‘You’:any natural or legal person who makes any usage of the Work under the terms of the
|
||||||
|
Licence.
|
||||||
|
— ‘Distribution’ or ‘Communication’:any act of selling, giving, lending, renting, distributing, communicating,
|
||||||
|
transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential
|
||||||
|
functionalities at the disposal of any other natural or legal person.
|
||||||
|
|
||||||
|
2.Scope of the rights granted by the Licence
|
||||||
|
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for
|
||||||
|
the duration of copyright vested in the Original Work:
|
||||||
|
— use the Work in any circumstance and for all usage,
|
||||||
|
— reproduce the Work,
|
||||||
|
— modify the Work, and make Derivative Works based upon the Work,
|
||||||
|
— communicate to the public, including the right to make available or display the Work or copies thereof to the public
|
||||||
|
and perform publicly, as the case may be, the Work,
|
||||||
|
— distribute the Work or copies thereof,
|
||||||
|
— lend and rent the Work or copies thereof,
|
||||||
|
— sublicense rights in the Work or copies thereof.
|
||||||
|
Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the
|
||||||
|
applicable law permits so.
|
||||||
|
In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed
|
||||||
|
by law in order to make effective the licence of the economic rights here above listed.
|
||||||
|
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the
|
||||||
|
extent necessary to make use of the rights granted on the Work under this Licence.
|
||||||
|
|
||||||
|
3.Communication of the Source Code
|
||||||
|
The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as
|
||||||
|
Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with
|
||||||
|
each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to
|
||||||
|
the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to
|
||||||
|
distribute or communicate the Work.
|
||||||
|
|
||||||
|
4.Limitations on copyright
|
||||||
|
Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the
|
||||||
|
exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations
|
||||||
|
thereto.
|
||||||
|
|
||||||
|
5.Obligations of the Licensee
|
||||||
|
The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those
|
||||||
|
obligations are the following:
|
||||||
|
|
||||||
|
Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to
|
||||||
|
the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the
|
||||||
|
Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work
|
||||||
|
to carry prominent notices stating that the Work has been modified and the date of modification.
|
||||||
|
|
||||||
|
Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this
|
||||||
|
Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless
|
||||||
|
the Original Work is expressly distributed only under this version of the Licence — for example by communicating
|
||||||
|
‘EUPL v. 1.2 only’. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the
|
||||||
|
Work or Derivative Work that alter or restrict the terms of the Licence.
|
||||||
|
|
||||||
|
Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both
|
||||||
|
the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done
|
||||||
|
under the terms of this Compatible Licence. For the sake of this clause, ‘Compatible Licence’ refers to the licences listed
|
||||||
|
in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with
|
||||||
|
his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail.
|
||||||
|
|
||||||
|
Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide
|
||||||
|
a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available
|
||||||
|
for as long as the Licensee continues to distribute or communicate the Work.
|
||||||
|
Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names
|
||||||
|
of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the copyright notice.
|
||||||
|
|
||||||
|
6.Chain of Authorship
|
||||||
|
The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or
|
||||||
|
licensed to him/her and that he/she has the power and authority to grant the Licence.
|
||||||
|
Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or
|
||||||
|
licensed to him/her and that he/she has the power and authority to grant the Licence.
|
||||||
|
Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions
|
||||||
|
to the Work, under the terms of this Licence.
|
||||||
|
|
||||||
|
7.Disclaimer of Warranty
|
||||||
|
The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work
|
||||||
|
and may therefore contain defects or ‘bugs’ inherent to this type of development.
|
||||||
|
For the above reason, the Work is provided under the Licence on an ‘as is’ basis and without warranties of any kind
|
||||||
|
concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or
|
||||||
|
errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this
|
||||||
|
Licence.
|
||||||
|
This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work.
|
||||||
|
|
||||||
|
8.Disclaimer of Liability
|
||||||
|
Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be
|
||||||
|
liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the
|
||||||
|
Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss
|
||||||
|
of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However,
|
||||||
|
the Licensor will be liable under statutory product liability laws as far such laws apply to the Work.
|
||||||
|
|
||||||
|
9.Additional agreements
|
||||||
|
While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services
|
||||||
|
consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole
|
||||||
|
responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by
|
||||||
|
the fact You have accepted any warranty or additional liability.
|
||||||
|
|
||||||
|
10.Acceptance of the Licence
|
||||||
|
The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ placed under the bottom of a window
|
||||||
|
displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of
|
||||||
|
applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms
|
||||||
|
and conditions.
|
||||||
|
Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You
|
||||||
|
by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution
|
||||||
|
or Communication by You of the Work or copies thereof.
|
||||||
|
|
||||||
|
11.Information to the public
|
||||||
|
In case of any Distribution or Communication of the Work by means of electronic communication by You (for example,
|
||||||
|
by offering to download the Work from a remote location) the distribution channel or media (for example, a website)
|
||||||
|
must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence
|
||||||
|
and the way it may be accessible, concluded, stored and reproduced by the Licensee.
|
||||||
|
|
||||||
|
12.Termination of the Licence
|
||||||
|
The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms
|
||||||
|
of the Licence.
|
||||||
|
Such a termination will not terminate the licences of any person who has received the Work from the Licensee under
|
||||||
|
the Licence, provided such persons remain in full compliance with the Licence.
|
||||||
|
|
||||||
|
13.Miscellaneous
|
||||||
|
Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the
|
||||||
|
Work.
|
||||||
|
If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or
|
||||||
|
enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid
|
||||||
|
and enforceable.
|
||||||
|
The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of
|
||||||
|
the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence.
|
||||||
|
New versions of the Licence will be published with a unique version number.
|
||||||
|
All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take
|
||||||
|
advantage of the linguistic version of their choice.
|
||||||
|
|
||||||
|
14.Jurisdiction
|
||||||
|
Without prejudice to specific agreement between parties,
|
||||||
|
— any litigation resulting from the interpretation of this License, arising between the European Union institutions,
|
||||||
|
bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice
|
||||||
|
of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union,
|
||||||
|
— any litigation arising between other parties and resulting from the interpretation of this License, will be subject to
|
||||||
|
the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business.
|
||||||
|
|
||||||
|
15.Applicable Law
|
||||||
|
Without prejudice to specific agreement between parties,
|
||||||
|
— this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat,
|
||||||
|
resides or has his registered office,
|
||||||
|
— this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside
|
||||||
|
a European Union Member State.
|
||||||
|
|
||||||
|
|
||||||
|
Appendix
|
||||||
|
|
||||||
|
‘Compatible Licences’ according to Article 5 EUPL are:
|
||||||
|
— GNU General Public License (GPL) v. 2, v. 3
|
||||||
|
— GNU Affero General Public License (AGPL) v. 3
|
||||||
|
— Open Software License (OSL) v. 2.1, v. 3.0
|
||||||
|
— Eclipse Public License (EPL) v. 1.0
|
||||||
|
— CeCILL v. 2.0, v. 2.1
|
||||||
|
— Mozilla Public Licence (MPL) v. 2
|
||||||
|
— GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||||
|
— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software
|
||||||
|
— European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||||
|
— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+).
|
||||||
|
|
||||||
|
The European Commission may update this Appendix to later versions of the above licences without producing
|
||||||
|
a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the
|
||||||
|
covered Source Code from exclusive appropriation.
|
||||||
|
All other changes or additions to this Appendix require the production of a new EUPL version.
|
||||||
65
README.md
65
README.md
|
|
@ -1 +1,64 @@
|
||||||
# Poindexter
|
# Poindexter
|
||||||
|
|
||||||
|
A Go library package providing utility functions including sorting algorithms with custom comparators.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 🔢 **Sorting Utilities**: Sort integers, strings, and floats in ascending or descending order
|
||||||
|
- 🎯 **Custom Sorting**: Sort any type with custom comparison functions or key extractors
|
||||||
|
- 🔍 **Binary Search**: Fast search on sorted data
|
||||||
|
- 📦 **Generic Functions**: Type-safe operations using Go generics
|
||||||
|
- ✅ **Well-Tested**: Comprehensive test coverage
|
||||||
|
- 📖 **Documentation**: Full documentation available at GitHub Pages
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/Snider/Poindexter
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Snider/Poindexter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Basic sorting
|
||||||
|
numbers := []int{3, 1, 4, 1, 5, 9}
|
||||||
|
poindexter.SortInts(numbers)
|
||||||
|
fmt.Println(numbers) // [1 1 3 4 5 9]
|
||||||
|
|
||||||
|
// Custom sorting with key function
|
||||||
|
type Product struct {
|
||||||
|
Name string
|
||||||
|
Price float64
|
||||||
|
}
|
||||||
|
|
||||||
|
products := []Product{
|
||||||
|
{"Apple", 1.50},
|
||||||
|
{"Banana", 0.75},
|
||||||
|
{"Cherry", 3.00},
|
||||||
|
}
|
||||||
|
|
||||||
|
poindexter.SortByKey(products, func(p Product) float64 {
|
||||||
|
return p.Price
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Full documentation is available at [https://snider.github.io/Poindexter/](https://snider.github.io/Poindexter/)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the European Union Public Licence v1.2 (EUPL-1.2). See [LICENSE](LICENSE) for details.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||||
318
docs/api.md
Normal file
318
docs/api.md
Normal file
|
|
@ -0,0 +1,318 @@
|
||||||
|
# API Reference
|
||||||
|
|
||||||
|
Complete API documentation for the Poindexter library.
|
||||||
|
|
||||||
|
## Core Functions
|
||||||
|
|
||||||
|
### Version
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Version() string
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the current version of the library.
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `string`: The version string (e.g., "0.1.0")
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
version := poindexter.Version()
|
||||||
|
fmt.Println(version) // Output: 0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Hello
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Hello(name string) string
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns a greeting message.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (string): The name to greet. If empty, defaults to "World"
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `string`: A greeting message
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Greet the world
|
||||||
|
message := poindexter.Hello("")
|
||||||
|
fmt.Println(message) // Output: Hello, World!
|
||||||
|
|
||||||
|
// Greet a specific person
|
||||||
|
message = poindexter.Hello("Alice")
|
||||||
|
fmt.Println(message) // Output: Hello, Alice!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sorting Functions
|
||||||
|
|
||||||
|
### Basic Sorting
|
||||||
|
|
||||||
|
#### SortInts
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortInts(data []int)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice of integers in ascending order in place.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
numbers := []int{3, 1, 4, 1, 5, 9}
|
||||||
|
poindexter.SortInts(numbers)
|
||||||
|
fmt.Println(numbers) // Output: [1 1 3 4 5 9]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### SortIntsDescending
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortIntsDescending(data []int)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice of integers in descending order in place.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
numbers := []int{3, 1, 4, 1, 5, 9}
|
||||||
|
poindexter.SortIntsDescending(numbers)
|
||||||
|
fmt.Println(numbers) // Output: [9 5 4 3 1 1]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### SortStrings
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortStrings(data []string)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice of strings in ascending order in place.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
words := []string{"banana", "apple", "cherry"}
|
||||||
|
poindexter.SortStrings(words)
|
||||||
|
fmt.Println(words) // Output: [apple banana cherry]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### SortStringsDescending
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortStringsDescending(data []string)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice of strings in descending order in place.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### SortFloat64s
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortFloat64s(data []float64)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice of float64 values in ascending order in place.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### SortFloat64sDescending
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortFloat64sDescending(data []float64)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice of float64 values in descending order in place.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Advanced Sorting
|
||||||
|
|
||||||
|
#### SortBy
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortBy[T any](data []T, less func(i, j int) bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice using a custom comparison function.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `data`: The slice to sort
|
||||||
|
- `less`: A function that returns true if data[i] should come before data[j]
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{"Alice", 30},
|
||||||
|
{"Bob", 25},
|
||||||
|
{"Charlie", 35},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by age
|
||||||
|
poindexter.SortBy(people, func(i, j int) bool {
|
||||||
|
return people[i].Age < people[j].Age
|
||||||
|
})
|
||||||
|
// Result: [Bob(25) Alice(30) Charlie(35)]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### SortByKey
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortByKey[T any, K int | float64 | string](data []T, key func(T) K)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice by extracting a comparable key from each element in ascending order.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `data`: The slice to sort
|
||||||
|
- `key`: A function that extracts a sortable key from each element
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Product struct {
|
||||||
|
Name string
|
||||||
|
Price float64
|
||||||
|
}
|
||||||
|
|
||||||
|
products := []Product{
|
||||||
|
{"Apple", 1.50},
|
||||||
|
{"Banana", 0.75},
|
||||||
|
{"Cherry", 3.00},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by price
|
||||||
|
poindexter.SortByKey(products, func(p Product) float64 {
|
||||||
|
return p.Price
|
||||||
|
})
|
||||||
|
// Result: [Banana(0.75) Apple(1.50) Cherry(3.00)]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### SortByKeyDescending
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortByKeyDescending[T any, K int | float64 | string](data []T, key func(T) K)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sorts a slice by extracting a comparable key from each element in descending order.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Student struct {
|
||||||
|
Name string
|
||||||
|
Score int
|
||||||
|
}
|
||||||
|
|
||||||
|
students := []Student{
|
||||||
|
{"Alice", 85},
|
||||||
|
{"Bob", 92},
|
||||||
|
{"Charlie", 78},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by score descending
|
||||||
|
poindexter.SortByKeyDescending(students, func(s Student) int {
|
||||||
|
return s.Score
|
||||||
|
})
|
||||||
|
// Result: [Bob(92) Alice(85) Charlie(78)]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Checking if Sorted
|
||||||
|
|
||||||
|
#### IsSorted
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsSorted(data []int) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
Checks if a slice of integers is sorted in ascending order.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### IsSortedStrings
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsSortedStrings(data []string) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
Checks if a slice of strings is sorted in ascending order.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### IsSortedFloat64s
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IsSortedFloat64s(data []float64) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
Checks if a slice of float64 values is sorted in ascending order.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Binary Search
|
||||||
|
|
||||||
|
#### BinarySearch
|
||||||
|
|
||||||
|
```go
|
||||||
|
func BinarySearch(data []int, target int) int
|
||||||
|
```
|
||||||
|
|
||||||
|
Performs a binary search on a sorted slice of integers.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `data`: A sorted slice of integers
|
||||||
|
- `target`: The value to search for
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `int`: The index where target is found, or -1 if not found
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
numbers := []int{1, 3, 5, 7, 9, 11}
|
||||||
|
index := poindexter.BinarySearch(numbers, 7)
|
||||||
|
fmt.Println(index) // Output: 3
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### BinarySearchStrings
|
||||||
|
|
||||||
|
```go
|
||||||
|
func BinarySearchStrings(data []string, target string) int
|
||||||
|
```
|
||||||
|
|
||||||
|
Performs a binary search on a sorted slice of strings.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `data`: A sorted slice of strings
|
||||||
|
- `target`: The value to search for
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `int`: The index where target is found, or -1 if not found
|
||||||
125
docs/getting-started.md
Normal file
125
docs/getting-started.md
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
This guide will help you get started with the Poindexter library.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install Poindexter, use `go get`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/Snider/Poindexter
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
### Importing the Library
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/Snider/Poindexter"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using the Hello Function
|
||||||
|
|
||||||
|
The `Hello` function returns a greeting message:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Snider/Poindexter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Say hello to the world
|
||||||
|
fmt.Println(poindexter.Hello(""))
|
||||||
|
// Output: Hello, World!
|
||||||
|
|
||||||
|
// Say hello to someone specific
|
||||||
|
fmt.Println(poindexter.Hello("Poindexter"))
|
||||||
|
// Output: Hello, Poindexter!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting the Version
|
||||||
|
|
||||||
|
You can check the library version:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Snider/Poindexter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
version := poindexter.Version()
|
||||||
|
fmt.Println("Library version:", version)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sorting Data
|
||||||
|
|
||||||
|
Poindexter includes comprehensive sorting utilities:
|
||||||
|
|
||||||
|
### Basic Sorting
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Snider/Poindexter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Sort integers
|
||||||
|
numbers := []int{3, 1, 4, 1, 5, 9}
|
||||||
|
poindexter.SortInts(numbers)
|
||||||
|
fmt.Println(numbers) // [1 1 3 4 5 9]
|
||||||
|
|
||||||
|
// Sort strings
|
||||||
|
words := []string{"banana", "apple", "cherry"}
|
||||||
|
poindexter.SortStrings(words)
|
||||||
|
fmt.Println(words) // [apple banana cherry]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Sorting with Custom Keys
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Snider/Poindexter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
Name string
|
||||||
|
Price float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
products := []Product{
|
||||||
|
{"Apple", 1.50},
|
||||||
|
{"Banana", 0.75},
|
||||||
|
{"Cherry", 3.00},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by price using SortByKey
|
||||||
|
poindexter.SortByKey(products, func(p Product) float64 {
|
||||||
|
return p.Price
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, p := range products {
|
||||||
|
fmt.Printf("%s: $%.2f\n", p.Name, p.Price)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Check out the [API Reference](api.md) for detailed documentation
|
||||||
|
- Read about the [License](license.md)
|
||||||
49
docs/index.md
Normal file
49
docs/index.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Poindexter
|
||||||
|
|
||||||
|
Welcome to the Poindexter Go library documentation!
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Poindexter is a Go library package licensed under EUPL-1.2.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Simple and easy to use
|
||||||
|
- Comprehensive sorting utilities with custom comparators
|
||||||
|
- Generic sorting functions with type safety
|
||||||
|
- Binary search capabilities
|
||||||
|
- Well-documented API
|
||||||
|
- Comprehensive test coverage
|
||||||
|
- Cross-platform support
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Install the library:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/Snider/Poindexter
|
||||||
|
```
|
||||||
|
|
||||||
|
Use it in your code:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Snider/Poindexter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(poindexter.Hello("World"))
|
||||||
|
fmt.Println("Version:", poindexter.Version())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the European Union Public Licence v1.2 (EUPL-1.2).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||||
32
docs/license.md
Normal file
32
docs/license.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# License
|
||||||
|
|
||||||
|
This project is licensed under the **European Union Public Licence v1.2 (EUPL-1.2)**.
|
||||||
|
|
||||||
|
## About EUPL-1.2
|
||||||
|
|
||||||
|
The European Union Public Licence (EUPL) is a copyleft free/open source software license created on the initiative of and approved by the European Commission.
|
||||||
|
|
||||||
|
## Key Points
|
||||||
|
|
||||||
|
- **Open Source**: The EUPL is an OSI-approved open source license
|
||||||
|
- **Copyleft**: Derivative works must be distributed under the same or compatible license
|
||||||
|
- **Multilingual**: The license is available in all official EU languages
|
||||||
|
- **Compatible**: Compatible with other major open source licenses including GPL
|
||||||
|
|
||||||
|
## Full License Text
|
||||||
|
|
||||||
|
The complete license text can be found in the [LICENSE](https://github.com/Snider/Poindexter/blob/main/LICENSE) file in the repository.
|
||||||
|
|
||||||
|
## Using EUPL-1.2 Licensed Code
|
||||||
|
|
||||||
|
When using this library:
|
||||||
|
|
||||||
|
1. You must retain copyright and license notices
|
||||||
|
2. You must state significant changes made to the code
|
||||||
|
3. Derivative works must be licensed under EUPL-1.2 or a compatible license
|
||||||
|
|
||||||
|
## More Information
|
||||||
|
|
||||||
|
For more information about the EUPL, visit:
|
||||||
|
- [Official EUPL Website](https://joinup.ec.europa.eu/collection/eupl)
|
||||||
|
- [EUPL on Wikipedia](https://en.wikipedia.org/wiki/European_Union_Public_Licence)
|
||||||
3
go.mod
Normal file
3
go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/Snider/Poindexter
|
||||||
|
|
||||||
|
go 1.24.9
|
||||||
61
mkdocs.yml
Normal file
61
mkdocs.yml
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
site_name: Poindexter
|
||||||
|
site_description: Poindexter Go Library Documentation
|
||||||
|
site_author: Snider
|
||||||
|
repo_url: https://github.com/Snider/Poindexter
|
||||||
|
repo_name: Snider/Poindexter
|
||||||
|
edit_uri: edit/main/docs/
|
||||||
|
|
||||||
|
theme:
|
||||||
|
name: material
|
||||||
|
palette:
|
||||||
|
# Palette toggle for light mode
|
||||||
|
- scheme: default
|
||||||
|
primary: indigo
|
||||||
|
accent: indigo
|
||||||
|
toggle:
|
||||||
|
icon: material/brightness-7
|
||||||
|
name: Switch to dark mode
|
||||||
|
# Palette toggle for dark mode
|
||||||
|
- scheme: slate
|
||||||
|
primary: indigo
|
||||||
|
accent: indigo
|
||||||
|
toggle:
|
||||||
|
icon: material/brightness-4
|
||||||
|
name: Switch to light mode
|
||||||
|
features:
|
||||||
|
- navigation.tabs
|
||||||
|
- navigation.sections
|
||||||
|
- navigation.top
|
||||||
|
- search.suggest
|
||||||
|
- search.highlight
|
||||||
|
- content.tabs.link
|
||||||
|
- content.code.annotation
|
||||||
|
- content.code.copy
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- search
|
||||||
|
|
||||||
|
markdown_extensions:
|
||||||
|
- pymdownx.highlight:
|
||||||
|
anchor_linenums: true
|
||||||
|
- pymdownx.inlinehilite
|
||||||
|
- pymdownx.snippets
|
||||||
|
- pymdownx.superfences
|
||||||
|
- pymdownx.tabbed:
|
||||||
|
alternate_style: true
|
||||||
|
- admonition
|
||||||
|
- pymdownx.details
|
||||||
|
- pymdownx.arithmatex:
|
||||||
|
generic: true
|
||||||
|
- footnotes
|
||||||
|
- pymdownx.mark
|
||||||
|
- attr_list
|
||||||
|
- md_in_html
|
||||||
|
|
||||||
|
nav:
|
||||||
|
- Home: index.md
|
||||||
|
- Getting Started: getting-started.md
|
||||||
|
- API Reference: api.md
|
||||||
|
- License: license.md
|
||||||
|
|
||||||
|
copyright: Copyright © 2025 Snider
|
||||||
15
poindexter.go
Normal file
15
poindexter.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Package poindexter provides functionality for the Poindexter library.
|
||||||
|
package poindexter
|
||||||
|
|
||||||
|
// Version returns the current version of the library.
|
||||||
|
func Version() string {
|
||||||
|
return "0.1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hello returns a greeting message.
|
||||||
|
func Hello(name string) string {
|
||||||
|
if name == "" {
|
||||||
|
return "Hello, World!"
|
||||||
|
}
|
||||||
|
return "Hello, " + name + "!"
|
||||||
|
}
|
||||||
34
poindexter_test.go
Normal file
34
poindexter_test.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package poindexter
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestVersion(t *testing.T) {
|
||||||
|
version := Version()
|
||||||
|
if version == "" {
|
||||||
|
t.Error("Version should not be empty")
|
||||||
|
}
|
||||||
|
if version != "0.1.0" {
|
||||||
|
t.Errorf("Expected version 0.1.0, got %s", version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHello(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"empty name", "", "Hello, World!"},
|
||||||
|
{"with name", "Poindexter", "Hello, Poindexter!"},
|
||||||
|
{"another name", "Go", "Hello, Go!"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := Hello(tt.input)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("Hello(%q) = %q, want %q", tt.input, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
89
sort.go
Normal file
89
sort.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package poindexter
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
// SortInts sorts a slice of integers in ascending order in place.
|
||||||
|
func SortInts(data []int) {
|
||||||
|
sort.Ints(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortIntsDescending sorts a slice of integers in descending order in place.
|
||||||
|
func SortIntsDescending(data []int) {
|
||||||
|
sort.Sort(sort.Reverse(sort.IntSlice(data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortStrings sorts a slice of strings in ascending order in place.
|
||||||
|
func SortStrings(data []string) {
|
||||||
|
sort.Strings(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortStringsDescending sorts a slice of strings in descending order in place.
|
||||||
|
func SortStringsDescending(data []string) {
|
||||||
|
sort.Sort(sort.Reverse(sort.StringSlice(data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortFloat64s sorts a slice of float64 values in ascending order in place.
|
||||||
|
func SortFloat64s(data []float64) {
|
||||||
|
sort.Float64s(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortFloat64sDescending sorts a slice of float64 values in descending order in place.
|
||||||
|
func SortFloat64sDescending(data []float64) {
|
||||||
|
sort.Sort(sort.Reverse(sort.Float64Slice(data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortBy sorts a slice using a custom less function.
|
||||||
|
// The less function should return true if data[i] should come before data[j].
|
||||||
|
func SortBy[T any](data []T, less func(i, j int) bool) {
|
||||||
|
sort.Slice(data, less)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortByKey sorts a slice by extracting a comparable key from each element.
|
||||||
|
// The key function should return a value that implements constraints.Ordered.
|
||||||
|
func SortByKey[T any, K int | float64 | string](data []T, key func(T) K) {
|
||||||
|
sort.Slice(data, func(i, j int) bool {
|
||||||
|
return key(data[i]) < key(data[j])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortByKeyDescending sorts a slice by extracting a comparable key from each element in descending order.
|
||||||
|
func SortByKeyDescending[T any, K int | float64 | string](data []T, key func(T) K) {
|
||||||
|
sort.Slice(data, func(i, j int) bool {
|
||||||
|
return key(data[i]) > key(data[j])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSorted checks if a slice of integers is sorted in ascending order.
|
||||||
|
func IsSorted(data []int) bool {
|
||||||
|
return sort.IntsAreSorted(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSortedStrings checks if a slice of strings is sorted in ascending order.
|
||||||
|
func IsSortedStrings(data []string) bool {
|
||||||
|
return sort.StringsAreSorted(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSortedFloat64s checks if a slice of float64 values is sorted in ascending order.
|
||||||
|
func IsSortedFloat64s(data []float64) bool {
|
||||||
|
return sort.Float64sAreSorted(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinarySearch performs a binary search on a sorted slice of integers.
|
||||||
|
// Returns the index where target is found, or -1 if not found.
|
||||||
|
func BinarySearch(data []int, target int) int {
|
||||||
|
idx := sort.SearchInts(data, target)
|
||||||
|
if idx < len(data) && data[idx] == target {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinarySearchStrings performs a binary search on a sorted slice of strings.
|
||||||
|
// Returns the index where target is found, or -1 if not found.
|
||||||
|
func BinarySearchStrings(data []string, target string) int {
|
||||||
|
idx := sort.SearchStrings(data, target)
|
||||||
|
if idx < len(data) && data[idx] == target {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
347
sort_test.go
Normal file
347
sort_test.go
Normal file
|
|
@ -0,0 +1,347 @@
|
||||||
|
package poindexter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSortInts(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{"empty slice", []int{}, []int{}},
|
||||||
|
{"single element", []int{5}, []int{5}},
|
||||||
|
{"already sorted", []int{1, 2, 3, 4, 5}, []int{1, 2, 3, 4, 5}},
|
||||||
|
{"reverse sorted", []int{5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5}},
|
||||||
|
{"unsorted", []int{3, 1, 4, 1, 5, 9, 2, 6}, []int{1, 1, 2, 3, 4, 5, 6, 9}},
|
||||||
|
{"with negatives", []int{-3, 5, -1, 0, 2}, []int{-3, -1, 0, 2, 5}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
data := make([]int, len(tt.input))
|
||||||
|
copy(data, tt.input)
|
||||||
|
SortInts(data)
|
||||||
|
if !reflect.DeepEqual(data, tt.expected) {
|
||||||
|
t.Errorf("SortInts(%v) = %v, want %v", tt.input, data, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortIntsDescending(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{"empty slice", []int{}, []int{}},
|
||||||
|
{"single element", []int{5}, []int{5}},
|
||||||
|
{"ascending order", []int{1, 2, 3, 4, 5}, []int{5, 4, 3, 2, 1}},
|
||||||
|
{"unsorted", []int{3, 1, 4, 1, 5, 9, 2, 6}, []int{9, 6, 5, 4, 3, 2, 1, 1}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
data := make([]int, len(tt.input))
|
||||||
|
copy(data, tt.input)
|
||||||
|
SortIntsDescending(data)
|
||||||
|
if !reflect.DeepEqual(data, tt.expected) {
|
||||||
|
t.Errorf("SortIntsDescending(%v) = %v, want %v", tt.input, data, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortStrings(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{"empty slice", []string{}, []string{}},
|
||||||
|
{"single element", []string{"hello"}, []string{"hello"}},
|
||||||
|
{"already sorted", []string{"a", "b", "c"}, []string{"a", "b", "c"}},
|
||||||
|
{"reverse sorted", []string{"z", "y", "x"}, []string{"x", "y", "z"}},
|
||||||
|
{"unsorted", []string{"banana", "apple", "cherry", "date"}, []string{"apple", "banana", "cherry", "date"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
data := make([]string, len(tt.input))
|
||||||
|
copy(data, tt.input)
|
||||||
|
SortStrings(data)
|
||||||
|
if !reflect.DeepEqual(data, tt.expected) {
|
||||||
|
t.Errorf("SortStrings(%v) = %v, want %v", tt.input, data, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortStringsDescending(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{"empty slice", []string{}, []string{}},
|
||||||
|
{"ascending order", []string{"a", "b", "c"}, []string{"c", "b", "a"}},
|
||||||
|
{"unsorted", []string{"banana", "apple", "cherry"}, []string{"cherry", "banana", "apple"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
data := make([]string, len(tt.input))
|
||||||
|
copy(data, tt.input)
|
||||||
|
SortStringsDescending(data)
|
||||||
|
if !reflect.DeepEqual(data, tt.expected) {
|
||||||
|
t.Errorf("SortStringsDescending(%v) = %v, want %v", tt.input, data, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortFloat64s(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []float64
|
||||||
|
expected []float64
|
||||||
|
}{
|
||||||
|
{"empty slice", []float64{}, []float64{}},
|
||||||
|
{"single element", []float64{3.14}, []float64{3.14}},
|
||||||
|
{"unsorted", []float64{3.14, 1.41, 2.71, 1.73}, []float64{1.41, 1.73, 2.71, 3.14}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
data := make([]float64, len(tt.input))
|
||||||
|
copy(data, tt.input)
|
||||||
|
SortFloat64s(data)
|
||||||
|
if !reflect.DeepEqual(data, tt.expected) {
|
||||||
|
t.Errorf("SortFloat64s(%v) = %v, want %v", tt.input, data, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortFloat64sDescending(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []float64
|
||||||
|
expected []float64
|
||||||
|
}{
|
||||||
|
{"empty slice", []float64{}, []float64{}},
|
||||||
|
{"unsorted", []float64{3.14, 1.41, 2.71}, []float64{3.14, 2.71, 1.41}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
data := make([]float64, len(tt.input))
|
||||||
|
copy(data, tt.input)
|
||||||
|
SortFloat64sDescending(data)
|
||||||
|
if !reflect.DeepEqual(data, tt.expected) {
|
||||||
|
t.Errorf("SortFloat64sDescending(%v) = %v, want %v", tt.input, data, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortBy(t *testing.T) {
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
people := []Person{
|
||||||
|
{"Alice", 30},
|
||||||
|
{"Bob", 25},
|
||||||
|
{"Charlie", 35},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by age
|
||||||
|
SortBy(people, func(i, j int) bool {
|
||||||
|
return people[i].Age < people[j].Age
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := []Person{
|
||||||
|
{"Bob", 25},
|
||||||
|
{"Alice", 30},
|
||||||
|
{"Charlie", 35},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(people, expected) {
|
||||||
|
t.Errorf("SortBy (by age) = %v, want %v", people, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortByKey(t *testing.T) {
|
||||||
|
type Product struct {
|
||||||
|
Name string
|
||||||
|
Price float64
|
||||||
|
}
|
||||||
|
|
||||||
|
products := []Product{
|
||||||
|
{"Apple", 1.50},
|
||||||
|
{"Banana", 0.75},
|
||||||
|
{"Cherry", 3.00},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by price
|
||||||
|
SortByKey(products, func(p Product) float64 {
|
||||||
|
return p.Price
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := []Product{
|
||||||
|
{"Banana", 0.75},
|
||||||
|
{"Apple", 1.50},
|
||||||
|
{"Cherry", 3.00},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(products, expected) {
|
||||||
|
t.Errorf("SortByKey (by price) = %v, want %v", products, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortByKeyDescending(t *testing.T) {
|
||||||
|
type Student struct {
|
||||||
|
Name string
|
||||||
|
Score int
|
||||||
|
}
|
||||||
|
|
||||||
|
students := []Student{
|
||||||
|
{"Alice", 85},
|
||||||
|
{"Bob", 92},
|
||||||
|
{"Charlie", 78},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by score descending
|
||||||
|
SortByKeyDescending(students, func(s Student) int {
|
||||||
|
return s.Score
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := []Student{
|
||||||
|
{"Bob", 92},
|
||||||
|
{"Alice", 85},
|
||||||
|
{"Charlie", 78},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(students, expected) {
|
||||||
|
t.Errorf("SortByKeyDescending (by score) = %v, want %v", students, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsSorted(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"empty slice", []int{}, true},
|
||||||
|
{"single element", []int{5}, true},
|
||||||
|
{"sorted ascending", []int{1, 2, 3, 4, 5}, true},
|
||||||
|
{"not sorted", []int{1, 3, 2, 4}, false},
|
||||||
|
{"sorted with duplicates", []int{1, 1, 2, 3}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := IsSorted(tt.input)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("IsSorted(%v) = %v, want %v", tt.input, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsSortedStrings(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"sorted", []string{"a", "b", "c"}, true},
|
||||||
|
{"not sorted", []string{"b", "a", "c"}, false},
|
||||||
|
{"empty", []string{}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := IsSortedStrings(tt.input)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("IsSortedStrings(%v) = %v, want %v", tt.input, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsSortedFloat64s(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []float64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"sorted", []float64{1.1, 2.2, 3.3}, true},
|
||||||
|
{"not sorted", []float64{2.2, 1.1, 3.3}, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := IsSortedFloat64s(tt.input)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("IsSortedFloat64s(%v) = %v, want %v", tt.input, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinarySearch(t *testing.T) {
|
||||||
|
data := []int{1, 3, 5, 7, 9, 11}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
target int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"found at start", 1, 0},
|
||||||
|
{"found in middle", 5, 2},
|
||||||
|
{"found at end", 11, 5},
|
||||||
|
{"not found", 4, -1},
|
||||||
|
{"not found - too small", 0, -1},
|
||||||
|
{"not found - too large", 12, -1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := BinarySearch(data, tt.target)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("BinarySearch(%v, %d) = %d, want %d", data, tt.target, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinarySearchStrings(t *testing.T) {
|
||||||
|
data := []string{"apple", "banana", "cherry", "date"}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
target string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"found at start", "apple", 0},
|
||||||
|
{"found in middle", "banana", 1},
|
||||||
|
{"found at end", "date", 3},
|
||||||
|
{"not found", "grape", -1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := BinarySearchStrings(data, tt.target)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("BinarySearchStrings(%v, %q) = %d, want %d", data, tt.target, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue