Advent of Code 2025 solutions
at main 340 lines 9.0 kB view raw
1import { expect, test, describe } from "bun:test"; 2import { 3 combinations, 4 multicombinations, 5 unique, 6 first, 7 last, 8 middle, 9 swap, 10 remove, 11 sum, 12 getIdxAt, 13 compareArr, 14} from "./array"; 15 16describe("combinations", () => { 17 test("returns empty array for empty input", () => { 18 expect(combinations([], 1)).toEqual([]); 19 expect(combinations([], 2)).toEqual([]); 20 }); 21 22 test("returns single element combinations", () => { 23 expect(combinations([1, 2, 3], 1)).toEqual([[1], [2], [3]]); 24 expect(combinations(["a", "b"], 1)).toEqual([["a"], ["b"]]); 25 }); 26 27 test("returns 2-element combinations", () => { 28 expect(combinations([1, 2, 3], 2)).toEqual([ 29 [1, 2], 30 [1, 3], 31 [2, 3], 32 ]); 33 expect(combinations(["a", "b", "c"], 2)).toEqual([ 34 ["a", "b"], 35 ["a", "c"], 36 ["b", "c"], 37 ]); 38 }); 39 40 test("returns 3-element combinations", () => { 41 expect(combinations([1, 2, 3, 4], 3)).toEqual([ 42 [1, 2, 3], 43 [1, 2, 4], 44 [1, 3, 4], 45 [2, 3, 4], 46 ]); 47 }); 48 49 test("returns empty array when n > array length", () => { 50 expect(combinations([1, 2], 3)).toEqual([]); 51 expect(combinations([1], 2)).toEqual([]); 52 }); 53 54 test("returns single combination when n equals array length", () => { 55 expect(combinations([1, 2, 3], 3)).toEqual([[1, 2, 3]]); 56 }); 57 58 test("works with complex types", () => { 59 const objects = [{ id: 1 }, { id: 2 }, { id: 3 }]; 60 const result = combinations(objects, 2); 61 expect(result).toHaveLength(3); 62 expect(result[0]).toEqual([{ id: 1 }, { id: 2 }]); 63 expect(result[1]).toEqual([{ id: 1 }, { id: 3 }]); 64 expect(result[2]).toEqual([{ id: 2 }, { id: 3 }]); 65 }); 66}); 67 68describe("multicombinations", () => { 69 test("returns empty array for empty input", () => { 70 expect(multicombinations([], 1)).toEqual([]); 71 expect(multicombinations([], 2)).toEqual([]); 72 }); 73 74 test("returns single element multicombinations", () => { 75 expect(multicombinations([1, 2, 3], 1)).toEqual([[1], [2], [3]]); 76 expect(multicombinations(["a", "b"], 1)).toEqual([["a"], ["b"]]); 77 }); 78 79 test("returns 2-element multicombinations (allows reuse)", () => { 80 expect(multicombinations([1, 2, 3], 2)).toEqual([ 81 [1, 1], 82 [1, 2], 83 [1, 3], 84 [2, 2], 85 [2, 3], 86 [3, 3], 87 ]); 88 }); 89 90 test("returns 3-element multicombinations", () => { 91 const result = multicombinations([1, 2], 3); 92 expect(result).toEqual([ 93 [1, 1, 1], 94 [1, 1, 2], 95 [1, 2, 2], 96 [2, 2, 2], 97 ]); 98 }); 99 100 test("handles larger n values correctly", () => { 101 const result = multicombinations([1, 2], 4); 102 expect(result).toHaveLength(5); // (n+k-1)!/(n!(k-1)!) = (4+2-1)!/(4!(2-1)!) = 5!/(4!1!) = 5 103 expect(result).toEqual([ 104 [1, 1, 1, 1], 105 [1, 1, 1, 2], 106 [1, 1, 2, 2], 107 [1, 2, 2, 2], 108 [2, 2, 2, 2], 109 ]); 110 }); 111}); 112 113describe("unique", () => { 114 test("removes duplicate numbers", () => { 115 expect(unique([1, 2, 2, 3, 1, 4])).toEqual([1, 2, 3, 4]); 116 }); 117 118 test("removes duplicate strings", () => { 119 expect(unique(["a", "b", "a", "c", "b"])).toEqual(["a", "b", "c"]); 120 }); 121 122 test("handles empty array", () => { 123 expect(unique([])).toEqual([]); 124 }); 125 126 test("handles array with no duplicates", () => { 127 expect(unique([1, 2, 3])).toEqual([1, 2, 3]); 128 }); 129 130 test("preserves order of first occurrence", () => { 131 expect(unique([3, 1, 2, 1, 3])).toEqual([3, 1, 2]); 132 }); 133}); 134 135describe("first", () => { 136 test("returns first element", () => { 137 expect(first([1, 2, 3])).toBe(1); 138 expect(first(["a", "b", "c"])).toBe("a"); 139 }); 140 141 test("returns undefined for empty array", () => { 142 expect(first([])).toBeUndefined(); 143 }); 144 145 test("works with single element array", () => { 146 expect(first([42])).toBe(42); 147 }); 148}); 149 150describe("last", () => { 151 test("returns last element", () => { 152 expect(last([1, 2, 3])).toBe(3); 153 expect(last(["a", "b", "c"])).toBe("c"); 154 }); 155 156 test("returns undefined for empty array", () => { 157 expect(last([])).toBeUndefined(); 158 }); 159 160 test("works with single element array", () => { 161 expect(last([42])).toBe(42); 162 }); 163}); 164 165describe("middle", () => { 166 test("returns middle element for odd length", () => { 167 expect(middle([1, 2, 3])).toBe(2); 168 expect(middle([1, 2, 3, 4, 5])).toBe(3); 169 }); 170 171 test("returns upper middle element for even length", () => { 172 expect(middle([1, 2, 3, 4])).toBe(3); 173 expect(middle([1, 2, 3, 4, 5, 6])).toBe(4); 174 }); 175 176 test("returns undefined for empty array", () => { 177 expect(middle([])).toBeUndefined(); 178 }); 179 180 test("works with single element array", () => { 181 expect(middle([42])).toBe(42); 182 }); 183}); 184 185describe("swap", () => { 186 test("swaps elements at given indices", () => { 187 expect(swap(0, 2, [1, 2, 3])).toEqual([3, 2, 1]); 188 expect(swap(1, 3, ["a", "b", "c", "d"])).toEqual(["a", "d", "c", "b"]); 189 }); 190 191 test("returns new array (immutable)", () => { 192 const original = [1, 2, 3]; 193 const swapped = swap(0, 2, original); 194 expect(swapped).toEqual([3, 2, 1]); 195 expect(original).toEqual([1, 2, 3]); // Original unchanged 196 }); 197 198 test("handles swapping same index", () => { 199 expect(swap(1, 1, [1, 2, 3])).toEqual([1, 2, 3]); 200 }); 201 202 test("works with negative indices", () => { 203 // Note: swap function doesn't handle negative indices, it uses them as-is 204 expect(swap(-1, 0, [1, 2, 3])).toEqual([undefined, 2, 3] as any); 205 }); 206}); 207 208describe("remove", () => { 209 test("removes element at given index", () => { 210 expect(remove([1, 2, 3, 4], 2)).toEqual([1, 2, 4]); 211 expect(remove(["a", "b", "c"], 0)).toEqual(["b", "c"]); 212 }); 213 214 test("returns new array (immutable)", () => { 215 const original = [1, 2, 3]; 216 const removed = remove(original, 1); 217 expect(removed).toEqual([1, 3]); 218 expect(original).toEqual([1, 2, 3]); // Original unchanged 219 }); 220 221 test("handles removing from empty array", () => { 222 expect(remove([], 0)).toEqual([]); 223 }); 224 225 test("handles out of bounds index", () => { 226 expect(remove([1, 2, 3], 10)).toEqual([1, 2, 3]); 227 expect(remove([1, 2, 3], -1)).toEqual([1, 2, 3]); 228 }); 229}); 230 231describe("sum", () => { 232 test("sums array of positive numbers", () => { 233 expect(sum([1, 2, 3, 4])).toBe(10); 234 expect(sum([5, 10, 15])).toBe(30); 235 }); 236 237 test("sums array with negative numbers", () => { 238 expect(sum([1, -2, 3, -4])).toBe(-2); 239 expect(sum([-1, -2, -3])).toBe(-6); 240 }); 241 242 test("handles empty array", () => { 243 expect(sum([])).toBe(0); 244 }); 245 246 test("handles single element array", () => { 247 expect(sum([42])).toBe(42); 248 }); 249 250 test("handles zeros", () => { 251 expect(sum([0, 0, 0])).toBe(0); 252 expect(sum([1, 0, 2, 0, 3])).toBe(6); 253 }); 254}); 255 256describe("getIdxAt", () => { 257 test("handles positive indices within bounds", () => { 258 expect(getIdxAt(0, 5)).toBe(0); 259 expect(getIdxAt(2, 5)).toBe(2); 260 expect(getIdxAt(4, 5)).toBe(4); 261 }); 262 263 test("handles positive indices out of bounds (wraps around)", () => { 264 expect(getIdxAt(5, 5)).toBe(0); 265 expect(getIdxAt(6, 5)).toBe(1); 266 expect(getIdxAt(10, 5)).toBe(0); 267 }); 268 269 test("handles negative indices", () => { 270 expect(getIdxAt(-1, 5)).toBe(4); 271 expect(getIdxAt(-2, 5)).toBe(3); 272 expect(getIdxAt(-5, 5)).toBe(5); // -5 % 5 = 0, so 5 + 0 = 5 273 }); 274 275 test("handles negative indices out of bounds (wraps around)", () => { 276 expect(getIdxAt(-6, 5)).toBe(4); 277 expect(getIdxAt(-7, 5)).toBe(3); 278 }); 279 280 test("handles edge cases", () => { 281 expect(getIdxAt(0, 1)).toBe(0); 282 expect(getIdxAt(-1, 1)).toBe(1); // -1 % 1 = 0, so 1 + 0 = 1 283 }); 284}); 285 286describe("compareArr", () => { 287 test("returns true for identical arrays", () => { 288 expect(compareArr([1, 2, 3], [1, 2, 3])).toBe(true); 289 expect(compareArr(["a", "b", "c"], ["a", "b", "c"])).toBe(true); 290 }); 291 292 test("returns false for different arrays", () => { 293 expect(compareArr([1, 2, 3], [1, 2, 4])).toBe(false); 294 expect(compareArr(["a", "b"], ["a", "c"])).toBe(false); 295 }); 296 297 test("returns false for different lengths", () => { 298 expect(compareArr([1, 2, 3], [1, 2])).toBe(false); 299 // Note: compareArr uses every() which only checks up to the shorter array length 300 expect(compareArr([1, 2], [1, 2, 3])).toBe(true); // Only compares first 2 elements 301 }); 302 303 test("returns true for empty arrays", () => { 304 expect(compareArr([], [])).toBe(true); 305 }); 306 307 test("handles mixed types", () => { 308 expect(compareArr([1, "2", true], [1, "2", true])).toBe(true); 309 expect(compareArr([1, "2", true], [1, 2, true])).toBe(false); 310 }); 311 312 test("handles nested arrays", () => { 313 // Note: compareArr uses === which doesn't work for nested array comparison 314 expect( 315 compareArr( 316 [ 317 [1, 2], 318 [3, 4], 319 ], 320 [ 321 [1, 2], 322 [3, 4], 323 ], 324 ), 325 ).toBe(false); // Different object references 326 expect( 327 compareArr( 328 [ 329 [1, 2], 330 [3, 4], 331 ], 332 [ 333 [1, 2], 334 [3, 5], 335 ], 336 ), 337 ).toBe(false); 338 }); 339}); 340