Practice

C# exercises

Make a change, predict what it will do, and compare the result.

  1. 1

    C# Basics

    Print a value from a .NET program.

    Adapt the lesson example. Change one value or element, predict the result, and explain why it changes.

    Show starting example
    C#
    using System;
    class Program {
      static void Main() {
        string name = "Ada";
        Console.WriteLine($"Hello, {name}");
      }
    }
  2. 2

    C# Loops

    Repeat an action with a for loop.

    Adapt the lesson example. Change one value or element, predict the result, and explain why it changes.

    Show starting example
    C#
    using System;
    class Program {
      static void Main() {
        for (int i = 1; i <= 3; i++) {
          Console.WriteLine(i);
        }
      }
    }
  3. 3

    C# Methods

    Package reusable logic in a static method.

    Predict Square(-3).

    Reveal answer and reasoning

    A negative times a negative is positive, so it returns 9.

    Show starting example
    C#
    using System;
    
    class Program {
      static int Square(int n) {
        return n * n;
      }
    
      static void Main() {
        Console.WriteLine(Square(4));
        Console.WriteLine(Square(Square(2)));
      }
    }
  4. 4

    C# strings and formatting

    Use common string methods, compare text correctly, and format numbers with format specifiers.

    How would you print 7 as 007?

    Reveal answer and reasoning

    Use the D3 format specifier: 7.ToString("D3") gives 007.

    Show starting example
    C#
    using System;
    using System.Text;
    
    class Program {
      static void Main() {
        string name = "Programonic";
        Console.WriteLine(name.Length + " " + name.Substring(0, 7) + " " + name.IndexOf('m'));
        Console.WriteLine(name.ToUpper() + " " + name.Replace("o", "0"));
    
        string[] parts = "a,b,,c".Split(',');
        Console.WriteLine(parts.Length + " " + string.Join("-", parts));
        Console.WriteLine("  padded ".Trim().PadLeft(8, '*'));
    
        Console.WriteLine("apple" == "APPLE");
        Console.WriteLine(string.Equals("apple", "APPLE", StringComparison.OrdinalIgnoreCase));
    
        double price = 1234.56;
        Console.WriteLine(FormattableString.Invariant($"{price:F2} {price:N0} {42:D5} {255:X}"));
    
        var builder = new StringBuilder();
        for (int i = 1; i <= 3; i++) {
          builder.Append(i).Append(i < 3 ? "," : "");
        }
        Console.WriteLine(builder);
      }
    }
  5. 5

    C# exceptions, finally, and using

    Throw and catch a custom exception, filter by condition, and release resources reliably.

    What happens if the filter is e.Missing > 100?

    Reveal answer and reasoning

    The filter is false, so this catch does not run and the exception propagates to the caller. finally still runs.

    Show starting example
    C#
    using System;
    
    class InsufficientFundsException : Exception {
      public int Missing { get; }
    
      public InsufficientFundsException(int missing) : base("short by " + missing) {
        Missing = missing;
      }
    }
    
    class Connection : IDisposable {
      public void Dispose() {
        Console.WriteLine("connection closed");
      }
    }
    
    class Program {
      static int Withdraw(int balance, int amount) {
        if (amount > balance) {
          throw new InsufficientFundsException(amount - balance);
        }
        return balance - amount;
      }
    
      static void Main() {
        try {
          using (var connection = new Connection()) {
            Console.WriteLine("working");
            Withdraw(50, 80);
            Console.WriteLine("not reached");
          }
        } catch (InsufficientFundsException e) when (e.Missing > 10) {
          Console.WriteLine("caught: " + e.Message);
        } finally {
          Console.WriteLine("finally");
        }
    
        if (!int.TryParse("12x", out int value)) {
          Console.WriteLine("TryParse failed without an exception");
        }
      }
    }
  6. 6

    C# nullable values, patterns, and switch expressions

    Handle missing values safely and branch on the type and value of an object.

    What does maybe.GetValueOrDefault() return when maybe is null?

    Reveal answer and reasoning

    0, the default value of int. GetValueOrDefault(5) would return 5.

    Show starting example
    C#
    using System;
    using System.Globalization;
    
    class Program {
      static (int Min, int Max) MinMax(int[] values) {
        int min = values[0], max = values[0];
        foreach (int v in values) {
          if (v < min) min = v;
          if (v > max) max = v;
        }
        return (min, max);
      }
    
      static void Main() {
        int? maybe = null;
        Console.WriteLine(maybe.HasValue + " " + (maybe ?? -1));
        string text = null;
        Console.WriteLine(text?.Length ?? 0);
    
        object[] things = { 42, 7, "hello", 3.5, null, new[] { 1, 2, 3 } };
        foreach (object thing in things) {
          string description = thing switch {
            int n when n > 40 => "big int " + n,
            int n => "int " + n,
            string s => "string of length " + s.Length,
            double x => "double " + x.ToString(CultureInfo.InvariantCulture),
            null => "null",
            int[] array => "array with " + array.Length + " items",
            _ => "something else",
          };
          Console.WriteLine(description);
        }
    
        var (min, max) = MinMax(new[] { 4, 9, 2, 7 });
        Console.WriteLine(min + " " + max);
      }
    }
  7. 7

    C# Classes and Properties

    Protect state with a property that only the class can change.

    Add a Reset method that sets Value back to 0.

    Reveal answer and reasoning

    public void Reset() { Value = 0; } is allowed because it is inside the class.

    Show starting example
    C#
    using System;
    
    class Counter {
      public int Value { get; private set; }
    
      public void Increment() {
        Value++;
      }
    }
    
    class Program {
      static void Main() {
        var counter = new Counter();
        counter.Increment();
        counter.Increment();
        Console.WriteLine(counter.Value);
      }
    }
  8. 8

    C# inheritance, virtual, and abstract members

    Override behaviour in subclasses and call it through a base-type reference.

    How would you add an Intern with a fixed pay?

    Reveal answer and reasoning

    Create class Intern : Employee with an override of MonthlyPay returning the fixed amount. The loop in Main needs no change.

    Show starting example
    C#
    using System;
    
    abstract class Employee {
      public string Name { get; }
    
      protected Employee(string name) {
        Name = name;
      }
    
      public abstract int MonthlyPay();
    
      public virtual string Title {
        get { return "Employee"; }
      }
    
      public override string ToString() {
        return Title + " " + Name + ": " + MonthlyPay();
      }
    }
    
    class Salaried : Employee {
      private readonly int yearly;
    
      public Salaried(string name, int yearly) : base(name) {
        this.yearly = yearly;
      }
    
      public override int MonthlyPay() {
        return yearly / 12;
      }
    }
    
    class Hourly : Employee {
      private readonly int rate;
      private readonly int hours;
    
      public Hourly(string name, int rate, int hours) : base(name) {
        this.rate = rate;
        this.hours = hours;
      }
    
      public override int MonthlyPay() {
        return rate * hours;
      }
    }
    
    sealed class Manager : Salaried {
      private readonly int bonus;
    
      public Manager(string name, int yearly, int bonus) : base(name, yearly) {
        this.bonus = bonus;
      }
    
      public override string Title {
        get { return "Manager"; }
      }
    
      public override int MonthlyPay() {
        return base.MonthlyPay() + bonus;
      }
    }
    
    class Program {
      static void Main() {
        Employee[] staff = {
          new Salaried("Ada", 60000),
          new Hourly("Bo", 20, 160),
          new Manager("Cy", 96000, 500),
        };
        int total = 0;
        foreach (Employee e in staff) {
          Console.WriteLine(e);
          total += e.MonthlyPay();
        }
        Console.WriteLine("total " + total);
      }
    }
  9. 9

    C# records, with-expressions, and value semantics

    Create immutable data types with value equality, copy them with changes, and compare with structs and classes.

    What does a == c give?

    Reveal answer and reasoning

    False, because the Y values (2 and 5) differ.

    Show starting example
    C#
    using System;
    
    record Point(int X, int Y);
    
    struct Counter {
      public int Value;
    }
    
    class Box {
      public int Value;
    }
    
    class Program {
      static void Main() {
        var a = new Point(1, 2);
        var b = new Point(1, 2);
        Console.WriteLine(a == b);
        Console.WriteLine(ReferenceEquals(a, b));
        Console.WriteLine(a);
    
        var c = a with { Y = 5 };
        Console.WriteLine(c + " " + a);
    
        var (x, y) = c;
        Console.WriteLine(x + " " + y);
    
        var s1 = new Counter { Value = 1 };
        var s2 = s1;
        s2.Value = 99;
        var c1 = new Box { Value = 1 };
        var c2 = c1;
        c2.Value = 99;
        Console.WriteLine("struct copy: " + s1.Value + ", class alias: " + c1.Value);
      }
    }
  10. 10

    C# delegates, events, and lambdas

    Pass behaviour as a value and let objects notify subscribers when something happens.

    Why is the event keyword used instead of a public delegate field?

    Reveal answer and reasoning

    With a plain public delegate, outside code could invoke it or replace all subscribers with =. An event only allows += and -=, so the publisher stays in control.

    Show starting example
    C#
    using System;
    
    class Button {
      public event Action<string> Clicked;
    
      public void Click(string user) {
        Clicked?.Invoke(user);
      }
    }
    
    class Program {
      static void Main() {
        Func<int, int> square = x => x * x;
        Func<int, int> addOne = x => x + 1;
        Func<int, int> squareThenAddOne = x => addOne(square(x));
        Console.WriteLine(square(5) + " " + squareThenAddOne(5));
    
        var button = new Button();
        int clicks = 0;
        Action<string> counter = user => clicks++;
        Action<string> greeter = user => Console.WriteLine("hello " + user);
    
        button.Clicked += counter;
        button.Clicked += greeter;
        button.Click("Ada");
        button.Clicked -= greeter;
        button.Click("Bo");
        Console.WriteLine("clicks: " + clicks);
      }
    }
  11. 11

    C# Lists

    Grow a collection with List<T> and add its values.

    Print the largest score without using LINQ.

    Reveal answer and reasoning

    Keep int max = scores[0] and replace it inside the loop whenever score > max. It prints 95.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    
    class Program {
      static void Main() {
        var scores = new List<int> { 82, 91, 76 };
        scores.Add(95);
        int total = 0;
        foreach (int score in scores) {
          total += score;
        }
        Console.WriteLine(scores.Count);
        Console.WriteLine(total);
      }
    }
  12. 12

    C# Dictionary and HashSet operations

    Count words with a dictionary and combine sets with union, intersection, and difference.

    How would you test whether a is a subset of b?

    Reveal answer and reasoning

    Call a.IsSubsetOf(b), which returns true when every element of a is also in b.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    class Program {
      static void Main() {
        string text = "the quick brown fox jumps over the lazy dog the end";
        var counts = new Dictionary<string, int>();
        foreach (string word in text.Split(' ')) {
          counts[word] = counts.TryGetValue(word, out int seen) ? seen + 1 : 1;
        }
        foreach (var pair in counts.OrderByDescending(p => p.Value).ThenBy(p => p.Key).Take(3)) {
          Console.WriteLine(pair.Key + ": " + pair.Value);
        }
        Console.WriteLine("distinct words: " + counts.Count);
    
        var a = new HashSet<int> { 1, 2, 3, 4 };
        var b = new HashSet<int> { 3, 4, 5 };
        var inter = new HashSet<int>(a);
        inter.IntersectWith(b);
        var union = new HashSet<int>(a);
        union.UnionWith(b);
        var diff = new HashSet<int>(a);
        diff.ExceptWith(b);
        Console.WriteLine("intersection " + string.Join(" ", inter.OrderBy(x => x)));
        Console.WriteLine("union " + string.Join(" ", union.OrderBy(x => x)));
        Console.WriteLine("difference " + string.Join(" ", diff.OrderBy(x => x)));
      }
    }
  13. 13

    C# Stack and Queue: evaluating postfix expressions

    Use a stack to evaluate expressions written in reverse Polish notation and a queue for first-in order.

    Write 2 + 3 * 4 in postfix.

    Reveal answer and reasoning

    2 3 4 * +: multiply 3 and 4 first, then add 2, which gives 14.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    
    class Program {
      static int EvalPostfix(string expression) {
        var stack = new Stack<int>();
        foreach (string token in expression.Split(' ')) {
          if (int.TryParse(token, out int number)) {
            stack.Push(number);
          } else {
            int right = stack.Pop();
            int left = stack.Pop();
            stack.Push(token switch {
              "+" => left + right,
              "-" => left - right,
              "*" => left * right,
              "/" => left / right,
              _ => throw new ArgumentException("bad operator " + token),
            });
          }
        }
        return stack.Pop();
      }
    
      static void Main() {
        Console.WriteLine(EvalPostfix("3 4 + 2 *"));
        Console.WriteLine(EvalPostfix("5 1 2 + 4 * + 3 -"));
        try {
          EvalPostfix("2 3 ^");
        } catch (ArgumentException e) {
          Console.WriteLine(e.Message);
        }
    
        var line = new Queue<string>();
        line.Enqueue("Ada");
        line.Enqueue("Bo");
        line.Enqueue("Cy");
        Console.WriteLine(line.Dequeue() + " served, " + line.Count + " waiting, next is " + line.Peek());
      }
    }
  14. 14

    C# sorting, stability, and Array.BinarySearch

    Sort arrays and records, keep equal items in order with LINQ, and find insertion points.

    How would you insert 4 so that the array stays sorted?

    Reveal answer and reasoning

    Compute the insertion index ~missing (2) and insert there, for example with List<int>.Insert(index, 4).

    Show starting example
    C#
    using System;
    using System.Linq;
    
    class Program {
      static void Main() {
        int[] numbers = { 5, 2, 9, 1, 5, 6 };
        Array.Sort(numbers);
        Console.WriteLine(string.Join(" ", numbers));
    
        int found = Array.BinarySearch(numbers, 9);
        int missing = Array.BinarySearch(numbers, 4);
        Console.WriteLine("index of 9: " + found);
        Console.WriteLine("4 would be inserted at " + ~missing);
    
        var people = new[] { (Name: "Ada", Age: 30), (Name: "Bo", Age: 25), (Name: "Cy", Age: 30), (Name: "Di", Age: 25) };
        var byAge = people.OrderBy(p => p.Age);
        Console.WriteLine(string.Join(",", byAge.Select(p => p.Name)));
    
        string[] words = { "pear", "Apple", "fig" };
        Array.Sort(words, StringComparer.OrdinalIgnoreCase);
        Console.WriteLine(string.Join(",", words));
      }
    }
  15. 15

    C# LRU cache with Dictionary and LinkedList

    Combine a hash table and a doubly linked list so that get, put, and eviction all take constant time.

    Why not use a List<T> to keep the order of use?

    Reveal answer and reasoning

    Removing an item from the middle of a List<T> shifts the remaining items, which is O(n), so the cache would no longer run in constant time.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    
    class LruCache {
      private readonly int capacity;
      private readonly Dictionary<int, LinkedListNode<(int Key, int Value)>> map = new Dictionary<int, LinkedListNode<(int Key, int Value)>>();
      private readonly LinkedList<(int Key, int Value)> order = new LinkedList<(int Key, int Value)>();
    
      public LruCache(int capacity) {
        this.capacity = capacity;
      }
    
      public int Get(int key) {
        if (!map.TryGetValue(key, out var node)) {
          return -1;
        }
        order.Remove(node);
        order.AddFirst(node);
        return node.Value.Value;
      }
    
      public void Put(int key, int value) {
        if (map.TryGetValue(key, out var existing)) {
          order.Remove(existing);
          map.Remove(key);
        } else if (map.Count == capacity) {
          var oldest = order.Last;
          order.RemoveLast();
          map.Remove(oldest.Value.Key);
          Console.WriteLine("evicted " + oldest.Value.Key);
        }
        var node = order.AddFirst((key, value));
        map[key] = node;
      }
    }
    
    class Program {
      static void Main() {
        var cache = new LruCache(2);
        cache.Put(1, 10);
        cache.Put(2, 20);
        Console.WriteLine(cache.Get(1));
        cache.Put(3, 30);
        Console.WriteLine(cache.Get(2));
        cache.Put(4, 40);
        Console.WriteLine(cache.Get(1) + " " + cache.Get(3) + " " + cache.Get(4));
      }
    }
  16. 16

    C# generic binary search tree with IEnumerable

    Constrain the element type with IComparable<T> and expose the sorted values with yield return.

    How would you make the tree sort strings ignoring case?

    Reveal answer and reasoning

    Accept an IComparer<T> in the constructor and use it instead of CompareTo, for example StringComparer.OrdinalIgnoreCase.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    class Tree<T> where T : IComparable<T> {
      private class Node {
        public T Value;
        public Node Left;
        public Node Right;
      }
    
      private Node root;
    
      public void Insert(T value) {
        root = Insert(root, value);
      }
    
      private Node Insert(Node node, T value) {
        if (node == null) {
          return new Node { Value = value };
        }
        int order = value.CompareTo(node.Value);
        if (order < 0) {
          node.Left = Insert(node.Left, value);
        } else if (order > 0) {
          node.Right = Insert(node.Right, value);
        }
        return node;
      }
    
      public IEnumerable<T> InOrder() {
        return InOrder(root);
      }
    
      private IEnumerable<T> InOrder(Node node) {
        if (node == null) {
          yield break;
        }
        foreach (T value in InOrder(node.Left)) {
          yield return value;
        }
        yield return node.Value;
        foreach (T value in InOrder(node.Right)) {
          yield return value;
        }
      }
    }
    
    class Program {
      static void Main() {
        var numbers = new Tree<int>();
        foreach (int n in new[] { 50, 30, 70, 20, 40, 60, 80, 40 }) {
          numbers.Insert(n);
        }
        Console.WriteLine(string.Join(" ", numbers.InOrder()));
        Console.WriteLine(string.Join(" ", numbers.InOrder().Take(3)));
    
        var words = new Tree<string>();
        foreach (string w in new[] { "pear", "fig", "apple", "kiwi" }) {
          words.Insert(w);
        }
        Console.WriteLine(string.Join(" ", words.InOrder()));
      }
    }
  17. 17

    C#: best and worst case of insertion sort

    Count comparisons on sorted, reversed, and mixed input to see how input order changes the cost.

    How many comparisons does reversed input of 10 elements need?

    Reveal answer and reasoning

    10 · 9 / 2 = 45 comparisons.

    Show starting example
    C#
    using System;
    
    class Program {
      static int Sort(int[] a) {
        int comparisons = 0;
        for (int i = 1; i < a.Length; i++) {
          int key = a[i];
          int j = i - 1;
          while (j >= 0) {
            comparisons++;
            if (a[j] <= key) {
              break;
            }
            a[j + 1] = a[j];
            j--;
          }
          a[j + 1] = key;
        }
        return comparisons;
      }
    
      static void Main() {
        Console.WriteLine("sorted input:   " + Sort(new[] { 1, 2, 3, 4, 5, 6, 7, 8 }));
        Console.WriteLine("reversed input: " + Sort(new[] { 8, 7, 6, 5, 4, 3, 2, 1 }));
        Console.WriteLine("mixed input:    " + Sort(new[] { 4, 8, 2, 7, 1, 6, 3, 5 }));
      }
    }
  18. 18

    C#: watching List<T> grow geometrically

    Observe the capacity of a List<int> as items are added and see that reallocations become rarer.

    Why does capacity jump at 5 items rather than 4?

    Reveal answer and reasoning

    Capacity is 4 after the first Add, so four items fit. The fifth item does not fit, so the array is doubled to 8.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    
    class Program {
      static void Main() {
        var list = new List<int>();
        int last = list.Capacity;
        Console.WriteLine("capacity " + last + " after 0 items");
        for (int i = 1; i <= 33; i++) {
          list.Add(i);
          if (list.Capacity != last) {
            last = list.Capacity;
            Console.WriteLine("capacity " + last + " after " + i + " items");
          }
        }
      }
    }
  19. 19

    C#: relational queries with LINQ join and group

    Join two collections on a key, sort the result, and aggregate it by group, as SQL does.

    How would you keep Di with a missing department, like a SQL LEFT JOIN?

    Reveal answer and reasoning

    Use a group join with DefaultIfEmpty: join ... into matches, then from d in matches.DefaultIfEmpty(), and handle the null department.

    Show starting example
    C#
    using System;
    using System.Linq;
    
    class Program {
      static void Main() {
        var departments = new[] { new { Id = 1, Title = "Eng" }, new { Id = 2, Title = "Ops" } };
        var staff = new[] {
          new { Name = "Ada", Dept = 1, Salary = 120 },
          new { Name = "Bo", Dept = 2, Salary = 90 },
          new { Name = "Cy", Dept = 1, Salary = 100 },
          new { Name = "Di", Dept = 3, Salary = 80 },
        };
    
        var joined = (from e in staff
                      join d in departments on e.Dept equals d.Id
                      orderby e.Name
                      select new { e.Name, d.Title, e.Salary }).ToList();
        foreach (var row in joined) {
          Console.WriteLine(row.Name + " " + row.Title + " " + row.Salary);
        }
    
        var totals = joined.GroupBy(r => r.Title)
                           .Select(g => g.Key + ": " + g.Sum(r => r.Salary))
                           .OrderBy(text => text);
        foreach (var line in totals) {
          Console.WriteLine(line);
        }
      }
    }
  20. 20

    C#: an ordered index with SortedSet

    Answer range, minimum, maximum, and nearest-value queries on sorted keys.

    How would you list the ages greater than 30?

    Reveal answer and reasoning

    Use ages.GetViewBetween(31, int.MaxValue); it contains 31 and 45.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    
    class Program {
      static void Main() {
        var ages = new SortedSet<int> { 31, 24, 45, 29 };
    
        Console.WriteLine("min " + ages.Min + " max " + ages.Max);
        Console.WriteLine("range 25..40: " + string.Join(" ", ages.GetViewBetween(25, 40)));
    
        int floor = ages.GetViewBetween(int.MinValue, 30).Max;
        int ceiling = ages.GetViewBetween(30, int.MaxValue).Min;
        Console.WriteLine("floor(30)=" + floor + " ceiling(30)=" + ceiling);
      }
    }
  21. 21

    C#: first-come-first-served versus shortest-job-first

    Compare the average waiting time of two scheduling policies for the same set of jobs.

    What is the average SJF wait for bursts 3, 1, 2?

    Reveal answer and reasoning

    Sorted order 1, 2, 3 gives waits 0, 1, and 3, so the average is 4 / 3 ≈ 1.33.

    Show starting example
    C#
    using System;
    using System.Globalization;
    using System.Linq;
    
    class Program {
      static double AverageWait(int[] bursts) {
        int clock = 0, totalWait = 0;
        foreach (int burst in bursts) {
          totalWait += clock;
          clock += burst;
        }
        return (double)totalWait / bursts.Length;
      }
    
      static void Main() {
        int[] bursts = { 8, 4, 9, 5 };
        Console.WriteLine("FCFS average wait: " + AverageWait(bursts).ToString("F2", CultureInfo.InvariantCulture));
        int[] shortestFirst = bursts.OrderBy(b => b).ToArray();
        Console.WriteLine("SJF  average wait: " + AverageWait(shortestFirst).ToString("F2", CultureInfo.InvariantCulture));
      }
    }
  22. 22

    C#: protecting shared state with lock

    Start two threads that update one counter and use lock so that no update is lost.

    Which class makes a single increment atomic without a lock?

    Reveal answer and reasoning

    System.Threading.Interlocked: Interlocked.Increment(ref counter) performs the update atomically.

    Show starting example
    C#
    using System;
    using System.Threading;
    
    class Program {
      static int counter = 0;
      static readonly object gate = new object();
    
      static void Work() {
        for (int i = 0; i < 100000; i++) {
          lock (gate) {
            counter++;
          }
        }
      }
    
      static void Main() {
        var a = new Thread(Work);
        var b = new Thread(Work);
        a.Start();
        b.Start();
        a.Join();
        b.Join();
        Console.WriteLine("counter=" + counter);
      }
    }
  23. 23

    C#: a TCP echo server on the loopback interface

    Use TcpListener and TcpClient with async methods to send a line and read the reply.

    How would the server serve many clients?

    Reveal answer and reasoning

    Loop on AcceptTcpClientAsync and start a new task for every accepted client.

    Show starting example
    C#
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading.Tasks;
    
    class Program {
      static async Task Main() {
        var listener = new TcpListener(IPAddress.Loopback, 0);
        listener.Start();
        int port = ((IPEndPoint)listener.LocalEndpoint).Port;
    
        Task server = Task.Run(async () => {
          using TcpClient accepted = await listener.AcceptTcpClientAsync();
          using var reader = new StreamReader(accepted.GetStream());
          using var writer = new StreamWriter(accepted.GetStream()) { AutoFlush = true };
          string line = await reader.ReadLineAsync();
          await writer.WriteLineAsync("echo: " + line);
        });
    
        using (var client = new TcpClient()) {
          await client.ConnectAsync(IPAddress.Loopback, port);
          using var reader = new StreamReader(client.GetStream());
          using var writer = new StreamWriter(client.GetStream()) { AutoFlush = true };
          await writer.WriteLineAsync("hello");
          Console.WriteLine(await reader.ReadLineAsync());
        }
        await server;
        listener.Stop();
      }
    }
  24. 24

    C#: IPv4 subnets with IPAddress and bit masks

    Compute the network and broadcast addresses for a CIDR block and test whether addresses belong to it.

    How many usable hosts are in a /24?

    Reveal answer and reasoning

    2^8 − 2 = 254 usable host addresses.

    Show starting example
    C#
    using System;
    using System.Net;
    
    class Program {
      static uint ToNumber(string text) {
        byte[] b = IPAddress.Parse(text).GetAddressBytes();
        return ((uint)b[0] << 24) | ((uint)b[1] << 16) | ((uint)b[2] << 8) | b[3];
      }
    
      static string ToText(uint v) {
        return new IPAddress(new[] { (byte)(v >> 24), (byte)(v >> 16), (byte)(v >> 8), (byte)v }).ToString();
      }
    
      static void Main() {
        int prefix = 26;
        uint mask = prefix == 0 ? 0 : uint.MaxValue << (32 - prefix);
        uint network = ToNumber("192.168.10.77") & mask;
        uint broadcast = network | ~mask;
        Console.WriteLine("network   " + ToText(network));
        Console.WriteLine("broadcast " + ToText(broadcast));
        Console.WriteLine("hosts     " + ((1L << (32 - prefix)) - 2));
        foreach (string candidate in new[] { "192.168.10.100", "192.168.10.130" }) {
          bool inside = (ToNumber(candidate) & mask) == network;
          Console.WriteLine(candidate + " in subnet: " + inside);
        }
      }
    }
  25. 25

    C#: testing values and exceptions with a small harness

    Check results, and verify that bad input throws the expected exception type.

    Add a boundary test for 100.

    Reveal answer and reasoning

    AssertEqual("parses 100", 100, Percent("100")); 100 is the largest accepted value.

    Show starting example
    C#
    using System;
    
    class Program {
      static int failures = 0;
    
      static int Percent(string text) {
        int value = int.Parse(text);
        if (value < 0 || value > 100) {
          throw new ArgumentOutOfRangeException("text", "percent out of range");
        }
        return value;
      }
    
      static void AssertEqual(string name, int expected, int actual) {
        if (expected == actual) {
          Console.WriteLine("PASS " + name);
        } else {
          failures++;
          Console.WriteLine("FAIL " + name + ": expected " + expected + " but was " + actual);
        }
      }
    
      static void AssertThrows<T>(string name, Action code) where T : Exception {
        try {
          code();
          failures++;
          Console.WriteLine("FAIL " + name + ": nothing was thrown");
        } catch (T) {
          Console.WriteLine("PASS " + name);
        }
      }
    
      static void Main() {
        AssertEqual("parses 42", 42, Percent("42"));
        AssertEqual("parses 0", 0, Percent("0"));
        AssertThrows<ArgumentOutOfRangeException>("rejects 101", () => Percent("101"));
        AssertThrows<FormatException>("rejects text", () => Percent("abc"));
        Console.WriteLine("failures: " + failures);
      }
    }
  26. 26

    C#: dependency injection makes code testable

    Pass a dependency in through the constructor so tests can replace it with a fixed one.

    Where would SystemClock be used?

    Reveal answer and reasoning

    In the real program: new Greeter(new SystemClock()) reads the actual time, while tests use FixedClock.

    Show starting example
    C#
    using System;
    
    interface IClock {
      int Hour { get; }
    }
    
    class SystemClock : IClock {
      public int Hour { get { return DateTime.Now.Hour; } }
    }
    
    class FixedClock : IClock {
      public FixedClock(int hour) {
        Hour = hour;
      }
    
      public int Hour { get; }
    }
    
    class Greeter {
      private readonly IClock clock;
    
      public Greeter(IClock clock) {
        this.clock = clock;
      }
    
      public string Greet() {
        return clock.Hour < 12 ? "Good morning" : "Good afternoon";
      }
    }
    
    class Program {
      static void Main() {
        Console.WriteLine(new Greeter(new FixedClock(9)).Greet());
        Console.WriteLine(new Greeter(new FixedClock(15)).Greet());
      }
    }
  27. 27

    C#: message authentication with HMAC-SHA256

    Compute a keyed tag so a receiver can detect tampering, and compare tags safely.

    Why not use SHA256(key + message) instead of HMAC?

    Reveal answer and reasoning

    Hashes built this way are vulnerable to length-extension attacks. HMAC is designed to avoid them.

    Show starting example
    C#
    using System;
    using System.Security.Cryptography;
    using System.Text;
    
    class Program {
      static void Main() {
        byte[] key = Encoding.UTF8.GetBytes("Jefe");
        byte[] message = Encoding.UTF8.GetBytes("what do ya want for nothing?");
        byte[] tampered = Encoding.UTF8.GetBytes("what do ya want for something?");
    
        using var hmac = new HMACSHA256(key);
        byte[] tag = hmac.ComputeHash(message);
        Console.WriteLine(Convert.ToHexString(tag).ToLowerInvariant());
    
        byte[] again = hmac.ComputeHash(message);
        Console.WriteLine("same message valid: " + CryptographicOperations.FixedTimeEquals(tag, again));
        byte[] forged = hmac.ComputeHash(tampered);
        Console.WriteLine("tampered message valid: " + CryptographicOperations.FixedTimeEquals(tag, forged));
      }
    }
  28. 28

    C#: password storage with a salt and PBKDF2

    Derive a slow salted hash from a password and verify a login attempt in constant time.

    Why compare with FixedTimeEquals rather than ==?

    Reveal answer and reasoning

    A normal comparison can stop at the first difference, and the time it takes can leak information about the stored hash.

    Show starting example
    C#
    using System;
    using System.Security.Cryptography;
    using System.Text;
    
    class Program {
      static byte[] Derive(string password, byte[] salt) {
        return Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(password), salt, 600000, HashAlgorithmName.SHA256, 32);
      }
    
      static void Main() {
        byte[] salt = RandomNumberGenerator.GetBytes(16);
        byte[] stored = Derive("correct horse", salt);
    
        Console.WriteLine("hash length: " + stored.Length + " bytes");
        Console.WriteLine("right password: " + CryptographicOperations.FixedTimeEquals(stored, Derive("correct horse", salt)));
        Console.WriteLine("wrong password: " + CryptographicOperations.FixedTimeEquals(stored, Derive("wrong guess", salt)));
      }
    }
  29. 29

    C#: running tasks in parallel with Task.WhenAll

    Start independent computations as tasks and await all of their results.

    How would you use four tasks?

    Reveal answer and reasoning

    Split the range into four slices with Skip and Take, create four tasks, and await Task.WhenAll with all four.

    Show starting example
    C#
    using System;
    using System.Linq;
    using System.Threading.Tasks;
    
    class Program {
      static async Task Main() {
        int[] data = Enumerable.Range(1, 1000).ToArray();
    
        Task<long> first = Task.Run(() => data.Take(500).Sum(x => (long)x));
        Task<long> second = Task.Run(() => data.Skip(500).Sum(x => (long)x));
    
        long[] parts = await Task.WhenAll(first, second);
        Console.WriteLine(parts[0] + " " + parts[1] + " " + parts.Sum());
      }
    }
  30. 30

    C#: Parallel.For with Interlocked

    Spread loop iterations over cores and combine results safely with an atomic add.

    What is the sum of squares from 1 to 10?

    Reveal answer and reasoning

    385, from 10 · 11 · 21 / 6.

    Show starting example
    C#
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program {
      static void Main() {
        long total = 0;
        Parallel.For(1, 1001, i => {
          Interlocked.Add(ref total, (long)i * i);
        });
        Console.WriteLine("Parallel.For: " + total);
    
        long viaLinq = Enumerable.Range(1, 1000).AsParallel().Sum(i => (long)i * i);
        Console.WriteLine("PLINQ:        " + viaLinq);
      }
    }
  31. 31

    C#: uniform-cost search with PriorityQueue

    Find the cheapest route from a start node to every other node in a weighted graph.

    What is the cost of the cheapest route from A to D?

    Reveal answer and reasoning

    20, by A → C → D (9 + 11), which beats A → B → D (7 + 15 = 22).

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    class Program {
      static void Main() {
        var graph = new Dictionary<string, List<(string To, int Cost)>>();
        void Edge(string a, string b, int cost) {
          if (!graph.ContainsKey(a)) graph[a] = new List<(string, int)>();
          if (!graph.ContainsKey(b)) graph[b] = new List<(string, int)>();
          graph[a].Add((b, cost));
          graph[b].Add((a, cost));
        }
        Edge("A", "B", 7); Edge("A", "C", 9); Edge("A", "F", 14);
        Edge("B", "C", 10); Edge("B", "D", 15); Edge("C", "D", 11);
        Edge("C", "F", 2); Edge("D", "E", 6); Edge("E", "F", 9);
    
        var best = graph.Keys.ToDictionary(k => k, k => int.MaxValue);
        var queue = new PriorityQueue<string, int>();
        best["A"] = 0;
        queue.Enqueue("A", 0);
        while (queue.TryDequeue(out string node, out int cost)) {
          if (cost > best[node]) {
            continue;
          }
          foreach (var (to, step) in graph[node]) {
            if (cost + step < best[to]) {
              best[to] = cost + step;
              queue.Enqueue(to, best[to]);
            }
          }
        }
        foreach (var pair in best.OrderBy(p => p.Key)) {
          Console.WriteLine(pair.Key + ": " + pair.Value);
        }
      }
    }
  32. 32

    C#: solving tic-tac-toe with minimax

    Search the complete game tree and show that perfect play by both sides ends in a draw.

    Why is X's best-value start −2?

    Reveal answer and reasoning

    Any real result is −1, 0, or +1, so −2 is guaranteed to be replaced by the first value found.

    Show starting example
    C#
    using System;
    
    class Program {
      static int nodes = 0;
      static readonly int[][] Lines = {
        new[] { 0, 1, 2 }, new[] { 3, 4, 5 }, new[] { 6, 7, 8 },
        new[] { 0, 3, 6 }, new[] { 1, 4, 7 }, new[] { 2, 5, 8 },
        new[] { 0, 4, 8 }, new[] { 2, 4, 6 },
      };
    
      static char Winner(char[] b) {
        foreach (int[] line in Lines) {
          if (b[line[0]] != ' ' && b[line[0]] == b[line[1]] && b[line[1]] == b[line[2]]) {
            return b[line[0]];
          }
        }
        return ' ';
      }
    
      static int Minimax(char[] b, char turn) {
        nodes++;
        char winner = Winner(b);
        if (winner == 'X') return 1;
        if (winner == 'O') return -1;
    
        bool moved = false;
        int best = turn == 'X' ? -2 : 2;
        for (int i = 0; i < 9; i++) {
          if (b[i] != ' ') continue;
          moved = true;
          b[i] = turn;
          int value = Minimax(b, turn == 'X' ? 'O' : 'X');
          b[i] = ' ';
          best = turn == 'X' ? Math.Max(best, value) : Math.Min(best, value);
        }
        return moved ? best : 0;
      }
    
      static void Main() {
        char[] board = new string(' ', 9).ToCharArray();
        Console.WriteLine("value of the empty board: " + Minimax(board, 'X'));
        Console.WriteLine("positions searched: " + nodes);
      }
    }
  33. 33

    C#: first-order unification

    Find a substitution that makes two logical terms identical, including the occurs check.

    Do p(X, X) and p(a, b) unify?

    Reveal answer and reasoning

    No: X is bound to a, and then a must equal b, which fails.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    class Term {
      public string Name;
      public Term[] Args;
    
      public Term(string name, params Term[] args) {
        Name = name;
        Args = args;
      }
    
      public bool IsVariable { get { return Args.Length == 0 && char.IsUpper(Name[0]); } }
    
      public override string ToString() {
        return Args.Length == 0 ? Name : Name + "(" + string.Join(",", Args.Select(a => a.ToString())) + ")";
      }
    }
    
    class Program {
      static Term Walk(Term t, Dictionary<string, Term> s) {
        while (t.IsVariable && s.ContainsKey(t.Name)) {
          t = s[t.Name];
        }
        return t;
      }
    
      static bool Occurs(string variable, Term t, Dictionary<string, Term> s) {
        t = Walk(t, s);
        if (t.IsVariable) return t.Name == variable;
        return t.Args.Any(a => Occurs(variable, a, s));
      }
    
      static bool Unify(Term x, Term y, Dictionary<string, Term> s) {
        x = Walk(x, s);
        y = Walk(y, s);
        if (x.IsVariable && y.IsVariable && x.Name == y.Name) return true;
        if (x.IsVariable) {
          if (Occurs(x.Name, y, s)) return false;
          s[x.Name] = y;
          return true;
        }
        if (y.IsVariable) return Unify(y, x, s);
        if (x.Name != y.Name || x.Args.Length != y.Args.Length) return false;
        for (int i = 0; i < x.Args.Length; i++) {
          if (!Unify(x.Args[i], y.Args[i], s)) return false;
        }
        return true;
      }
    
      static void Try(Term a, Term b) {
        var s = new Dictionary<string, Term>();
        if (Unify(a, b, s)) {
          string bindings = string.Join(", ", s.OrderBy(p => p.Key).Select(p => p.Key + "=" + p.Value));
          Console.WriteLine(a + " ~ " + b + ": " + bindings);
        } else {
          Console.WriteLine(a + " ~ " + b + ": fail");
        }
      }
    
      static void Main() {
        Term X = new Term("X"), Y = new Term("Y"), a = new Term("a"), b = new Term("b"), mary = new Term("mary"), john = new Term("john");
        Try(new Term("f", X, b), new Term("f", a, Y));
        Try(new Term("loves", X, mary), new Term("loves", john, X));
        Try(new Term("f", X), new Term("f", new Term("g", X)));
      }
    }
  34. 34

    C#: semantic networks with default reasoning

    Inherit properties up an is-a hierarchy and let more specific nodes override defaults.

    What does tweety's lookup of 'alive' return?

    Reveal answer and reasoning

    True, inherited through bird from animal.

    Show starting example
    C#
    using System;
    using System.Collections.Generic;
    
    class Node {
      public string Name;
      public Node Parent;
      public Dictionary<string, bool> Properties = new Dictionary<string, bool>();
    
      public Node(string name, Node parent) {
        Name = name;
        Parent = parent;
      }
    
      public bool? Lookup(string property) {
        for (Node n = this; n != null; n = n.Parent) {
          if (n.Properties.TryGetValue(property, out bool value)) {
            return value;
          }
        }
        return null;
      }
    }
    
    class Program {
      static void Main() {
        var animal = new Node("animal", null);
        animal.Properties["alive"] = true;
        var bird = new Node("bird", animal);
        bird.Properties["flies"] = true;
        bird.Properties["feathers"] = true;
        var penguin = new Node("penguin", bird);
        penguin.Properties["flies"] = false;
        var tweety = new Node("tweety", bird);
        var opus = new Node("opus", penguin);
    
        foreach (var (node, property) in new[] { (tweety, "flies"), (opus, "flies"), (opus, "feathers"), (opus, "swims") }) {
          Console.WriteLine(node.Name + " " + property + ": " + (node.Lookup(property)?.ToString() ?? "unknown"));
        }
      }
    }
  35. 35

    C#: logistic regression for classification

    Predict a probability with the sigmoid function and train the weights by gradient descent.

    What does a probability of exactly 0.5 mean?

    Reveal answer and reasoning

    z = w·x + b equals 0, so the point lies on the decision boundary.

    Show starting example
    C#
    using System;
    using System.Globalization;
    
    class Program {
      static double Sigmoid(double z) {
        return 1.0 / (1.0 + Math.Exp(-z));
      }
    
      static string F(double v) {
        return v.ToString("F2", CultureInfo.InvariantCulture);
      }
    
      static void Main() {
        double[] hours = { 1, 2, 3, 4, 5, 6 };
        int[] passed = { 0, 0, 1, 0, 1, 1 };
        double w = 0, b = 0, rate = 0.1;
    
        for (int epoch = 0; epoch < 5000; epoch++) {
          double gw = 0, gb = 0;
          for (int i = 0; i < hours.Length; i++) {
            double error = Sigmoid(w * hours[i] + b) - passed[i];
            gw += error * hours[i] / hours.Length;
            gb += error / hours.Length;
          }
          w -= rate * gw;
          b -= rate * gb;
        }
    
        Console.WriteLine("w=" + F(w) + " b=" + F(b));
        Console.WriteLine("P(pass | 1h) = " + F(Sigmoid(w * 1 + b)));
        Console.WriteLine("P(pass | 6h) = " + F(Sigmoid(w * 6 + b)));
        int correct = 0;
        for (int i = 0; i < hours.Length; i++) {
          int predicted = Sigmoid(w * hours[i] + b) >= 0.5 ? 1 : 0;
          if (predicted == passed[i]) correct++;
        }
        Console.WriteLine("accuracy: " + correct + "/" + hours.Length);
      }
    }
  36. 36

    C#: entropy and information gain for decision trees

    Measure how much a feature reduces uncertainty about a label, the criterion used to choose a split.

    What is the entropy of a 50/50 split?

    Reveal answer and reasoning

    Exactly 1 bit: −0.5·log2(0.5) − 0.5·log2(0.5) = 1.

    Show starting example
    C#
    using System;
    using System.Globalization;
    
    class Program {
      static double Entropy(int yes, int no) {
        double total = yes + no;
        double result = 0;
        foreach (int count in new[] { yes, no }) {
          if (count > 0) {
            double p = count / total;
            result -= p * Math.Log2(p);
          }
        }
        return result;
      }
    
      static string F(double v) {
        return v.ToString("F3", CultureInfo.InvariantCulture);
      }
    
      static void Main() {
        double whole = Entropy(9, 5);
        int[][] outlook = { new[] { 2, 3 }, new[] { 4, 0 }, new[] { 3, 2 } };
        double remainder = 0;
        foreach (int[] group in outlook) {
          remainder += (group[0] + group[1]) / 14.0 * Entropy(group[0], group[1]);
        }
        Console.WriteLine("entropy of the data: " + F(whole));
        Console.WriteLine("entropy after splitting on Outlook: " + F(remainder));
        Console.WriteLine("information gain: " + F(whole - remainder));
      }
    }
  37. 37

    C#: backpropagation learns XOR

    Train a 2-2-1 sigmoid network by gradient descent until it computes XOR.

    What breaks if the sigmoid is replaced by a linear function?

    Reveal answer and reasoning

    The whole network collapses to a single linear map, which cannot represent XOR.

    Show starting example
    C#
    using System;
    using System.Globalization;
    
    class Program {
      static double Sigmoid(double z) {
        return 1.0 / (1.0 + Math.Exp(-z));
      }
    
      static void Main() {
        double[,] inputs = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 } };
        double[] targets = { 0, 1, 1, 0 };
        double[,] w1 = { { 0.5, -0.4 }, { 0.9, 0.7 } };
        double[] b1 = { 0.1, -0.3 };
        double[] w2 = { 0.8, -0.6 };
        double b2 = 0.2;
        double rate = 0.5;
    
        for (int epoch = 0; epoch < 20000; epoch++) {
          for (int n = 0; n < 4; n++) {
            double[] h = new double[2];
            for (int j = 0; j < 2; j++) {
              h[j] = Sigmoid(inputs[n, 0] * w1[0, j] + inputs[n, 1] * w1[1, j] + b1[j]);
            }
            double o = Sigmoid(h[0] * w2[0] + h[1] * w2[1] + b2);
    
            double dOut = (o - targets[n]) * o * (1 - o);
            for (int j = 0; j < 2; j++) {
              double dHidden = dOut * w2[j] * h[j] * (1 - h[j]);
              w2[j] -= rate * dOut * h[j];
              w1[0, j] -= rate * dHidden * inputs[n, 0];
              w1[1, j] -= rate * dHidden * inputs[n, 1];
              b1[j] -= rate * dHidden;
            }
            b2 -= rate * dOut;
          }
        }
    
        for (int n = 0; n < 4; n++) {
          double[] h = new double[2];
          for (int j = 0; j < 2; j++) {
            h[j] = Sigmoid(inputs[n, 0] * w1[0, j] + inputs[n, 1] * w1[1, j] + b1[j]);
          }
          double o = Sigmoid(h[0] * w2[0] + h[1] * w2[1] + b2);
          Console.WriteLine(inputs[n, 0] + " XOR " + inputs[n, 1] + " -> " + Math.Round(o) + " (" + o.ToString("F3", CultureInfo.InvariantCulture) + ")");
        }
      }
    }
  38. 38

    C#: activation functions and their ranges

    Compare sigmoid, tanh, and ReLU on the same inputs.

    Which of the three is zero for every negative input?

    Reveal answer and reasoning

    ReLU: max(0, x) returns 0 whenever x is negative.

    Show starting example
    C#
    using System;
    using System.Globalization;
    
    class Program {
      static string F(double v) {
        return v.ToString("F3", CultureInfo.InvariantCulture);
      }
    
      static void Main() {
        Console.WriteLine("x     sigmoid  tanh    relu");
        foreach (double x in new[] { -2.0, 0.0, 2.0 }) {
          double sigmoid = 1.0 / (1.0 + Math.Exp(-x));
          double tanh = Math.Tanh(x);
          double relu = Math.Max(0.0, x);
          Console.WriteLine(F(x).PadLeft(6) + F(sigmoid).PadLeft(9) + F(tanh).PadLeft(8) + F(relu).PadLeft(8));
        }
      }
    }
  39. 39

    C#: Sobel gradient magnitude on a diagonal edge

    Combine horizontal and vertical Sobel responses into one edge-strength value.

    What is the magnitude where the window contains only bright pixels?

    Reveal answer and reasoning

    Zero. Gx and Gy both cancel when all nine pixels have the same value.

    Show starting example
    C#
    using System;
    
    class Program {
      static void Main() {
        int[,] image = new int[5, 5];
        for (int r = 0; r < 5; r++) {
          for (int c = 0; c < 5; c++) {
            image[r, c] = c >= r ? 100 : 0;
          }
        }
        int[,] kx = { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };
        int[,] ky = { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };
    
        for (int r = 1; r < 4; r++) {
          string row = "";
          for (int c = 1; c < 4; c++) {
            int gx = 0, gy = 0;
            for (int i = 0; i < 3; i++) {
              for (int j = 0; j < 3; j++) {
                int pixel = image[r - 1 + i, c - 1 + j];
                gx += pixel * kx[i, j];
                gy += pixel * ky[i, j];
              }
            }
            row += ((int)Math.Round(Math.Sqrt(gx * gx + gy * gy))).ToString().PadLeft(5);
          }
          Console.WriteLine(row);
        }
      }
    }
  40. 40

    C#: the integral image (summed-area table)

    Precompute running sums so that the total of any rectangle takes four lookups.

    What is the sum of the top-left 2×2 block?

    Reveal answer and reasoning

    1 + 2 + 5 + 6 = 14, which is integral[2, 2].

    Show starting example
    C#
    using System;
    
    class Program {
      static void Main() {
        int[,] image = new int[4, 4];
        int value = 1;
        for (int r = 0; r < 4; r++) {
          for (int c = 0; c < 4; c++) {
            image[r, c] = value++;
          }
        }
    
        int[,] integral = new int[5, 5];
        for (int r = 1; r <= 4; r++) {
          for (int c = 1; c <= 4; c++) {
            integral[r, c] = image[r - 1, c - 1] + integral[r - 1, c] + integral[r, c - 1] - integral[r - 1, c - 1];
          }
        }
        Console.WriteLine("total of the whole image: " + integral[4, 4]);
    
        int top = 1, left = 1, bottom = 2, right = 2;
        int box = integral[bottom + 1, right + 1] - integral[top, right + 1] - integral[bottom + 1, left] + integral[top, left];
        Console.WriteLine("sum of rows 1-2, columns 1-2: " + box);
      }
    }