Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vVMM9-000wwV-2r for pgsql-general@arkaria.postgresql.org; Tue, 16 Dec 2025 04:08:14 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vVMM7-003sQT-2J for pgsql-general@arkaria.postgresql.org; Tue, 16 Dec 2025 04:08:12 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vVMM7-003sQK-0R for pgsql-general@lists.postgresql.org; Tue, 16 Dec 2025 04:08:12 +0000 Received: from mail-oo1-xc33.google.com ([2607:f8b0:4864:20::c33]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vVMM6-000tsO-0C for pgsql-general@lists.postgresql.org; Tue, 16 Dec 2025 04:08:11 +0000 Received: by mail-oo1-xc33.google.com with SMTP id 006d021491bc7-65b395ca332so2423979eaf.3 for ; Mon, 15 Dec 2025 20:08:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1765858089; x=1766462889; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=0+fs9iWucbSvR3CjYyVn6K86kIYdvBerI/E5oj7ro78=; b=jnZY59ZxStKLhSh38asrBuFFPz9PcOZa57t7vxuhW0AAbqov03b19iulX5x/Ot+Ouu jICSkUkuwmuE8bsTdhk3f195Nszk/ufma2YMkhd2Pj+l75OnQRjxpzMkEYxc7k+S2QDs QCwLLXPEHdzE7XZJKR6MJ8/jh9Rl05FGRk8DWQWgB/J2tzQEAKEMCEzxGsfxNSidJ1fF 77HC2pNcXC5cbD0MX+BXixyTeWeL5LABxQTE0z0UIIHDdq5EkHgJSYpRaVlXZDy0y42i O7Pnm0wv6fLhAA+nXQQLZcwLbQ5e1PsE4TCX0X463kSaw4Gqv6vXZdX64uZeGL/B5tgw SJzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765858089; x=1766462889; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=0+fs9iWucbSvR3CjYyVn6K86kIYdvBerI/E5oj7ro78=; b=JSgcONSPXF6gJHpRjK49R7ejXi6bNBtC53ljshzBrcwzQNn8dQtGFmpczf3QtFnNL3 VwNjwWvm6q+GqlVM2b+YXW98HOPJ7CGJwlSK9n0GGFHng6xW0IYw7L9D9XcjqQc3NxOk AJl9dhj0x7DygsZP6UyBbxtYPhIMytl+Lv4M54++7aj3W8/8aEXUKalxgiu+Ws37wUSW FIEwZe2Wru/lyZd5PoUf8LDcaTwH2XGnDSl11qNmon26R/I12xLusIMgzB4NkyPX2eQs NGooXfUMZQHCExnZSPRED611azZ3I0rwsE+9LdSUIApzSKDLlokYu74bTpb1eZ7iEK4T RkpA== X-Gm-Message-State: AOJu0YzNTNAV92kDuaaBJ+QsZsz+Wiu7769djWhs7q0/F2yCsNXbrqky udvTpXgKKoTE0d/DRgiyCzLCx540/PKWCWp134RwCmbtZ64srMf854+86QJQ9jPx0flXWmt0ELO 9h8EXFp7MiGsVvF7B77NDBVjEwfOT1CAdu+ZpbzM= X-Gm-Gg: AY/fxX66S70zo9AbDxiYTuM+KqtblKpBMJRiNQYjlNtLmn/sTJc6rVn0YCJ65ezrVYH f/zHBErPRT1/pzd6LHz/QR7dAyzKxxfe6GVeNWinjuP0RIdYEwtq12btAyjXHiuBmcL4vFdzfSl yVD3mbLPFly8Ao2cwOwMEhRUGSphq9dP97jri6+gLdSWuuY7omlr+inNtLVoeQOeNpq+/dBF2Tn Q6jOC8tJGXaeIQfBheCsxn8Td+U9q6q1l/hhkeSjt97ZL3d9/S4jrCFEDSmM+x+RWX8K3K8cH3C 5HtP3nyFHWmiuDhrRrIV59ncIpk= X-Google-Smtp-Source: AGHT+IGr3cMuZ9AECPGznZpWdwAFKF9VNtodffCIiXf3ySAt3BYuQQcL4vv9k8NVbxhBZaBaSnx6mI4HG64yu/oGYR4= X-Received: by 2002:a05:6820:982:b0:65b:34ea:583c with SMTP id 006d021491bc7-65b451feaedmr5716622eaf.39.1765858088827; Mon, 15 Dec 2025 20:08:08 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Greg Sabino Mullane Date: Mon, 15 Dec 2025 23:07:36 -0500 X-Gm-Features: AQt7F2q-zwq2sSnP522hNtlnlYvn4opDd95qeN6h7dUlBLX8YCn3HffE5-5_VBg Message-ID: Subject: Re: Advent of Code Day 8 To: Bernice Southey Cc: pgsql-general@lists.postgresql.org Content-Type: multipart/alternative; boundary="0000000000001bbe3e064609e0cb" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000001bbe3e064609e0cb Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable > Is anyone else doing AoC in postgres this year? > https://adventofcode.com/2025/day/8 I am doing it, or at least chipping away a little but on the weekends. This last weekend I got up to day 9. Most days I can solve with a single SQL statement. Day 8 was not one of those, so I fell back to plpgsql. > part 1 and 2 with a brute force loop, but there must be better ways. What's so wrong with brute force? :) Day 8 seemed pretty straightforward: split into x,y,z coordinates, calculate distances, then walk through in distance order and create / merge groups (circuits) as you go. In case it helps, here is my solution: /* Greg Sabino Mullane for AOC 2025 Day 8 "Playground" */ /* Assumes data file is in /tmp/aoc_2026_day8.input */ \pset footer off \set ON_ERROR_STOP on \set QUIET on SET client_min_messages =3D ERROR; CREATE EXTENSION IF NOT EXISTS file_fdw SCHEMA public; CREATE SERVER IF NOT EXISTS aoc2025 FOREIGN DATA WRAPPER file_fdw; DROP SCHEMA IF EXISTS aoc_2025_8 CASCADE; CREATE SCHEMA aoc_2025_8; SET search_path =3D aoc_2025_8, public; CREATE FOREIGN TABLE aoc_2025_day8 (line TEXT) SERVER aoc2025 OPTIONS (filename '/tmp/aoc_2025_day8.input'); --------------------------- -- AOC 2025 DAY 8 PART 1 -- --------------------------- \timing on create unlogged table t (box1 smallint, box2 smallint, d bigint, xs bigint)= ; WITH aoc AS (select row_number() over() AS row, split_part(line,',',1)::int AS x, split_part(line,',',2)::int AS y, split_part(line,',',3)::int AS z from aoc_2025_day8) , dist as (select a1.row as box1, a2.row as box2, a1.x::bigint * a2.x::bigint AS xs, (pow(a2.x-a1.x,2) + pow(a2.y-a1.y,2) + pow(a2.z-a1.z,2)) as d from aoc a1 join aoc a2 on (a1.row < a2.row) ) insert into t select box1, box2, d, xs from dist; -- Best time: 289ms CREATE FUNCTION connect_em(target int) returns text LANGUAGE plpgsql AS $$ DECLARE k record; cid int =3D 0; loops int =3D 0; circuit int[] =3D '{}'; ccount int[]; c1 int; c2 int; solution1 text =3D '?'; solution2 text =3D '?'; BEGIN /* Walk through each pair of boxes, closest ones first */ FOR k IN select * from t order by d asc LOOP loops =3D loops + 1; /* For the first part, sum up the three largest circuits */ IF loops =3D target then SELECT INTO solution1 exp(sum(ln(q)))::int AS a FROM (select q FROM (SELECT unnest(ccount) q) order by q desc limit 3); END IF; c1 =3D circuit[k.box1]; c2 =3D circuit[k.box2]; /* If neither box is part of an existing circuit, assign them to a new one */ IF c1 IS NULL and c2 IS NULL THEN cid =3D cid + 1; circuit[k.box1] =3D cid; circuit[k.box2] =3D cid; ccount[cid] =3D 2; raise debug ' Created circuit #% with boxes % and %', cid, k.box1, k.box2; continue; END IF; /* If only box1 is part of an existing circuit, add box2 */ IF c1 IS NOT NULL and c2 IS NULL THEN circuit[k.box2] =3D c1; ccount[c1] =3D ccount[c1] + 1; raise debug ' Moved second box % to circuit #%, used by box %', k.box2, c1, k.box1; END IF; /* If only box2 is part of an existing circuit, add box1 */ IF c1 IS NULL and c2 IS NOT NULL THEN circuit[k.box1] =3D c2; ccount[c2] =3D ccount[c2] + 1; raise debug ' Moved first box % to circuit #% , used by box %', k.box1, c2, k.box2; END IF; /* Both boxes are already part of a circuit, so merge or ignore */ IF c1 IS NOT NULL and c2 IS NOT NULL THEN IF c1 =3D c2 THEN raise debug ' Skip, as both boxes already belong to the same circuit'; continue; END IF; /* Move anything in the old circuit to the new one */ for x in array_lower(circuit,1) .. array_upper(circuit,1) loop if circuit[x] =3D c2 then circuit[x] =3D c1; ccount[c2] =3D ccount[c2] - 1; ccount[c1] =3D ccount[c1] + 1; end if; end loop; raise debug ' Merge box % circuit #% (now at %) into box % circuit #% (now at %)', k.box2, c2, ccount[c2], k.box1, c1, ccount[c1]; END IF; /* We avoided using CONTINUE above just to make this check */ if c1 is null then c1 =3D c2; end if; IF ccount[c1] =3D target THEN solution2 =3D k.xs; exit; END IF; END LOOP; RETURN format('Solution 1: %s Solution 2: %s', solution1, solution2); END $$; -- SET client_min_messages =3D DEBUG1; SELECT connect_em(1000); -- Best time: 126 ms --------------------------- -- AOC 2025 DAY 8 PART 2 -- --------------------------- -- Same function as above -- Best overall time: 451ms On Mon, Dec 8, 2025 at 10:36=E2=80=AFAM Bernice Southey wrote: > Hi, > > Is anyone else doing AoC in postgres this year? I've solved today's > part 1 and 2 with a brute force loop, but there must be better ways. > If anyone found something clever in postgres, please give me a big > hint. > https://adventofcode.com/2025/day/8 > > Thanks, Bernice > > > Cheers, Greg -- Crunchy Data - https://www.crunchydata.com Enterprise Postgres Software Products & Tech Support --0000000000001bbe3e064609e0cb Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
> Is anyone else doing AoC in postgres= this year?
> https:/= /adventofcode.com/2025/day/8

I am doing it, or at least chipping= away a little but on the weekends. This last weekend I got up to day 9. Mo= st days I can solve with a single SQL statement. Day 8 was not one of those= , so I fell back to plpgsql.

> part 1 and 2 with a brute force lo= op, but there must be better ways.

What's so wrong with brute fo= rce? :) Day 8 seemed pretty straightforward: split into x,y,z coordinates, = calculate distances, then walk through in distance order and create / merge= groups (circuits) as you go.

In case it helps, here is my solution:=

/* Greg Sabino Mullane for AOC 2025 Day 8 "Playground" */=

/* Assumes data file is in /tmp/aoc_2026_day8.input */

\pset= footer off
\set ON_ERROR_STOP on
\set QUIET on

SET client_min= _messages =3D ERROR;

CREATE EXTENSION IF NOT EXISTS file_fdw SCHEMA = public;

CREATE SERVER IF NOT EXISTS aoc2025 FOREIGN DATA WRAPPER fil= e_fdw;

DROP SCHEMA IF EXISTS aoc_2025_8 CASCADE;

CREATE SCHEM= A aoc_2025_8;

SET search_path =3D aoc_2025_8, public;

CREATE = FOREIGN TABLE aoc_2025_day8 (line TEXT)
=C2=A0 SERVER aoc2025 OPTIONS (f= ilename '/tmp/aoc_2025_day8.input');

-----------------------= ----
-- AOC 2025 DAY 8 PART 1 --
---------------------------

\= timing on

create unlogged table t (box1 smallint, box2 smallint, d b= igint, xs bigint);

WITH
aoc AS (select row_number() over() AS ro= w,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 split_part(line,',',1)::int AS x,=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 split_part(line,',',2)::int AS y,=C2=A0 =C2=A0 =C2=A0 =C2=A0 split_part(line,',',3)::int AS z
= =C2=A0 from aoc_2025_day8)
, dist as (select a1.row as box1, a2.row as b= ox2, a1.x::bigint * a2.x::bigint AS xs,
=C2=A0 (pow(a2.x-a1.x,2) + pow(a= 2.y-a1.y,2) + pow(a2.z-a1.z,2)) as d
=C2=A0 from aoc a1 join aoc a2 on (= a1.row < a2.row)
)
insert into t select box1, box2, d, xs from dis= t;

-- Best time: 289ms

CREATE FUNCTION connect_em(target int)= returns text
LANGUAGE plpgsql
AS $$

DECLARE
=C2=A0 k recor= d;
=C2=A0 cid int =3D 0;
=C2=A0 loops int =3D 0;
=C2=A0 circuit in= t[] =3D '{}';
=C2=A0 ccount int[];
=C2=A0 c1 int; c2 int;
= =C2=A0 solution1 text =3D '?'; solution2 text =3D '?';
B= EGIN

=C2=A0 /* Walk through each pair of boxes, closest ones first *= /
=C2=A0 FOR k IN select * from t order by d asc LOOP

=C2=A0 =C2= =A0 loops =3D loops + 1;

=C2=A0 =C2=A0 /* For the first part, sum up= the three largest circuits */
=C2=A0 =C2=A0 IF loops =3D target then=C2=A0 =C2=A0 =C2=A0 SELECT INTO solution1 exp(sum(ln(q)))::int AS a FROM = (select q FROM (SELECT unnest(ccount) q) order by q desc limit 3);
=C2= =A0 =C2=A0 END IF;

=C2=A0 =C2=A0 c1 =3D circuit[k.box1]; c2 =3D circ= uit[k.box2];

=C2=A0 =C2=A0 /* If neither box is part of an existing = circuit, assign them to a new one */
=C2=A0 =C2=A0 IF c1 IS NULL and c2 = IS NULL THEN
=C2=A0 =C2=A0 =C2=A0 cid =3D cid + 1;
=C2=A0 =C2=A0 =C2= =A0 circuit[k.box1] =3D cid;
=C2=A0 =C2=A0 =C2=A0 circuit[k.box2] =3D ci= d;
=C2=A0 =C2=A0 =C2=A0 ccount[cid] =3D 2;
=C2=A0 =C2=A0 =C2=A0 raise= debug ' =C2=A0Created circuit #% with boxes % and %', cid, k.box1,= k.box2;
=C2=A0 =C2=A0 =C2=A0 continue;
=C2=A0 =C2=A0 END IF;

= =C2=A0 =C2=A0 /* If only box1 is part of an existing circuit, add box2 */=C2=A0 =C2=A0 IF c1 IS NOT NULL and c2 IS NULL THEN
=C2=A0 =C2=A0 =C2= =A0 circuit[k.box2] =3D c1;
=C2=A0 =C2=A0 =C2=A0 ccount[c1] =3D ccount[c= 1] + 1;
=C2=A0 =C2=A0 =C2=A0 raise debug ' =C2=A0Moved second box % = to circuit #%, used by box %', k.box2, c1, k.box1;
=C2=A0 =C2=A0 END= IF;

=C2=A0 =C2=A0 /* If only box2 is part of an existing circuit, a= dd box1 */
=C2=A0 =C2=A0 IF c1 IS NULL and c2 IS NOT NULL THEN
=C2=A0= =C2=A0 =C2=A0 circuit[k.box1] =3D c2;
=C2=A0 =C2=A0 =C2=A0 ccount[c2] = =3D ccount[c2] + 1;
=C2=A0 =C2=A0 =C2=A0 raise debug ' =C2=A0Moved f= irst box % to circuit #% , used by box %', k.box1, c2, k.box2;
=C2= =A0 =C2=A0 END IF;

=C2=A0 =C2=A0 /* Both boxes are already part of a= circuit, so merge or ignore */
=C2=A0 =C2=A0 IF c1 IS NOT NULL and c2 I= S NOT NULL THEN
=C2=A0 =C2=A0 =C2=A0 IF c1 =3D c2 THEN
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 raise debug ' =C2=A0Skip, as both boxes already belong to= the same circuit';
=C2=A0 =C2=A0 =C2=A0 =C2=A0 continue;
=C2=A0 = =C2=A0 =C2=A0 END IF;

=C2=A0 =C2=A0 =C2=A0 /* Move anything in the o= ld circuit to the new one */
=C2=A0 =C2=A0 =C2=A0 for x in array_lower(c= ircuit,1) .. array_upper(circuit,1) loop
=C2=A0 =C2=A0 =C2=A0 =C2=A0 if = circuit[x] =3D c2 then
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 circuit[x] =3D= c1;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ccount[c2] =3D ccount[c2] - 1;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ccount[c1] =3D ccount[c1] + 1;
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 end if;
=C2=A0 =C2=A0 =C2=A0 end loop;

= =C2=A0 =C2=A0 =C2=A0 raise debug ' =C2=A0Merge box % circuit #% (now at= %) into box % circuit #% (now at %)',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 k= .box2, c2, ccount[c2], k.box1, c1, ccount[c1];
=C2=A0 =C2=A0 END IF;
=
=C2=A0 =C2=A0 /* We avoided using CONTINUE above just to make this chec= k */
=C2=A0 =C2=A0 if c1 is null then c1 =3D c2; end if;
=C2=A0 =C2= =A0 IF ccount[c1] =3D target THEN
=C2=A0 =C2=A0 =C2=A0 solution2 =3D k.x= s;
=C2=A0 =C2=A0 =C2=A0 exit;
=C2=A0 =C2=A0 END IF;

=C2=A0END = LOOP;

RETURN format('Solution 1: %s =C2=A0Solution 2: %s', s= olution1, solution2);

END
$$;

-- SET client_min_messages = =3D DEBUG1;
SELECT connect_em(1000);

-- Best time: 126 ms

= ---------------------------
-- AOC 2025 DAY 8 PART 2 --
-------------= --------------

-- Same function as above

-- Best overall time= : 451ms


<= div dir=3D"ltr" class=3D"gmail_attr">On Mon, Dec 8, 2025 at 10:36=E2=80=AFA= M Bernice Southey <bernice.= southey@gmail.com> wrote:
Hi,

Is anyone else doing AoC in postgres this year? I've solved today's=
part 1 and 2 with a brute force loop, but there must be better ways.
If anyone found something clever in postgres, please give me a big
hint.
https://adventofcode.com/2025/day/8

Thanks, Bernice





Cheers,
= Greg

--
Crunchy Data - https://www.crunchydata.com
Enterprise Postgres Software Products & Tech Support
=
--0000000000001bbe3e064609e0cb--