A collection of C# exercises covering language fundamentals, data structures, LINQ, object-oriented design and classic algorithms. Every exercise is covered by xUnit tests.
| Topic | Exercise | Concepts |
|---|---|---|
| Basics | FizzBuzz | tuple pattern matching, switch expressions |
| MathHelpers | primes, factorial, Fibonacci (yield return), GCD |
|
| Strings | StringExercises | reverse, palindrome, anagram, run-length encoding, word frequency |
| LogParser | [GeneratedRegex] with named groups, key=value extraction, redacting emails and card numbers |
|
| TemplateEngine | {{ name | filter }} placeholders with Regex.Replace and a MatchEvaluator, fail-loud on missing values |
|
| UnicodeText | grapheme-aware length, reverse and truncate, slugs via NormalizationForm.FormD, accent-insensitive compare |
|
| Collections | Stack<T> | generics, a growable array from scratch |
| BracketValidator | using a stack to check nesting | |
| LruCache<TKey, TValue> | O(1) cache with a dictionary and a linked list | |
| SinglyLinkedList<T> | in-place reverse, fast/slow pointers, IEnumerable<T> |
|
| BinarySearchTree<T> | insert, delete with in-order successor, iterative in-order traversal | |
| MinHeap<T> | array-backed heap, sift up/down, priority queue via IComparer<T> |
|
| Graph<T> | adjacency list, BFS, DFS, shortest path, topological sort | |
| Trie | prefix tree for autocomplete, ranked suggestions, removal with pruning, iterative traversal | |
| UnionFind<T> | disjoint sets with union by size and path compression | |
| RingBuffer<T> | fixed-size circular buffer, overwrite-oldest, moving average, fail-fast enumeration | |
| Deque<T> | double-ended queue on a growable circular array, O(1) at both ends and by index | |
| SkipList<T> | sorted linked list with random "express lanes", O(log n) expected, range queries | |
| LfuCache<TKey, TValue> | O(1) least-frequently-used eviction with per-frequency lists, ties broken by recency | |
| BiMap<TKey, TValue> | lookups in both directions, keeping two dictionaries consistent | |
| IntervalSet | merged half-open ranges: adding merges, removing splits, finding free slots | |
| AvlTree<T> | self-balancing BST with rotations, subtree sizes for k-th smallest and rank | |
| Rope | a persistent tree of string pieces: cheap inserts and deletes, shared old versions | |
| InsertionOrderedMap<TKey, TValue> | a hash map plus linked list that keeps insertion order, or acts as an LRU cache | |
| IndexedPriorityQueue<TItem, TPriority> | a heap with a position index for decrease-key, Dijkstra without duplicate entries | |
| Bag<T> | a multiset with counts, most common items, multiset union, intersection and difference | |
| LINQ | LinqExercises | GroupBy, MaxBy, CountBy, Chunk, records |
| MyLinq | LINQ operators rebuilt with yield: deferred vs. immediate, streaming vs. buffering, eager argument checks |
|
| RangeSequence | IEnumerable<T> / IEnumerator<T> written by hand: what the compiler generates for yield return |
|
| OOP | Shapes | abstraction, inheritance, polymorphism, primary constructors |
| BankAccount | encapsulation, invariants, guard clauses | |
| Design patterns | Strategy | swappable, composable discount rules for a shopping cart |
| Observer | stock price alerts with IObservable<T> / IObserver<T> |
|
| Factory | create shapes from text, register new ones without changing the factory | |
| Decorator | caching, logging and sale prices stacked around a price service | |
| Builder | fluent API producing an immutable, validated email | |
| Command | undo/redo history, commands that remember state, macros undone as one step | |
| Chain of responsibility | expense approvals passed along until someone decides | |
| Visitor | HTML, Markdown, word count and table of contents over a document tree via double dispatch | |
| Composite | files and folders behind one abstraction, sizes, search and a tree-style listing |
|
| Adapter | wrapping a legacy Fahrenheit driver with error codes into an async Celsius interface | |
| Proxy | lazy (virtual) and protection proxies, a logging proxy generated with DispatchProxy |
|
| Template method | fixed export steps with CSV and Markdown variants and optional hooks | |
| Mediator | a chat room routing broadcasts, direct messages and mutes | |
| Memento | opaque snapshots and named checkpoints with a capped history | |
| State | a connection whose behaviour changes by swapping state objects | |
| Flyweight | shared text styles: 11,000 characters, two style objects | |
| Prototype | shallow vs deep copies, a registry of templates | |
| Null object | a do-nothing audit log and a guest shopper instead of null checks | |
| Bridge | figures × drawing back-ends (SVG, canvas) varying independently | |
| Dependency injection | MiniContainer | a DI container from scratch: reflection-based constructor injection, lifetimes, scopes, cycle and captive-dependency detection |
| Messaging | Microsoft.Extensions.DependencyInjection: AddXxx extension, keyed services, TimeProvider, scope validation |
|
| Testing techniques | RenewalService | code designed for testing: dependencies as interfaces, TimeProvider for the clock |
| Test doubles | hand-written dummy, stub, spy and fake, compared with NSubstitute mocks | |
| SubscriptionBuilder | test data builder with defaults and an implicit conversion | |
| TimeBased | rate limiter and debouncer tested instantly with FakeTimeProvider |
|
| SnapshotTesting | approval tests with .received/.approved files and line diffs | |
| FakeHttpHandler | testing HttpClient code with canned responses, failure sequences and recorded requests |
|
| InMemoryFileSystem | an IFileSystem fake with injectable failures |
|
| MutationTesting | mutants made by rewriting expression trees, killed and surviving (and equivalent) mutants | |
| Algorithms | Sorting | bubble sort, merge sort, quick sort (generic) |
| Searching | binary search, Two Sum with a hash map | |
| DynamicProgramming | coin change (min coins & number of ways), longest common subsequence, edit distance | |
| Dijkstra | weighted shortest paths with a priority queue (reuses MinHeap<T>) |
|
| MinimumSpanningTree | Kruskal's algorithm (reuses UnionFind<T>) |
|
| Backtracking | permutations without duplicates, subsets, combination sum with pruning, N-Queens | |
| Sudoku | backtracking with bit-mask candidates and fewest-options-first | |
| StringSearch | Knuth–Morris–Pratt substring search in O(n + m) | |
| Knapsack | 0/1 knapsack with item reconstruction, a one-row version, why greedy fails, subset sum | |
| LongestIncreasingSubsequence | patience sorting in O(n log n), nesting envelopes | |
| MaximumSubarray | Kadane's algorithm, circular arrays, best single trade | |
| WordBreak | splitting unspaced text into words: memoised search, all splits, fewest words | |
| MatrixChain | interval DP for the cheapest multiplication order | |
| IntervalScheduling | greedy by end time, room assignment with a priority queue, weighted scheduling with DP | |
| EggDrop | the "floors covered by d drops" reformulation vs. the direct DP, a two-egg plan | |
| PalindromeDp | longest palindromic subsequence, minimum insertions and palindrome partitions | |
| PatternMatchingDp | regex ./* and SQL LIKE matching as DP tables |
|
| RodCutting | the textbook DP, with cutting costs | |
| GridPaths | counting paths around obstacles, cheapest path reconstruction | |
| StockTrading | buy/sell state machines: unlimited, k trades, cooldown, fees, checked by brute force | |
| Caching | TtlCache<TKey, TValue> | absolute and sliding expiration on TimeProvider, lazy expiry, compaction, hit/miss counts |
| SingleFlight<TKey, TValue> | stampede protection: concurrent requests for a key share one load, per-caller cancellation | |
| CachedLookup<TKey, TValue> | cache-aside with negative caching and invalidation | |
| Immutable data | PersistentStack<T> / PersistentQueue<T> | structural sharing, O(1) versions, a queue from two stacks |
| UndoableEditor | undo/redo with snapshots of an ImmutableList<T> document, bounded history |
|
| ReadOnlyExposure | IReadOnlyList vs ReadOnlyCollection vs ImmutableArray, FrozenDictionary |
|
| I/O & binary data | ReadingsFile | a versioned binary format: magic number, BinaryWriter/BinaryReader, CRC-32 trailer, reading old versions, distrusting counts |
| Crc32 | table-driven checksum, computed incrementally | |
| PacketHeader | big-endian network headers with BinaryPrimitives on spans, validating untrusted input |
|
| CountingStream | a Stream decorator, measuring gzip size without buffering, constant-memory CSV processing |
|
| Numerics | FloatingPoint | why 0.1 + 0.2 != 0.3, tolerant comparison, Kahan summation, ULP distance |
| Rational | exact fractions on BigInteger with operator interfaces, a valid default |
|
| NumberTheory | modular exponentiation with UInt128, modular inverse, deterministic Miller–Rabin, fast-doubling Fibonacci |
|
| Fixed16 | Q16.16 fixed-point arithmetic, integer-only square root | |
| ContinuedFractions | convergents, best rational approximations (355/113), repeating decimals as fractions | |
| BaseConversion | any base with BigInteger, negabinary, balanced ternary, spreadsheet column names |
|
| Interval | interval arithmetic for tolerances, the dependency problem | |
| Divisors | divisor counts and sieves, perfect and amicable numbers | |
| Hashing | StableHash | why string.GetHashCode() is randomised, FNV-1a, a SplitMix64 finalizer |
| OpenAddressingMap<TKey, TValue> | a hash map from scratch: linear probing, tombstones, power-of-two sizing, load factor | |
| BloomFilter | probabilistic set membership, sizing formulas, double hashing | |
| ConsistentHashRing<TNode> | sharding with virtual nodes: adding a server moves ~1/n of the keys instead of most of them | |
| Modern C# | Records | immutable order with with expressions, value equality over collections |
| ListPatterns | [first, .., last], slice patterns, a text command parser |
|
| GenericMath | INumber<T>, IBinaryInteger<T>, a custom Vector2D<T> with operator interfaces |
|
| SpanExercises | allocation-free parsing, string.Create, in-place rotation with Span<T> |
|
| Async | ParallelDownloader | Task.WhenAll, throttling with SemaphoreSlim, racing with Task.WhenAny |
| Resilience | retry with exponential backoff, timeouts via CancellationToken |
|
| AsyncStreams | IAsyncEnumerable, [EnumeratorCancellation], producer/consumer with Channel<T> |
|
| AsyncCoordination | AsyncLock across awaits, AsyncManualResetEvent with RunContinuationsAsynchronously, AsyncLazy<T> with retry |
|
| AsyncBatcher<T> | flushing by size or age on a channel, per-item completion | |
| KeyedLock<TKey> | one async lock per key with reference-counted cleanup | |
| PeriodicJob | PeriodicTimer on a TimeProvider, no overlapping runs, surviving failures |
|
| CallbackAdapters | callbacks, events and timers wrapped as tasks with TaskCompletionSource |
|
| ProgressReporting | IProgress<T> with a throttle that always delivers the final report |
|
| Concurrency | Counters | lost updates from ++, lock with System.Threading.Lock, Interlocked, a lock-free max with compare-and-swap |
| BoundedBuffer<T> | blocking producer/consumer queue with Monitor.Wait / PulseAll |
|
| OnceCache<TKey, TValue> | ConcurrentDictionary + Lazy<T> so the factory runs once per key, retry after a failed build |
|
| LockFreeStack<T> | Treiber stack with compare-and-swap, SpinWait back-off, why .NET has no ABA problem here |
|
| ReadWriteCache<TKey, TValue> | ReaderWriterLockSlim with an upgradeable lock so a missing value is built once |
|
| Actor | lock-free state owned by one mailbox loop on a Channel<T>, post and ask |
|
| ParallelAggregation | thread-local partial sums vs. a shared lock, parallel histograms, PLINQ | |
| PhasedComputation | Barrier phases with double buffering, CountdownEvent, exceptions on pool threads |
|
| StripedMap<TKey, TValue> | lock striping, consistent counts by taking locks in a fixed order | |
| Domain modelling | PrefixedIds | strongly typed cus_… / ord_… ids with static abstract members, time-ordered Guid.CreateVersion7 |
| ValueObjects | always-valid EmailAddress via Result<T>, DateRange with overlap, intersection and merging |
|
| Allocation | splitting money without losing cents: largest-remainder method, by ratios, refunds | |
| Specification<T> | composable business rules as expression trees (&, |, !), parameter rebinding, usable with IQueryable |
|
| Error handling | Result<T> | errors as values: Map, Bind, Ensure, Match, LINQ query syntax, combining independent results |
| SignupValidator | form validation that fails fast vs. reports every error, cross-field rules | |
| ExceptionHandling | every failure from Task.WhenAll, cleanup steps that all run, ExceptionDispatchInfo to keep stack traces |
|
| Reflection | PropertyAccessor | expression trees compiled into fast getters and setters instead of PropertyInfo.GetValue |
| AttributeValidator | custom attributes ([Required], [Range], [MaxLength]) read once per type and cached |
|
| CommandRegistry | plugin discovery: scan assemblies for [Command] classes, create them with Activator |
|
| ObjectDumper | printing any object graph with cycle detection and a depth limit | |
| DeepCloner | deep copies of fields (private ones too), shared references and cycles preserved | |
| ObjectDiff | property-level changes with paths like Address.City, for audit logs |
|
| ObjectMapper | an AutoMapper-style mapper compiled from expression trees, with unmapped-member checks | |
| Serialization | Money | custom JsonConverter<T> writing a value object as "12.50 EUR", errors with the JSON path |
| OrderEvents | polymorphic JSON with [JsonDerivedType], source-generated JsonSerializerContext, strict unknown-member handling, event replay |
|
| Resource management | Disposables | idempotent Dispose, ObjectDisposedException, disposing in reverse order, IAsyncDisposable with await using |
| Pooling | an object pool with allocation-free struct leases, renting buffers from ArrayPool<T>.Shared |
|
| Delegates & events | Functional | composing functions, currying, partial application, memoizing recursive functions |
| TemperatureSensor | the standard event pattern, isolating handlers that throw, unsubscribing lambdas with an IDisposable token |
|
| MiddlewarePipeline | ASP.NET Core-style middleware built by composing next => handler delegates, short-circuiting |
|
| State machines | StateMachine<TState, TTrigger> | table-driven transitions with a fluent API, guards with reasons, entry/exit actions, history |
| DocumentWorkflow | draft → review → approved → published, approval quorum, rules that read like requirements | |
| VendingMachine | a pure (state, event) => (state, effects) function with pattern matching, replayable and immutable |
|
| Dates & times | CalendarMath | DateOnly, age with 29 February birthdays, nth weekday of a month, Easter, business days with holidays |
| TimeZones | skipped and repeated local times around DST, "same time tomorrow", shared working hours, a weekly schedule | |
| RecurrenceRule | iCalendar RRULEs (daily, weekly by day, monthly), COUNT and UNTIL, skipping short months | |
| BusinessHours | SLA due dates in working time, skipping nights, weekends and holidays | |
| RelativeTime | "5 minutes ago", "yesterday", "in 3 weeks", compact "3h" forms | |
| Iso8601 | ISO week numbers around New Year, P1DT2H durations with calendar-aware adding |
|
| Parsing | Lexer | turning text into tokens, syntax errors with positions |
| Parser | recursive descent: precedence and associativity from the grammar, a syntax tree of records | |
| ExpressionTools | evaluating with variables and functions, constant folding, printing with minimal parentheses | |
| Generics | NotificationBus | contravariant handlers (in T), dispatch on the runtime type, covariance and the array covariance trap |
| EnumInfo<TEnum> | a static generic class as a per-type cache, struct, Enum constraint, IParsable<T> with static abstract members |
|
| Compression & encoding | Varint | LEB128 variable-length integers, ZigZag for negatives, delta-encoding sorted timestamps, overflow-safe decoding |
| Huffman | optimal prefix codes with PriorityQueue, canonical codes so the header stores only lengths, bit-level reading and writing |
|
| Base32 | Crockford's alphabet for codes humans type, forgiving decoding, a mod-37 check symbol that catches typos and swaps | |
| Lz77 | sliding-window back-references with hashed match search, overlapping copies | |
| Lzw | a dictionary built on the fly by both sides, the "cScSc" special case | |
| BurrowsWheeler | the bzip2 transform that groups similar characters, and its inverse | |
| MoveToFront | turning runs into zeros, measured with Shannon entropy | |
| BitPacking | storing values in exactly the bits they need, frame-of-reference for timestamps | |
| Diffing | MyersDiff | the shortest edit script in O((N+M)·D), the algorithm behind git diff, walking the path back through saved frontiers |
| UnifiedDiff | diff -u output: hunks with context, merging nearby changes, @@ -l,s +l,s @@ headers, parsing a diff back |
|
| Patch | applying hunks to a file that changed since the diff was made: nearest-match search, offsets, conflicts | |
| Geometry | Vec / Segments | dot and cross products, orientation, segment intersection without division, point-to-segment distance |
| ConvexHull | Andrew's monotone chain in O(n log n), diameter | |
| Polygon | shoelace area, winding order, centroid, ray-casting containment, convexity | |
| ClosestPair | divide and conquer with a strip check, verified against brute force | |
| GeoDistance | haversine distance, initial bearing, bounding boxes for "within r km" | |
| RectangleUnion | area of overlapping rectangles with a sweep line | |
| Bit manipulation | BitTricks | power-of-two tests, Kernighan popcount, lowest set bit, bit reversal, Gosper's hack, subset enumeration |
| BitSet | 64 flags per ulong, set operations, enumerating with trailing-zero counts, a bit-packed sieve |
|
| GrayCode | sequences where neighbours differ by one bit, encoding and decoding | |
| MortonCode | Z-order curves by interleaving bits, a cheap spatial index | |
| TravellingSalesman | Held–Karp dynamic programming over bit-mask subsets | |
| Range queries | PrefixSums | O(1) range sums in 1D and 2D, counting subarrays with a given sum |
| FenwickTree | binary indexed tree with O(n) build, counting inversions | |
| SegmentTree<T> | bottom-up, iterative, any associative operation, order-preserving for non-commutative ones | |
| SparseTable | O(1) range minimum on static data with overlapping power-of-two windows | |
| DifferenceArray | batched range updates, peak overlap of bookings | |
| SlidingWindow | window maximums with a monotonic deque, longest substring without repeats, shortest window with a sum | |
| Graph algorithms | StronglyConnectedComponents | Tarjan's algorithm in one DFS, condensation |
| BellmanFord | negative weights, finding the negative cycle, currency arbitrage with -log(rate) | |
| FloydWarshall | all-pairs shortest paths, next-hop path reconstruction, graph centre | |
| Bipartite | two-colouring with BFS, maximum matching with augmenting paths | |
| MaxFlow | Edmonds–Karp with residual capacities, the minimum cut | |
| GridPathfinding | A* with a Manhattan heuristic and weighted cells, compared with Dijkstra | |
| CriticalConnections | bridges and articulation points with low-link values | |
| EulerianPath | Hierholzer's algorithm, reconstructing an itinerary from tickets | |
| PrimMst | minimum spanning trees grown from a vertex with a priority queue | |
| GraphColoring | greedy and Welsh–Powell colouring, exact chromatic numbers by backtracking | |
| TransitiveClosure | Warshall's algorithm on BitArray rows, transitive reduction |
|
| LowestCommonAncestor | binary lifting for O(log n) ancestor queries and tree distances | |
| TreeMetrics | tree diameter with two searches, the tree centre | |
| Text algorithms | RabinKarp | rolling hashes mod a Mersenne prime, longest repeated substring by binary search |
| ZFunction | prefix-match lengths in O(n), pattern search, smallest period | |
| Manacher | longest palindromic substring in O(n) | |
| SuffixArray | prefix doubling, Kasai's LCP array, substring search, distinct substrings | |
| AhoCorasick | many patterns in one pass with failure links, a word censor | |
| Soundex | phonetic codes so "Robert" matches "Rupert" | |
| WordWrap | greedy line breaking vs. minimum raggedness with DP | |
| Randomness | Xoshiro256 | a seeded PRNG, SplitMix64 seeding, unbiased bounded integers with Lemire's method |
| Shuffling | Fisher–Yates vs. the biased naive shuffle, sampling without replacement | |
| ReservoirSampler<T> | uniform samples from a stream of unknown length, weighted reservoir sampling | |
| AliasTable<T> | Vose's alias method: O(1) weighted draws | |
| MonteCarlo | estimating π, the birthday paradox, Monty Hall | |
| Statistics | RunningStats | Welford's one-pass variance, merging partial results, why E[x²] - E[x]² fails |
| StreamingMedian | median of a stream with two heaps | |
| Percentiles | nearest-rank vs. interpolated percentiles, quickselect | |
| HyperLogLog | counting distinct items in 4 KB, merging sketches | |
| CountMinSketch | approximate counts that never underestimate, heavy hitters | |
| LinearRegression | least squares fit, R², correlation | |
| MovingAverages | simple and exponential moving averages, half-life decay for irregular samples | |
| Linear algebra | Matrix | immutable row-major matrix, cache-friendly multiplication order |
| GaussianElimination | solving systems with partial pivoting, determinant, Gauss–Jordan inverse | |
| MatrixPower | exponentiation by squaring for Fibonacci, Tribonacci and counting walks | |
| GridTransforms | in-place rotation, spiral order, searching a sorted matrix | |
| Transform2D | affine transforms in homogeneous coordinates, composition, inverse, rotating around a point | |
| PageRank | power iteration with damping and dead ends | |
| Reliability | TokenBucket | burst-friendly rate limiting with lazy refill on TimeProvider, Retry-After |
| CircuitBreaker | closed → open → half-open with a single trial call, state change events | |
| Bulkhead | capping concurrency plus a queue, rejecting the rest immediately | |
| LoadBalancer | smooth weighted round-robin, health checks, power of two choices | |
| TimingWheel | O(1) timeouts in hashed slots with rotation counts | |
| CronSchedule | five-field cron parsing with lists, ranges and steps, the day-of-month/day-of-week rule | |
| Codecs | Base64Codec | 6-bit groups and padding by hand, URL-safe variant, strict decoding |
| HexDump | hex with nibble arithmetic, an xxd-style dump |
|
| PercentEncoding | RFC 3986 escaping of UTF-8 bytes, + for spaces in forms, query strings |
|
| Utf8Codec | UTF-8 by hand, rejecting overlong forms and surrogates, truncating on character boundaries | |
| RomanNumerals | greedy encoding, parsing that rejects non-canonical numerals | |
| NumberToWords | spelling numbers in English, cheque amounts, ordinals | |
| File formats | Csv | RFC 4180 state machine: quotes, escaped quotes, embedded newlines, headers |
| IniFile | sections, comments, quoted values, ${section.key} references, typed reads with IParsable<T> |
|
| MiniJson | a complete JSON parser and serializer with positioned errors and a nesting limit | |
| SemanticVersion | semver precedence (pre-releases, numeric identifiers), caret ranges | |
| CommandLine | --name value, --name=value, bundled flags, --, shell-style splitting |
|
| HumanReadable | 1.5 GiB vs 1.5 GB, 2h30m durations, both directions |
|
| FixedWidth | column-based records, padding rules, implied decimals with trailing signs | |
| Logfmt | key=value log lines with quoting, escaping and bare flags |
|
| NdJson | streaming JSON Lines with IAsyncEnumerable, skipping and reporting bad lines |
|
| RssFeed | RSS and Atom with LINQ to XML, reading either format | |
| Simulations | GameOfLife | Conway's Life on an unbounded board stored as a set of live cells |
| ElementaryAutomaton | Wolfram rules 30, 90 and 110 | |
| TicTacToe | negamax with alpha-beta pruning, perfect play ends in a draw | |
| Maze | recursive backtracker with an explicit stack, BFS solving, ASCII rendering | |
| FloodFill | iterative paint bucket, counting islands, capturing surrounded regions | |
| QueueSimulation | discrete-event simulation of tellers, compared with M/M/1 queueing theory | |
| Boids | flocking from separation, alignment and cohesion on a wrapping world | |
| EloRating | rating updates by surprise, convergence to true strength | |
| DiceProbability | exact distributions by convolution, "roll 4, keep 3", advantage | |
| SirModel | epidemics, R₀, the peak and herd immunity | |
| PredatorPrey | Lotka–Volterra cycles with RK4 and a conserved quantity | |
| GamblersRuin | random walks with absorbing barriers, the cost of a house edge | |
| Functional programming | Option<T> | an optional value instead of null, Map/Bind/Match, LINQ query syntax |
| Trampoline | deep and mutual recursion in constant stack space | |
| Lens | composable getters and immutable setters for nested records | |
| Zipper<T> | an immutable list with an O(1) cursor | |
| Monoids | IMonoid<T> with static abstract members, parallel folds, Kadane as a monoid |
|
| ParserCombinators | small parsers combined with Or, Many and LINQ into a calculator grammar |
|
| Checksums | Luhn | payment card check digits, card brands, the 09 ↔ 90 blind spot |
| Iban | mod 97 by long division, country lengths, helpful error messages | |
| Isbn | ISBN-10 (mod 11) and ISBN-13 (EAN), converting between them | |
| Damm | a quasigroup table that catches every single error and adjacent swap | |
| Finance | Amortization | annuity payments, a cent-exact repayment schedule, the effect of paying extra |
| CashFlows | NPV, IRR with Newton's method and a bisection fallback, payback period, compounding | |
| Combinatorics | Binomial | exact binomials without factorials, Pascal's triangle, Lucas' theorem mod a prime |
| Permutations | next permutation in place, rank and unrank via factorial base, derangements | |
| Combinations | lazy k-combinations, cartesian products as an odometer, listing change-making ways | |
| Partitions | integer partitions, Stirling numbers of the second kind, Bell numbers | |
| Catalan | balanced brackets and binary tree shapes | |
| InclusionExclusion | counting divisible numbers over bit-mask subsets, surjections, Euler's totient | |
| Fuzzy matching | JaroWinkler | name similarity with transpositions and a prefix bonus |
| DamerauLevenshtein | edit distance counting swaps, a banded version with early exit | |
| NGramSimilarity | Jaccard and Dice over n-grams, a trigram index | |
| BkTree | fuzzy dictionary lookup pruned by the triangle inequality | |
| SpellChecker | Norvig's corrector: candidate edits ranked by word frequency | |
| MinHash | estimating Jaccard similarity of documents from small signatures | |
| Search engine | Tokenizer | lower-casing, accent folding, stop words, a light stemmer, positions |
| InvertedIndex | posting lists, merge intersection, NOT, phrase queries with positions | |
| Ranking | TF-IDF and BM25 with term saturation and length normalisation | |
| QueryParser | a AND (b OR "c d") -e parsed into a tree and evaluated |
|
| Snippets | best matching window with highlighted terms | |
| Autocomplete | prefix ranges by binary search, top-k by popularity | |
| Operating systems | CpuScheduling | FCFS, shortest job first, round-robin, Gantt charts and waiting times |
| PageReplacement | FIFO, LRU, optimal and clock, Belady's anomaly | |
| BankersAlgorithm | deadlock avoidance with safe sequences | |
| DiskScheduling | FCFS, shortest seek, SCAN (elevator), C-SCAN | |
| FreeListAllocator | first, best and worst fit, splitting, coalescing, fragmentation | |
| BuddyAllocator | power-of-two blocks, buddies found by flipping one bit | |
| Networking | Ipv4Network | CIDR parsing, membership, subnetting, summarising a range into blocks |
| RoutingTable | longest prefix match in a binary trie | |
| InternetChecksum | RFC 1071 ones'-complement checksum, incremental updates, an IPv4 header | |
| HttpMessageParser | incremental HTTP/1.1 parsing, chunked bodies, limits, smuggling checks | |
| Backoff | exponential backoff with full, equal and decorrelated jitter, measured spikes | |
| UriTemplate | RFC 6570 expansion with operators and escaping | |
| Distributed systems | LamportClock | logical timestamps and a causally consistent total order |
| VectorClock | detecting concurrent writes, keeping siblings in a replica | |
| Crdts | G-counter, PN-counter, LWW register, observed-remove set | |
| MerkleTree | finding differing blocks in O(log n) comparisons, inclusion proofs | |
| SnowflakeId | time-sorted 64-bit ids without coordination, clock-skew protection | |
| TwoPhaseCommit | prepare and vote, commit or abort everyone, timeouts as no votes | |
| Event sourcing | EventStore | append-only streams, optimistic concurrency, global positions |
| InventoryItem | an aggregate rebuilt from events, decisions separate from state changes | |
| Projections | read models that catch up idempotently, a daily report | |
| Snapshots | loading from a snapshot plus the events after it | |
| Saga | multi-step transactions with compensation in reverse order | |
| Upcasting | converting old event versions step by step when reading | |
| Spatial indexes | QuadTree<T> | adaptive subdivision, range and radius queries |
| KdTree<T> | nearest and k-nearest neighbours with pruning | |
| GeoHash | interleaved lat/long bits as base-32 strings, neighbouring cells | |
| SpatialHashGrid | collision broad phase in near-linear time | |
| IntervalTree<T> | overlap queries with subtree max-end pruning | |
| Image processing | Colors | hex, RGB ↔ HSL, mixing, WCAG contrast ratio and readable text colour |
| Convolution | blur, sharpen, Sobel edges, a summed-area-table box blur | |
| Dithering | threshold vs Floyd–Steinberg vs ordered (Bayer) dithering | |
| Histogram | equalisation, contrast stretch, Otsu's threshold | |
| Resampling | nearest neighbour and bilinear resizing, fitting thumbnails | |
| Netpbm | reading and writing PGM images (ASCII and binary) | |
| Signal processing | Fourier | DFT vs Cooley–Tukey FFT, dominant frequencies, polynomial multiplication |
| Dtmf | generating and decoding phone tones with the Goertzel algorithm | |
| WavFile | writing and reading RIFF/WAVE PCM, skipping unknown chunks | |
| Filters | one-pole low-pass and high-pass, median filter for spikes | |
| PitchDetection | autocorrelation pitch detection, nearest note and cents | |
| Interpreters | ShuntingYard | infix to RPN with associativity, unary minus and functions |
| StackMachine | a bytecode VM with calls and jumps, a two-pass assembler | |
| TinyRegex | Thompson NFA matching without catastrophic backtracking | |
| TinyLisp | reader, eval, closures, let, and tail calls in a loop |
|
| Brainfuck | bracket pre-pairing and run folding | |
| Numerical methods | RootFinding | bisection, Newton and secant, convergence speed compared |
| Integration | trapezoid, Simpson, adaptive Simpson, Monte Carlo | |
| Interpolation | linear, Lagrange (and Runge's phenomenon), natural cubic splines | |
| GradientDescent | numerical gradients, momentum, fitting a line, the Rosenbrock valley | |
| OdeSolvers | Euler, RK4, semi-implicit Euler conserving a spring's energy | |
| SimulatedAnnealing | 2-opt moves with a cooling schedule on a 100-city tour | |
| Security | PasswordHasher | salted PBKDF2, constant-time comparison, rehashing on cost upgrades |
| SignedToken | HS256 JWTs by hand, rejecting alg: none, tampering and expiry |
|
| Totp | RFC 6238 codes, drift window, replay protection, provisioning URIs | |
| PasswordStrength | entropy estimates discounted for common words, repeats and sequences | |
| HtmlSanitizer | allowlisted tags and attributes, safe URL schemes | |
| Commerce | VatCalculator | rounding per line vs per rate, extracting VAT from gross prices |
| TieredPricing | volume vs graduated tiers, price cliffs | |
| Proration | plan changes mid-cycle by actual days, partial first periods | |
| InventoryReservations | holding stock with expiry, confirming and releasing | |
| CurrencyConverter | triangulated rates, minor units per currency, spreads | |
| Data processing | Joins | nested loop, hash and merge joins, left outer joins |
| ExternalSort | sorted runs on disk merged with a lazy, stable k-way merge | |
| PivotTable<TRow> | rows × columns aggregation with totals and a text rendering | |
| TimeSeries | resampling into buckets, zero / forward / interpolated gap filling, counter resets | |
| WindowFunctions | ROW_NUMBER, RANK, DENSE_RANK, LAG, running totals, moving averages | |
| SnapshotDiff | added, removed and modified records between two exports, field by field | |
| Property-based testing | Gen<T> | composable random generators with shrinkers |
| Property | checking properties over generated inputs, shrinking counterexamples, replayable seeds | |
| Text formatting | CaseConverter | camel, Pascal, snake, kebab and title case, acronyms like HTTPResponse |
| NaturalComparer | "file2" before "file10", digit runs of any length | |
| Glob | *, **, ? and [a-z] path matching without exponential backtracking |
|
| TextTable | aligned console tables, numbers right-aligned, truncation | |
| Pluralizer | English plural rules and exceptions, "1 file" / "2 files" | |
| MiniMarkdown | a safe Markdown subset to HTML: escaping first, protected code spans | |
| Puzzles | TowerOfHanoi | recursive and iterative solutions, a move validator |
| WordLadder | BFS over words bucketed by wildcard patterns | |
| KnightsTour | Warnsdorff's heuristic with a little backtracking | |
| SlidingPuzzle | the 8-puzzle solved optimally with A*, solvability by inversion parity | |
| WaterJugs | state-space BFS, possibility by gcd | |
| Josephus | simulation, the O(n) recurrence, the bit-rotation closed form | |
| Ciphers | Caesar | shifting letters, cracked with chi-squared letter frequencies |
| Vigenere | key length from the index of coincidence, then per-column Caesar attacks | |
| ToyRsa | RSA from the maths up (and why textbook RSA is insecure) | |
| DiffieHellman | key agreement over a safe prime, HKDF, the real thing with ECDH | |
| SecretSharing | Shamir's k-of-n sharing over a prime field | |
| AuthenticatedEncryption | AES-GCM done right: fresh nonces, associated data, tamper detection | |
| Machine learning | KMeans | clustering with k-means++ initialisation, the inertia elbow |
| NearestNeighbors<TLabel> | k-NN classification with feature standardisation | |
| NaiveBayes | a spam filter with log probabilities and Laplace smoothing | |
| LogisticRegression | gradient descent on cross-entropy, precision and recall | |
| DecisionTree | Gini splits, depth limits, rules you can read | |
| Perceptron | why XOR needs a hidden layer, and a tiny backpropagation network that learns it | |
| Web | RouteMatcher | route templates with constraints, optional and catch-all parameters, most-specific-wins, link generation |
| ContentNegotiation | Accept and Accept-Language with q-values and specificity |
|
| ConditionalRequests | ETags: 304 Not Modified, 412 for lost updates, 428 to require them | |
| Pagination | cursor vs offset paging under inserts, opaque cursors, Link headers |
|
| CorsPolicy | origins, preflights, credentials and Vary |
|
| Cookies | Cookie and Set-Cookie parsing, secure defaults, Max-Age vs Expires |
|
| UrlShortener | base-62 codes from a scrambled counter, aliases, click counts | |
| Accounting | Ledger | double-entry bookkeeping, trial balance, the accounting equation |
| Depreciation | straight-line, declining balance (with the switch), sum of the years' digits | |
| InventoryCosting | FIFO, LIFO and weighted average cost of goods sold | |
| Reconciliation | matching bank lines to the books by reference, then by amount and date | |
| AgingReport | receivables by days overdue, bad debt allowance, DSO | |
| Planning | CriticalPath | earliest and latest starts, slack, the critical chain |
| BinPacking | next fit, first fit (decreasing), exact branch and bound | |
| ConstraintSolver | backtracking with MRV and forward checking: map colouring, exam timetables | |
| Assignment | the Hungarian algorithm in O(n³), checked against brute force | |
| Recommendations | CollaborativeFiltering | user similarity on mean-centred ratings, predicted ratings |
| AssociationRules | Apriori frequent itemsets, rules with confidence and lift | |
| RankingFormulas | Hacker News gravity, Reddit hot, Wilson lower bound, Bayesian averages | |
| Diagnostics | StructuredLogger | message templates with named properties, scopes on AsyncLocal |
| Metrics | counters, gauges, bucketed histograms with percentile estimates, Prometheus output | |
| Tracing | span trees across awaits, traceparent propagation |
|
| HealthChecks | parallel checks with timeouts, critical vs degraded | |
| CorrelationContext | how AsyncLocal flows down but not back up, suppressing flow |
|
| LogDeduplicator | "last message repeated N times" instead of log floods |
csharp-exercises/
├── src/CSharpExercises/ # exercise implementations, one folder per topic
├── src/CSharpExercises.Api/ # ASP.NET Core minimal API built on the exercises
├── tests/CSharpExercises.Tests/ # xUnit unit and integration tests
└── benchmarks/CSharpExercises.Benchmarks/ # BenchmarkDotNet performance comparisons
Requires the .NET 10 SDK.
git clone https://github.com/dejanciev-dev/csharp-exercises.git
cd csharp-exercises
dotnet testCSharpExercises.Api puts the exercises together in an ASP.NET Core minimal API:
| Endpoint | Uses |
|---|---|
POST /api/sort |
bubble / merge / quick sort |
POST /api/routes/shortest |
Dijkstra (with the hand-written MinHeap<T>) |
GET /api/coins |
dynamic programming coin change |
GET /api/text/edit-distance, /lcs, /palindrome |
dynamic programming and string exercises |
GET / POST /api/shapes |
shape factory (injected via DI) |
Along the way it shows input validation with ProblemDetails responses, limits that stop expensive algorithms from being abused (bubble sort is capped at 5,000 numbers), rate limiting with the SlidingWindowRateLimiter from the testing exercises (429 + Retry-After), options bound from appsettings.json, and an OpenAPI document.
dotnet run --project src/CSharpExercises.Api
curl -X POST http://localhost:5028/api/sort -H "Content-Type: application/json" \
-d '{"algorithm":"quick","numbers":[5,2,9,1]}'
# {"algorithm":"quick","count":4,"sorted":[1,2,5,9]}More sample requests are in CSharpExercises.Api.http, and the OpenAPI document is at /openapi/v1.json in Development. Integration tests run the whole app in memory with WebApplicationFactory, swapping in a fake clock to test rate limiting without waiting.
The benchmarks project uses BenchmarkDotNet to compare the hand-written sorts against Array.Sort and LINQ, and the Span<T> CSV parser against string.Split.
# quick run (a few minutes); drop --job short for full statistical precision
dotnet run -c Release --project benchmarks/CSharpExercises.Benchmarks -- --filter '*' --job short
# just one class
dotnet run -c Release --project benchmarks/CSharpExercises.Benchmarks -- --filter '*Csv*'| Method | Random | Already sorted | Reversed | Allocated |
|---|---|---|---|---|
Array.Sort (baseline) |
0.67 ms | 0.07 ms | 0.18 ms | 39 KB |
LINQ Order() |
0.64 ms | 0.08 ms | 0.18 ms | 39 KB |
| QuickSort | 0.94 ms | 0.15 ms | 0.16 ms | 39 KB |
| MergeSort | 2.32 ms | 1.18 ms | 1.56 ms | 2,133 KB |
| BubbleSort | 145.9 ms | 0.01 ms | 93.8 ms | 39 KB |
What the numbers show:
- Big-O wins at scale. Bubble sort is O(n²): about 200× slower than
Array.Sorton 10,000 random items, but only 10× slower on 100. - Best cases matter. Bubble sort stops after one pass when nothing swaps, so on already-sorted input it is the fastest of all.
- Allocations cost time. Merge sort creates new arrays at every level of recursion (2 MB vs 39 KB), which is the main reason it trails quick sort.
- Benchmarking finds bugs. The first version of
QuickSortalways picked the last element as pivot. On sorted input that is its worst case, and it crashed with a stack overflow at 10,000 items. It now uses a middle pivot with Hoare partitioning and recurses only into the smaller half.
| Method | 10 fields | 1,000 fields | Allocated (1,000 fields) |
|---|---|---|---|
string.Split + int.Parse |
438 ns | 61.4 µs | 70 KB |
SumCsv with ReadOnlySpan<char> |
239 ns | 33.4 µs | 0 B |
Slicing spans instead of creating substrings makes parsing about 45% faster and allocation-free.
Measured with BenchmarkDotNet 0.15.8 --job short on .NET 10, Intel Core i7-6700HQ, Windows 10. Short runs are indicative; absolute numbers will differ on your machine.
Ideas for upcoming exercises:
- Async: parallel downloads with
Task.WhenAll, cancellation tokens,IAsyncEnumerable - Data structures: linked list, binary search tree, min-heap / priority queue, graph with BFS & DFS
- Algorithms: dynamic programming (coin change, longest common subsequence), Dijkstra
- Design patterns: strategy, observer, factory, decorator, builder
- Modern C#: records with
withexpressions, list patterns, generic math (INumber<T>),Span<T> - Benchmarks: comparing the sorting algorithms with BenchmarkDotNet
- LINQ internals: rebuilding
Where,Select,OrderBy,GroupByand friends withyield - Dependency injection: constructor injection, lifetimes,
Microsoft.Extensions.DependencyInjection - Testing techniques: test doubles and mocks, test data builders, testing time with
TimeProvider - Putting it together: a small console app or web API built on these exercises
- Concurrency: race conditions,
lockvsInterlocked, a blocking queue withMonitor, run-once caching - Error handling: a
Result<T>type, validation that collects every error, exceptions that are easy to lose - Reflection: custom attributes, expression trees, discovering plugins in assemblies
- Serialization:
System.Text.Jsoncustom converters, polymorphism and source generation - Resource management:
IDisposableandIAsyncDisposabledone right, object and array pooling - Delegates & events: function composition and memoization, event pitfalls, a middleware pipeline
- Parsing: a tokenizer and recursive-descent parser for arithmetic, evaluating and simplifying the syntax tree
- Generics: variance, static generic caches, constraints with static abstract interface members
- More data structures & algorithms: trie, union-find, ring buffer, Kruskal, backtracking, a Sudoku solver
- State machines: table-driven with guards, a document workflow, a pure event-driven vending machine
- Dates & times:
DateOnlycalendar math, business days, DST-safe time zone conversion, recurring schedules - Text processing: source-generated regex, a template engine, Unicode grapheme clusters, KMP search
- Hashing: stable hashes, a hash map from scratch, Bloom filters, consistent hashing
- Caching: expiration with
TimeProvider, stampede protection, cache-aside with negative caching - Immutable data: persistent collections with structural sharing, snapshot-based undo, what "read-only" really means
- I/O & binary data: a versioned file format with checksums, endianness, stream decorators, streaming processing
- Numerics: floating-point pitfalls, exact rationals, modular arithmetic and primality testing
- Domain modelling: strongly typed ids, value objects, money allocation, the specification pattern
- Async coordination: async locks and events, async lazy initialisation, a lock-free stack
- Compression & encoding: varints with ZigZag and deltas, Huffman coding, human-friendly Base32 with check symbols
- Diffing: the Myers diff algorithm, unified diff output, applying patches that have drifted
- Geometry & bits: convex hulls, polygons, closest pair, haversine, bit tricks, Gray and Morton codes, Held–Karp
- Range queries: prefix sums, Fenwick and segment trees, sparse tables, sliding windows
- Graph & text algorithms: SCCs, Bellman–Ford, Floyd–Warshall, max flow, A*, suffix arrays, Aho–Corasick, Manacher
- More DP & randomness: knapsack, LIS, Kadane, interval scheduling, Fisher–Yates, reservoir sampling, alias tables
- Statistics & linear algebra: Welford, HyperLogLog, count-min, regression, Gaussian elimination, PageRank
- Reliability: token bucket, circuit breaker, bulkhead, load balancing, timing wheels, cron
- Codecs & formats: Base64, UTF-8, percent-encoding, CSV, INI, JSON, semver, command lines
- Simulations: Game of Life, cellular automata, minimax, mazes, flood fill, queueing
- Functional programming: Option, trampolines, lenses, zippers, monoids, parser combinators
- More patterns & collections: command, visitor, composite, proxy, mediator; deque, skip list, LFU, AVL tree
- Checksums & finance: Luhn, IBAN, ISBN, Damm, loan amortization, NPV and IRR
- Combinatorics & fuzzy matching: binomials, permutations, partitions, Catalan; Jaro–Winkler, BK-trees, MinHash
- Search engine: tokenizing and stemming, inverted index, BM25, boolean queries, snippets, autocomplete
- Operating systems & networking: scheduling, paging, allocators; CIDR, routing, checksums, HTTP parsing
- Distributed systems & event sourcing: logical clocks, CRDTs, Merkle trees, 2PC; event store, projections, sagas
- Spatial, imaging & signals: quadtrees, k-d trees, geohash; filters, dithering; FFT, DTMF, pitch detection
- Interpreters & numerical methods: a VM, a regex engine, a Lisp; root finding, integration, ODEs, annealing
- Security & commerce: password hashing, JWT, TOTP, sanitizing; VAT, tiered pricing, proration, FX
- Data processing & property testing: joins, external sort, pivots, window functions; generators and shrinking
- More dates, collections & concurrency: RRULEs, SLAs, ISO weeks; rope, bag, decrease-key heap; actors, barriers, lock striping
- Text formatting & puzzles: case conversion, natural sort, globs, Markdown; Hanoi, word ladders, the 8-puzzle
- More graphs, DP & compression: Euler paths, colouring, LCA; egg drop, regex DP, stock trading; LZ77, LZW, BWT
- Ciphers & machine learning: Caesar/Vigenère attacks, RSA, Shamir, AES-GCM; k-means, naive Bayes, decision trees
- Web & patterns: routing, content negotiation, ETags, cursors, CORS, cookies; memento, state, flyweight, bridge
- Accounting, formats, async & numerics: ledgers, depreciation; logfmt, NDJSON, RSS; batching, keyed locks; fixed point, continued fractions
- Testing, reflection, planning, recommendations & diagnostics: snapshot and mutation testing; mappers and diffs; CPM, Hungarian; Apriori; logging, metrics, tracing
Dejan Ciev · GitHub @dejanciev-dev
If you find this useful, consider giving the repo a ⭐