// Exercise 1: remove quadratic string building (easy) // // BuildReport(rows) must return one line per row: ": = \n". // The stub uses `report += ...` in a loop, which copies the whole string every time (O(n²) bytes allocated). // Numbers use the invariant culture ("2.35", never "2,35"). // Rewrite it with a StringBuilder (pre-size it with a reasonable capacity, e.g. rows.Length * 32). // // The self-check builds 5,000 rows and measures allocated bytes: the += version allocates hundreds of MB. // // Run with: dotnet run (inside a console project). using System; using System.Text; class Program { static string BuildReport((string name, double value)[] rows) { string report = ""; for (int i = 0; i < rows.Length; i++) report += FormattableString.Invariant($"{i}: {rows[i].name} = {rows[i].value:0.00}\n"); return report; } static void Main() { int pass = 0, total = 0; void Check(string name, bool ok) { total++; if (ok) pass++; Console.WriteLine($"{(ok ? "PASS" : "FAIL")}: {name}"); } var small = new[] { ("a", 1.0), ("b", 2.345) }; Check("format", BuildReport(small) == "0: a = 1.00\n1: b = 2.35\n" || BuildReport(small) == "0: a = 1.00\n1: b = 2.34\n"); var rows = new (string, double)[5000]; for (int i = 0; i < rows.Length; i++) rows[i] = ("item" + i, i * 1.5); BuildReport(new[] { ("x", 1.0) }); long before = GC.GetAllocatedBytesForCurrentThread(); string r = BuildReport(rows); long mb = (GC.GetAllocatedBytesForCurrentThread() - before) / 1_000_000; Check("all lines present", r.Split('\n').Length == 5001 && r.StartsWith("0: item0 = 0.00\n")); Check($"linear allocations ({mb} MB allocated)", mb < 20); Console.WriteLine($"--- {pass}/{total} checks passed ---"); } }