Copse.Core
0.3.0-alpha.19
dotnet add package Copse.Core --version 0.3.0-alpha.19
NuGet\Install-Package Copse.Core -Version 0.3.0-alpha.19
<PackageReference Include="Copse.Core" Version="0.3.0-alpha.19" />
<PackageVersion Include="Copse.Core" Version="0.3.0-alpha.19" />
<PackageReference Include="Copse.Core" />
paket add Copse.Core --version 0.3.0-alpha.19
#r "nuget: Copse.Core, 0.3.0-alpha.19"
#:package Copse.Core@0.3.0-alpha.19
#addin nuget:?package=Copse.Core&version=0.3.0-alpha.19&prerelease
#tool nuget:?package=Copse.Core&version=0.3.0-alpha.19&prerelease
Copse
LINQ for trees. ITreenumerable<T> is to trees what IEnumerable<T> is to sequences — a lazy,
composable abstraction supporting depth-first and breadth-first traversal with 40+ operations:
Where, Select, GetLeaves, PruneBefore, LeaffixAggregate, Union, and more.
No equality contract required: node types need not implement IEquatable<T> or override
GetHashCode.
Install
dotnet add package Copse.Linq --prerelease
Copse.Linq transitively brings in Copse and Copse.Core. Targets net48, net8.0, and netstandard2.0.
Examples
Adapt any tree type by implementing IChildEnumerator<T> — a struct Copse calls to enumerate each
node's children:
using Copse;
using Copse.Linq;
using Copse.Treenumerables;
using System.Linq;
// Node n has children 2n and 2n+1 — a complete binary tree capped at 7.
struct BinaryChildren : IChildEnumerator<int>
{
private int _next;
private readonly int _last;
private bool _disposed;
public BinaryChildren(int parent)
{
_next = parent * 2; // first child of n is 2n...
_last = parent * 2 + 1; // ...and its second (last) child is 2n+1
_disposed = false;
}
public ChildResult<int> MoveNext()
{
if (_disposed || _next > _last || _next > 7)
return default;
var child = new NodeAndSiblingIndex<int>(_next, _next % 2);
_next++;
return new ChildResult<int>(child);
}
public void Dispose() => _disposed = true;
}
ITreenumerable<int> tree = new Treenumerable<int, BinaryChildren>(
ctx => new BinaryChildren(ctx.Node), new[] { 1 });
// 1
// / \
// 2 3
// / \ / \
// 4 5 6 7
Once you have an ITreenumerable<T>, the full operation set is available. Operations compose
without materialization when possible — the streaming operators stay lazy end-to-end — and when
an operation does capture the tree (or might), its return type and docs say so:
int[] preOrder = tree.GetPreorderTraversal().ToArray(); // [1, 2, 4, 5, 3, 6, 7]
int[] leaves = tree.GetLeaves().ToArray(); // [4, 5, 6, 7]
// Select transforms values while preserving tree structure
int[] doubled = tree
.Select(node => node * 2)
.GetPreorderTraversal()
.ToArray(); // [2, 4, 8, 10, 6, 12, 14]
// PruneBefore removes a node and its descendants when the predicate is true
int[] topTwo = tree
.PruneBefore((node, position) => position.Depth >= 2)
.GetLeaves()
.ToArray(); // [2, 3]
Where is structural. A filtered-out node's children are promoted to the nearest remaining
ancestor — unlike IEnumerable.Where, which is a flat element filter:
// Remove even nodes. Children of 2 (which are 4 and 5) become children of 1.
// 4 and 6 are also removed but have no children, so they simply vanish.
int[] filtered = tree
.Where(node => node % 2 != 0)
.GetPreorderTraversal()
.ToArray();
// Result tree: 1(5, 3(7)) => [1, 5, 3, 7]
LeaffixAggregate folds bottom-up, one value per root: each family's completed child
accumulations are reduced pairwise (the edge accumulator), then the node folds itself in
once (the node accumulator); leaves answer through the leaf selector directly:
int subtreeSum = tree
.LeaffixAggregate(
leaf => leaf, // each leaf's own accumulation
(accumulate, childAccumulate) => accumulate + childAccumulate,
(accumulate, node) => accumulate + node)
.First()
.Accumulate; // results are NodeAccumulations: the root's value paired with its fold
// 28 (1 + 2 + 3 + 4 + 5 + 6 + 7)
Packages
| Package | Description |
|---|---|
Copse.Core |
Interfaces, enums, and position types (ITreenumerable<T>, IWalkableTreenumerable<T,H>, TreeWalker, NodePosition, …) |
Copse |
Depth-first and breadth-first traversal engine |
Copse.Linq |
LINQ-style tree operations (Where, Select, GetLeaves, PruneBefore, LeaffixAggregate, Union, tree-walker navigation, …) |
Copse.SimpleSerializer |
Text-format tree serialization for debugging and testing |
Copse.Core.Async / Copse.Async / Copse.Linq.Async |
The async family — the same surface over awaited pulls (these are the codegen sources the sync packages are generated from) |
Documentation
Full documentation is coming to copselib.org (WIP). For now, the examples above and the source are the reference.
Benchmarks
Performance results are published at copselib.github.io/copse-dotnet.
License
MIT — see LICENSE. © 2023–2026 Jason Boyd.
The disposable utilities in Copse.Disposables (CompositeDisposable, RefCountDisposable,
Disposable.Create, …) are adapted from System.Reactive
(MIT, © .NET Foundation and Contributors) — same names, same semantics, no new concepts.
See THIRD-PARTY-NOTICES.md.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 is compatible. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.8
- Copse.Vocabulary (>= 0.3.0-alpha.19)
-
.NETStandard 2.0
- Copse.Vocabulary (>= 0.3.0-alpha.19)
-
.NETStandard 2.1
- Copse.Vocabulary (>= 0.3.0-alpha.19)
-
net8.0
- Copse.Vocabulary (>= 0.3.0-alpha.19)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Copse.Core:
| Package | Downloads |
|---|---|
|
Copse
Depth-first and breadth-first tree traversal engine for Copse. |
|
|
Copse.SimpleSerializer
Text-based tree serialization for Copse. |
|
|
Copse.Linq
LINQ-style query, transformation, and set operations over trees for Copse (Where, Select, Union, aggregation, tree-walker navigation, and more). |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.3.0-alpha.19 | 51 | 8/24/2026 |
| 0.3.0-alpha.18 | 70 | 8/17/2026 |
| 0.3.0-alpha.17 | 72 | 8/5/2026 |
| 0.3.0-alpha.16 | 61 | 8/5/2026 |
| 0.3.0-alpha.15 | 68 | 8/5/2026 |
| 0.3.0-alpha.14 | 82 | 8/5/2026 |
| 0.3.0-alpha.13 | 65 | 8/4/2026 |
| 0.3.0-alpha.12 | 69 | 8/4/2026 |
| 0.3.0-alpha.11 | 77 | 8/4/2026 |
| 0.3.0-alpha.10 | 67 | 8/4/2026 |
| 0.3.0-alpha.9 | 73 | 8/2/2026 |
| 0.3.0-alpha.8 | 71 | 8/2/2026 |
| 0.3.0-alpha.7 | 64 | 8/2/2026 |
| 0.3.0-alpha.6 | 74 | 7/17/2026 |
| 0.3.0-alpha.5 | 83 | 7/10/2026 |
| 0.3.0-alpha.4 | 72 | 7/10/2026 |
| 0.3.0-alpha.3 | 78 | 7/10/2026 |
| 0.3.0-alpha.2 | 76 | 7/9/2026 |
| 0.3.0-alpha.1 | 75 | 7/6/2026 |
| 0.2.0-alpha.1 | 72 | 7/6/2026 |