with origin AS (
SELECT row_number() over () as rn, line
FROM lance_input
), empty AS (
SELECT coalesce(lag(rn) over (order by rn), 0) as pre_rn, rn
FROM (
SELECT rn
FROM origin
WHERE line is null
UNION ALL
SELECT max(rn) + 1 as rn
FROM origin
) t
), matrix AS (
SELECT empty.pre_rn as rn, origin.rn :: INTEGER as _row, x.idx :: INTEGER as _col, x.pos
FROM origin, regexp_split_to_table(line, '') with ordinality as x(pos, idx), empty
WHERE origin.rn between empty.pre_rn and empty.rn
), rows AS (
SELECT rn, _row, string_agg(pos, '' order by _col) as line
FROM matrix
GROUP BY rn, _row
), cols AS (
SELECT rn, _col, string_agg(pos, '' order by _row) as line
FROM matrix
GROUP BY rn, _col
), lock_key AS (
SELECT rn, CASE WHEN line like '%.%' THEN 'key' ELSE 'lock' END as type
FROM (
SELECT rn, row_number() over (partition by rn order by _row) as seq, line
FROM rows
) t
WHERE t.seq = 1
)
SELECT COUNT(*)
FROM (
SELECT b.rn as lock, d.rn as key, min(case when length(replace(a.line, '.', '')) + length(replace(c.line, '.', '')) - 2 <= 5 then 1 else 0 end) as result
FROM cols a
JOIN lock_key b
ON a.rn = b.rn
AND b.type = 'lock'
JOIN cols c
ON a._col = c._col
JOIN lock_key d
ON c.rn = d.rn
AND d.type = 'key'
GROUP BY b.rn, d.rn
) t
WHERE result = 1